前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis实战(10)-一条命令在Redis是如何执行的?

Redis实战(10)-一条命令在Redis是如何执行的?

作者头像
JavaEdge
发布2023-09-22 08:47:55
2880
发布2023-09-22 08:47:55
举报
文章被收录于专栏:JavaEdge

Redis Server一旦和某客户端建立连接,就会在事件驱动框架中注册可读事件,对应客户端的命令请求。

整个命令处理过程可分阶段:

  • 命令解析,processInputBufferAndReplicate
  • 命令执行,processCommand
  • 结果返回,addReply

1 命令读取:readQueryFromClient

会从客户端连接的socket中,读取最大为readlen长度的数据,readlen大小为宏定义PROTO_IOBUF_LEN,默认16KB。

接着根据读取数据的情况,进行异常处理,如:

  • 数据读取失败
  • 或客户端连接关闭等

若当前客户端是主从复制中的主节点,readQueryFromClient会把读取的数据,追加到用于主从节点命令同步的缓冲区中。

最后,调用processInputBuffer,进入命令解析阶段。

代码语言:javascript
复制
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
   ...
   readlen = PROTO_IOBUF_LEN;  // 从客户端socket中读取的数据长度,默认16KB
   ...
   c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);  // 给缓冲区分配空间
   nread = read(fd, c->querybuf+qblen, readlen);  // 调用read从描述符为fd的客户端socket中读取数据
    ...
    processInputBufferAndReplicate(c);  // 进一步处理读取内容
}

2 命令解析:processInputBuffer

根据当前客户端是否有CLIENT_MASTER标记,执行如下分支:

  • Case1 客户端无CLIENT_MASTER标记,即当前客户端不属于主从的Master。processInputBufferAndReplicate直接调processInputBuffer,对客户端输入缓冲区中的命令和参数进行解析。所以在这里,实际执行命令解析的函数是processInputBuffer
  • Case2 客户端有CLIENT_MASTER标记。processInputBufferAndReplicate除了会调用processInputBuffer,解析客户端命令,还会调用replicationFeedSlavesFromMasterStream,将主节点接收到的命令同步给从节点

最终命令解析就在processInputBuffer

  • 首先,processInputBuffer函数会执行一个while循环,不断从客户端的输入缓冲区读数据
  • 然后,判断读取到的命令格式,是否以“*”开头:
    • 命令 *开头,processInputBuffer会调processMultibulkBuffer解析读取到的命令
    • 不是*开头,即管道命令,命令和命令间用换行符\r\n分隔的。如使用Telnet发给Redis的命令就属该类型命令。processInputBuffer会调用processInlineBuffer解析命令。

命令解析完成后,processInputBuffer就会调用processCommand,进入命令处理的第三阶段:命令执行。

执行流程图

3 命令执行:processCommand

实际执行命令前的主要逻辑:

  1. processCommand调moduleCallCommandFilters,将Redis命令替换成module想替换的命令
  2. processCommand判断当前命令是否为quit命令并做相应处理
  1. processCommand调lookupCommand,在全局变量server的commands成员变量中查找相关命令

全局变量server的commands成员变量是个哈希表,定义在redisServer结构体:

commands成员变量的初始化是在initServerConfig,调用dictCreate完成哈希表创建,再调用populateCommandTable将Redis提供的命令名称和对应的实现函数,插入哈希表。

而这其中的populateCommandTable使用redisCommand结构体数组redisCommandTable。

redisCommandTable数组在server.c定义,它的每一个元素是redisCommand结构体类型的记录,对应Redis实现的一条命令。即redisCommand结构体记录当前命令所对应的实现函数。

如下代码展示GET、SET等命令信息,实现函数getCommand,setCommand:

所以lookupCommand会根据解析的命令名称,在commands对应的哈希表中查找相应命令。

查到对应命令后,processCommand就会检查,如命令参数是否有效、发送命令的用户是否进行过验证、当前内存的使用情况等。

等processCommand对命令做完各种检查,就开始执行命令,判断当前客户端是否有CLIENT_MULTI标记:

  • 有,说明要处理Redis事务相关命令 按事务要求,调queueMultiCommand:将命令入队保存,等待后续再一把梭
  • 无,无关事务特性 调call实际执行命令。call通过调用命令本身,即redisCommand结构体中定义的函数指针完成。每个redisCommand结构体中都定义了其对应实现函数,在redisCommandTable数组。

分布式锁的加锁操作就是使用SET命令,就通过SET命令看一个命令实际执行过程。

SET命令对应实现函数setCommand:

  • 首先会判断命令参数,如是否带有NX、EX、XX、PX等可选项,若有,就会记录这些标记
  • 然后,调用setGenericCommand:根据setCommand记录的命令参数标记,进行相应处理。如命令参数中有NX,则setGenericCommand会调用lookupKeyWrite,查找要执行SET命令的K是否已存在
  • 若K已存在,则setGenericCommand会调用addReply,返回NULL,正符合分布式锁语义。

若SET命令可正常执行,即:

  • 命令带NX选项,但K不存在
  • 或带有XX选项,但K已存在

这样setGenericCommand就会调用setKey完成KV对的实际插入:

代码语言:javascript
复制
setKey(c->db,key,val);

然后,若命令设置了TTL,setGenericCommand还会调用setExpire函数设置过期时间。最后,setGenericCommand调用addReply函数,将结果返给客户端:

代码语言:javascript
复制
addReply(c, ok_reply ? ok_reply : shared.ok);
SET命令执行流程图

无论:

  • 在命令执行过程中,发现不符合命令的执行条件
  • 或是命令能成功执行

addReply函数都会被调用以返回结果。所以,这就进入命令处理过程的最后一个阶段:结果返回阶段。

4 结果返回:addReply

调用prepareClientToWrite,并在prepareClientToWrite中调用clientInstallWriteHandler,将待写回客户端加入到全局变量server的clients_pending_write列表。

然后,addReply会调用_addReplyToBuffer等函数,将要返回的结果添加到客户端的输出缓冲区。

至此,这就是一条命令如何从读取,经过解析、执行等步骤,最终将结果返给客户端,该过程以及涉及的主要函数:

若在前面命令处理过程中,都由I/O主线程处理,则命令执行的原子性肯定能得到保证,分布式锁的原子性也相应得到保证。

FAQ

但若这个处理过程配合I/O多路复用机制和多IO线程机制,那这俩机制是在这个过程的什么阶段发挥作用?会不会影响命令执行原子性?

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 命令读取:readQueryFromClient
  • 2 命令解析:processInputBuffer
    • 执行流程图
    • 3 命令执行:processCommand
      • SET命令执行流程图
      • 4 结果返回:addReply
      • FAQ
      相关产品与服务
      云数据库 Redis
      腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档