1. 服务器定义
Redis服务段结构是其最核心的功能结构,负责维护与客户端键的网络连接,维护数据库状态,执行客户端的命令请求,统计一些运行数据等等。
1 | struct redisServer { |
2. Redis命令
2.1 命令表 commands
命令表存储了Redis可执行的命令字典,并对每个命令设定了一些执行参数。orig_commands是重命名之后的命令表,通过重命名可以提升Redis的安全性,比如讲KEYS, FLUSHDB这类的命令重命名为别的,这样一般用户在就不知道其真实的命令。
- 命令结构表示如下
字段名 | 描述 |
---|---|
name | 命名的名称,用户执行命令通过查找命令表匹配该字段来寻找相应的命令 |
function | 执行命令实现的方法 |
arity | 命令参数的个数,-N表示大于等于N。命令本身也是一个参数 |
sflags | 字符串形式标识符,用于设置命令的属性 |
flags | sflags标识符的二进制标识,由sflags计算 |
get_keys_proc | 一个可选函数,用于获取命令参数,只有在first_key_index,last_key_index,key_step无法指定哪些是参数时才使用此选项 |
first_key_index | 第一个参数是key |
last_key_index | 最后一个参数是key |
key_step | key之间的步长,如MSET步长为2, MSET key value key2 value2 … |
microseconds | 服务器执行该命令耗费的时间 |
calls | 服务器总共执行了多少次该命令 |
- 命令的属性sflag标识
标识 | 意义 |
---|---|
w | 写命令 |
r | 读命令 |
m | 该命令会占用大量内存,执行之前需要检查内存情况 |
a | 管理命令,如SAVE,SHUTDOWN等 |
p | 发布订阅模式的命令 |
f | 强制复制命令,无视服务器脏计数 |
s | Lua脚本中不允许的命令 |
R | 随机命令,相同情况下,结果可能不同 |
S | Lua脚本中使用标识则需要对结果进行排序 |
l | 服务器载入情况下可以使用的命令 |
t | 从节点服务器数据过期时允许执行的命令 |
M | 在MONITOR模式下不会自动传播 |
k | 执行一个显示的ASKING,使得在集群模式下,被标志为imporing的槽可以接受该命令 |
F | 快速模式,O(1)或O(log(N))的复杂度 |
2.2 命令请求执行过程
客户端发送命令到服务端执行返回有一下几个步骤:
客户端与服务端建立连接,按照一定格式封装命令
1
2# SET msg hello 命令转换后如下
*3\r\n$3\r\nSET\r\n$3\r\nmsg\r\n$5\r\nhello\r\n服务器接收到来自网络客户端的请求数据,按照协议格式解析命令
- 读取套接字中的命令请求,存储到对应的客户端输入缓冲区中
- 读取缓冲区中数据,解析命令,提取命令请求和参数
获得解析命令后查找服务器的命令表,找到对应的命令及其执行方法
- 查找服务器命令表匹配redisCommand结构中的name字段
- 查询到命令后获取其执行方法,属性等,将命令执行方法保存在客户端cmd字段中
服务器根据执行命令,并返回处理后数据
客户端接收到服务器处理回复
命令执行时会根据其sflag标志和服务器设置有不同的处理操作,具体情况可看源码
3. Redis周期性任务
服务器为了维护自身资源,默认每100毫秒执行一次周期性任务serverCron(),主要负责删除过期键、服务器状态监控、更新统计信息、渐进式rehash、触发BGSAVE/AOF重写并处理子进程中断、从节点复制重连等等。
3.1 更新服务器时间缓存
Redis服务器很多功能需要获取系统当前时间,该系统调用需要消耗一定的CPU时间,对某些时间精度要求不高的功能我们可以在serverCron()中获取系统时间后缓存下来,可以有效的减少系统调用次数,服务器状态中unixtime和mstime属性用于存储时间缓存。
对于日志打印、更新服务器LRU时钟等任务可以使用服务器缓冲时间;对于过期键的检测、慢查询日志等需要高精度时间任务来说,需要重新系统调用获取时间来计算。
3.2 处理SIGTERM信号
启动服务器时,Redis会设置SIGTERM信号处理函数,当服务器接收到SIGTERM信号时,会开启shutdown_asap标识。serverCron()函数中,每次都会检测该标识,如果设置该标识,服务器会安全关闭,并记录日志。
3.3 客户端资源管理clientsCron()
serverCron()函数每次执行都会调用clientsCron函数,因为函数每秒调用server.hz(默认10),为了确保每个客户端至少执行一秒,所以迭代次数至少为numclients/server.hz,该函数执行以下检查:
- 如果客户端与服务器连接超时,那么释放该客户端
- 如果客户端输入缓冲区超过一定限制,则重新分配缓存区的内存空间,确保没有浪费
3.4 管理数据库资源databaseCron()
serverCron()函数每次执行会调用databaseCron()函数,该函数会对数据库进行检查,删除过期键,resize,rehash等操作。
- 主节点则开启过期键自动删除功能,从节点直接删除过期键
- 内存碎片整理
- 如果服务器为进行AOF或RDB,则进行Rehash和Resize
3.5 持久化记录
如果服务器没有RDB或AOF持久正在进行,那么开启后台的AOF重写任务;如果有RDB或AOF,则等待子进程信号,如果接收到信号说明持久化已完成,否则表示未完成。
即使AOF重写错误,也需要刷新AOF缓冲区
3.6 更新信息并记录日志
- 更新服务器每秒执行命令次数
- 更新LRU时钟
- 更新内存使用峰值
- 记录非空数据库日志
- 非哨兵模式服务器,记录客户端连接日志
3.7 其他一些任务
异步关闭需要关闭的客户端
如果需要解除客户端暂停状态
周期性复制任务
集群模式下,集群周期性任务
哨兵模式下任务
清理sockets连接
4. 源码剖析
- 周期性任务
1 | /** |
- 进入事件循环前调用
1 | /** |