redis缓存命令处理实现原理

Redis服务器负责与多个客户端建立连接,处理客户端请求,保存各个数据库状态。使用由I/O多路复用技术实现的事件处理器,Redis服务器采用单线程单进程处理客户端命令请求。Redis通过redisServer结构体来记录服务端的各种状态。

命令请求执行过程

1、客户端发送命令请求,客户端将命令请求转换成协议格式。

2、服务端读取命令请求,将命令请求缓存在客户端输入缓冲区中,对输入缓冲区中的命令进行分析把参数和参数个数分别保存到客户端状态的argv属性和argc属性中,然后调用命令执行器执行指定的命令。

3、命令执行器根据argv[0]参数在命令表中查找参数所指定的命令,并将找到的命令保存到客户端状态cmd属性中

4、执行预备操作:

检查客户端cmd指针是否指向NULL,如果是返回一个错误;

根据客户端cmd属性指向的redisCommand结构的arity属性,检查命令请求所给定的参数个数是否正确,如果参数个数不正确返回错误;

检查客户端是否通过了身份验证,未通过身份验证的客户端只能执行AUTH命令,如果未通过身份验证的客户端执行了除AUTH之外的其它命令,返回错误;

如果服务器打开了maxmemory开关功能,那么在执行命令前,先检查服务器的内存占用情况,并在有需要时进行内存回收,从而使得接下来的命令能够顺利执行;

如果服务器上一次执行BGSAVE命令时出错,并且服务器打开了stop-writes-on-bgsave-error功能,而且服务器将要执行的是一个写命令,那么服务器将拒绝执行这个命令,并向客户端返回一个错误;

当客户端当前正在用SUBSCRIBE命令订阅频道,或者正在用PSUBSCRIBE命令订阅模式,那么服务器只会执行 客户端发来的SUBSCRIBE、PSUBSCRIBE、UNSUBSCRIBE、PUNSUBSCRIBE四个命令,其它命令都会被拒绝;

如果服务器正在进行数据载入,那么客户端发送的命令必须带有1标识(比如INFO、SHUTDOWN、PUBLISH) 才会被服务器执行,其它命令都会被服务器拒绝;

如果服务器因为执行Lua脚本而超时并进入阻塞状态,那么服务器只会执行客户端发来的SHUTDOWN nosave命令和SCRIPT KILL命令,其它命令都会被服务器拒绝;

如果客户端正在执行事务,那么服务器只会执行客户端发来的EXEC、DISCARD、MULTI、WATCH四个命令,其它命令都会被放进事务队列中;

如果服务器打开了监视器功能,那么服务器会将要执行的命令和参数等信息发送给监视器。

当完成了以上预备操作之后,服务器就可以开始真正执行命令了。

5、调用命令的实现函数,redisCommand中有个proc属性,它是一个函数指针,通过调用它来执行最终的命令实现。

6、执行后续工作:

如果服务器开启了慢日志查询日志功能,那么慢查询日志模块会检查是否需要为刚刚执行完的命令请求添加一条新的慢查询日志;

根据执行命令所耗费的时长,更新被执行命令的redisCommand结构的milliseconds属性,并它的calls计数器值增1;

如果服务器开启了AOF持久化功能,那么AOF持久化模块会将刚刚执行的命令请求写入到AOF缓冲区里面;

如果有其它从服务器正在复制当前服务器,那么服务器将刚刚执行的命令传播给所有从服务器;

7、将命令回复发送给客户端,命令实现函数会将命令回复保存到客户端的输出缓冲区里面,并为客户端的套接字关联命令回复处理器,当客户端套接字变为可写状态时,服务器将会执行命令回复处理器,将保存在客户端缓冲区中的命令回复发送个客户端。当命令发送完毕后,回复处理器清空客户端输出缓冲区。

serverCron函数

服务器每个100毫秒执行一次serverCron函数,通常情况下,该函数是redis服务器唯一的定时任务,它的主要职责如下:

1、更新服务器时间缓存,redisServer中有unixtime和mstime两个属性,由于100毫秒才调用一次,所以这两个属性保存的时间都是不精确的,如果要获取精确的系统时间,必须执行系统调用获取。

