Redis 命令解析流程:以 SET 命令为例
Redis 对命令的解析和执行是其核心功能之一,涉及协议解析、参数处理、命令路由等多个环节。本文以 SET 命令为例,详细解析 Redis 如何接收、解析并执行命令。
命令解析的整体流程
Redis 处理客户端命令的流程可概括为:
接收命令数据 → 协议解析 → 填充 client 结构体 → 路由到对应命令函数 → 执行命令 → 返回结果
其中,client 结构体是贯穿全程的核心载体,记录了命令的参数、客户端状态、连接信息等关键数据。
client 结构体:命令解析的核心载体
client 结构体是 Redis 描述客户端连接和命令请求的核心数据结构,与命令解析相关的关键字段如下:
| 字段名 | 作用 |
|---|---|
argc |
命令参数的数量(包含命令名本身)。例如 SET key value 中,argc=3。 |
argv |
命令参数数组(robj* 类型,robj 是 Redis 通用对象结构)。argv[0] 为命令名(如 "SET"),argv[1...] 为命令参数(如 "key"、"value")。 |
querybuf |
接收客户端命令的缓冲区(sds 类型,动态字符串),存储未解析的原始命令数据。 |
qb_pos |
querybuf 中已解析的位置,用于增量解析命令。 |
cmd |
指向当前执行的命令结构体(redisCommand 类型),包含命令的处理函数、参数个数限制等元信息。 |
SET 命令解析:从代码看细节
setCommand 函数为例,解析 Redis 如何处理 SET 命令:
1 | void setCommand(client *c) { |
步骤 1:解析扩展参数(parseExtendedStringArgumentsOrReply)
SET 命令支持丰富的扩展选项(如 SET key value EX 30 NX),该函数的作用是解析这些选项:
- 选项解析:识别
EX(过期时间秒)、PX(过期时间毫秒)、NX(仅键不存在时设置)、XX(仅键存在时设置)等。 - 参数校验:检查选项格式是否正确(如
EX后必须跟数字),若错误则向客户端返回提示(通过reply机制)。 - 结果存储:将解析出的过期时间(
expire)、时间单位(unit)、标志(flags,如NX对应CLIENT_SET_NX标志)存入变量,供后续使用。
步骤 2:值对象编码优化(tryObjectEncoding)
c->argv[2] 是 SET 命令的 value 参数(robj 类型)。tryObjectEncoding 函数对其进行编码优化:
- 若
value是字符串且可转为整数(如"123"),则转为整数编码(OBJ_ENCODING_INT),节省内存。 - 若字符串较短(如长度 ≤ 44 字节),则用
embstr编码(紧凑存储);否则用raw编码。 - 优化后的值对象替换原
c->argv[2],后续操作使用优化后的对象。
步骤 3:执行核心逻辑(setGenericCommand)
setGenericCommand 是 SET 命令的核心实现函数,负责:
- 根据
flags检查键是否存在(NX/XX逻辑)。 - 若设置了过期时间(
expire非空),计算绝对过期时间(当前时间 + 过期秒数 / 毫秒数)。 - 将
key和value存入当前数据库(c->db,redisDb类型,包含键空间哈希表)。 - 若有过期时间,设置键的过期属性(
setExpire函数)。 - 向客户端返回结果(如
OK)。
命令解析的前置步骤:从字节流到 argc/argv
setCommand 是命令执行的最后阶段,在此之前,Redis 需完成从原始字节流到 argc/argv 的解析:
接收命令数据(querybuf)
客户端通过 TCP 发送命令(遵循 RESP 协议),例如 SET key value 的 RESP 格式为:
1 | *3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n |
*3表示有 3 个参数,$3表示下一个参数长度为 3(即"SET"),以此类推。
Redis 将这些字节存入 client->querybuf。
协议解析(生成 argc/argv)
Redis 的事件循环(aeMain)会触发读事件处理器(readQueryFromClient),解析 querybuf:
- 调用
processInputBuffer函数,按 RESP 协议解析querybuf中的数据。 - 解析出的参数数量存入
client->argc,参数对象(robj)存入client->argv。 - 解析完成后,
qb_pos更新为已解析的位置,未解析的数据保留在querybuf中。
命令路由(找到 setCommand)
解析出 argc/argv 后,Redis 通过 argv[0](命令名)查找对应的 redisCommand 结构体:
1 | // redisCommand 结构体示例(简化) |
Redis 维护一个命令表(commands 字典),键为命令名(小写),值为 redisCommand 结构体。找到后,将 client->cmd 指向该结构体,随后调用 cmd->proc(client)(即 setCommand(c))。