且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Redis的IO多路复用和多线程特性会破坏分布式锁的原子性吗?(中)

更新时间:2021-11-08 03:02:01

3.3 命令执行:processCommand

实现在server.c,实际执行命令前的主要逻辑:

  1. processCommand调用moduleCallCommandFilters,将Redis命令替换成module想替换的命令
  2. processCommand判断当前命令是否为quit命令并做相应处理

Redis的IO多路复用和多线程特性会破坏分布式锁的原子性吗?(中)

3.processCommand调用lookupCommand,在全局变量server的commands成员变量中查找相关命令

Redis的IO多路复用和多线程特性会破坏分布式锁的原子性吗?(中)



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

Redis的IO多路复用和多线程特性会破坏分布式锁的原子性吗?(中)

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

Redis的IO多路复用和多线程特性会破坏分布式锁的原子性吗?(中)

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


redisCommandTable数组是在server.c文件中定义的,它的每一个元素是一个redisCommand结构体类型的记录,对应了Redis实现的一条命令。也就是说,redisCommand结构体中就记录了当前命令所对应的实现函数是什么。


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

Redis的IO多路复用和多线程特性会破坏分布式锁的原子性吗?(中)

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


那么,一旦查到对应命令后,processCommand函数就会进行多种检查,比如命令的参数是否有效、发送命令的用户是否进行过验证、当前内存的使用情况,等等。这部分的处理逻辑比较多,你可以进一步阅读processCommand函数来了解下。


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


  • 若有,说明要处理Redis事务相关命令

就要按事务要求,调用queueMultiCommand:将命令入队保存,等待后续再一把梭处理。

  • 若无,无关事务特性

processCommand调用call:实际执行命令。call函数执行命令是通过调用命令本身,即redisCommand结构体中定义的函数指针完成。每个redisCommand结构体中都定义了其对应实现函数,在redisCommandTable数组可查到。


分布式锁的加锁操作就是使用SET命令实现的,所以来看SET命令为例,来看一个命令实际执行过程。


SET命令对应实现函数setCommand:首先会判断命令参数,如是否带有NX、EX、XX、PX等可选项,若有,就会记录这些标记。


然后,setCommand会调用setGenericCommand:根据setCommand记录的命令参数标记,进行相应处理。如命令参数中有NX,则setGenericCommand会调用lookupKeyWrite,查找要执行SET命令的key是否已存在。


若K已存在,则setGenericCommand会调用addReply,返回NULL,正符合分布式锁的语义。


Redis的IO多路复用和多线程特性会破坏分布式锁的原子性吗?(中)

Redis的IO多路复用和多线程特性会破坏分布式锁的原子性吗?(中)

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

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


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

setKey(c->db,key,val);

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

addReply(c, ok_reply ? ok_reply : shared.ok);

SET命令执行流程:

Redis的IO多路复用和多线程特性会破坏分布式锁的原子性吗?(中)

无论:

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


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