2、更新LRU时钟,redisServer中的lruclock属性保存了服务器的LRU时钟,它也是个服务器时间缓存,当服务器要计算一个数据库键的空转时间时,通过服务器的lruclock减redisObject的lru属性获取。serverCron默认以每10秒一次的频率更新lruclock,所以它也是一个估算值。

3、更新服务器每秒执行命令数。

4、更新服务器内存峰值,记录在redisServer的stat_peak_memory属性中。

5、处理SIGTERM信号,在启动服务器时,Redis会为服务器进程的SIGTERM信号关联处理器sigtermHandler函数,这个信号处理器负责在服务器接到SIGTERM信号时,打开服务器状态的shutdown_asap标识,每次serverCron函数运行时,程序会对服务器状态的shutdown_asap属性进行检查,并根据属性的值决定是否关闭服务器,在关闭服务器前服务器会进行RDB持久化操作,这就是服务器拦截SIGTERM信号的原因。

6、管理客户端资源,serverCron函数每次执行都会调用clientsCron函数, clientsCron函数会对一定数量的客户端进行一下两个检查:

如果客户端与服务器之间连接已经超时(很长时间没有互动),那么程序释放这个客户端,关闭连接。

如果客户端在上一次执行命令请求之后,输入缓冲区的大小超过了一定的长度,那么程序会释放客户端当前的输入缓冲区,并重新创建一个默认大小的输入缓冲区,从而防止客户端的输入缓冲区耗费了过多的内存

7、管理数据库资源,调用databaseCron函数,对服务器中的一部分数据库进行检查,删除其中的过期键,并在有需要时对字典进行收缩操作。

8、执行被延迟的BGREWRITEAOF,服务器在执行BGSAVE期间,如果客户端向服务器请求了BGREWRITEAOF命令,那么服务器会将BGREWRITEAOF命令延迟到BGSAVE执行完毕之后执行,通过redisServer中的aof_rewrite_scheduled记录(值为1表示有BGREWRITEAOF命令被延迟了)。

9、检查持久化操作的运行状态,服务器通过redisServer的rdb_child_pid和aof_child_pid属性记录执行BGSAVE和BGREWRITEAOF命令的子进程id,这个两个属性也可用于检查当前是否有BGSAVE或BGREWRITEAOF命令正在执行,每次serverCron执行时都会检查者两个属性的值,只要有一个值不为-1,程序就会执行一次wait3函数,检查子进程是否有信号发来服务器:

如果有信号达到,表示新的RDB文件已经生成或者AOF文件重写已经完毕,服务器需要进行相应的后续操作,比如用新的RDB文件替换现有的RDB文件,或者用重写后的AOF文件替换现有的AOF文件

如果没有信号到达,那么表示持久化操作未完成,不做动作。

如果rdb_child_pid和aof_child_pid两个属性的值都为-1,那么表示服务器没有在进行持久化操作,这种情况下,程序执行三个检查:

查看是否有BGREWRITEAOF被延迟,如果有,执行一次新的BGREWRITEAOF。

查看自动保存条件是否满足,如果满足并且服务器没有执行其他持久化操作,那么服务器开始一次新的BGSAVE。

检查AOF重写条件是否满足,如果满足并且服务器没有执行其他的持久化操作,执行一次新的BGREWRITEAOF命令。

10、将AOF缓冲区中的内容写入AOF文件,如果开启了AOF持久化功能,并且AOF缓冲区还有待写入的数据,那么serverCron会调用相关的函数将AOF缓冲区中的内容写入到AOF文件中

11、关闭异步客户端,服务器会关闭那些输出缓冲区超出限制的客户端。

12、增加cronloops计数器的值,这个值记录了serverCron函数被执行了多少次,这个属性会用来“每执行N次serverCron函数就执行一次指定代码”的功能。

image.png

根据key路由到具体的redis实例上,查询返回

[redis 命令的调用过程]

(https://www.cnblogs.com/bush2582/p/9326745.html)

Redis 命令参考

你可能感兴趣的:(redis缓存命令处理实现原理)