一、客户端
Redis 服务器状态结构的 clients 属性是一个链表,这个链表保存了所有与服务器连接的客户端的状态结构,对客户端执行批量操作,或者查找某个指定的客户端,都可以通过遍历 clients 链表来完成
1 | struct redisServer { |
而这个 clients 的每个节点指向的结构体如下
1 | struct redisClient { |
- 套接字描述符 fd,可以是 -1 是 伪客户端,一个是用于载入AOF文件并还原数据库状态,一个是用于执行 Lua 脚本中包含的 Redis 命令。大于 -1 的整数即为网络套接字
- 名字 name,可以使用
client setname
设置客户端名字 - 标志 flags 记录了客户端的角色,以及客户端目前所处的状态
- 输入缓冲区 querybuf 用于保存客户端发送的命名请求,输入缓冲区的大小会根据输入内容动态的缩小或者扩大,但它不能超过1GB,否则服务器将关闭这个客户端。
- 服务器对 querybuf 输入缓冲区的内容进行分析,将得出的命令参数以及个数分别保存到客户端状态的 argv 属性和 argc 属性。
- argv[0] 存储的是 Redis 的命令字,然后在命令表中查找命令所对应的命令实现函数。命令表是一个字典,键是 sds 的命令名字,值是该命令对应的 redisCommand 结构,这个结构保存了命令的实现函数、命令的标志等等信息。redis 的名字不区分大小写
- 执行命令所得的命令回复会被保存在客户端状态的输出缓冲区里面,有两个缓冲区,一个固定大小,一个大小可变。固定大小的缓冲是 buf,bufpos 表示长度,保存哪些长度比较小的回复。当 buf 数组的空间已经用完,或者回复因为太大而没有办法放进 buf 数组里面时,服务器就会开始使用可变大小缓冲区。可变大小缓冲区由 reply 链表和一个或多个字符串对象组成。
- 身份验证 authenticated ,当为 0 时,除了 AUTH 命令之外,客户端发送的所有其他命令都会被服务器拒绝执行。
二、服务器
1. 命令请求执行过程
客户端发送 set key value
命令在获得回复 OK 期间,会进行如下操作:
- 客户端向服务端发送
set key value
请求 - 服务器接收并处理客户端发来的命令请求
set key value
,在数据库中进行设置操作,并产生命令回复 OK - 服务器将命令回复 OK 发送给客户端
- 客户端接收服务器返回的命令回复 OK,并将这个回复打印给用户
2. 命令执行过程
- 先查找命令实现,并将找到的命令保存到客户端状态的 cmd 属性里面
- 执行预备操作,
- 如果找不到相应的命令字实现,则向客户端返回一个错误返回
- 根据客户端 cmd 属性指向的 redisCommand 结构的 arity 属性,检查命令请求所给定的参数个数是否正确,当参数个数不正确时,不再执行后续操作,返回客户端错误。比如,arity 属性的值为 -3,那么用户输入的命令参数个数必须大于等于 3个才行
- 身份验证,auth
- 如果服务器打开了 maxmemory 功能,那么在执行命令之前,先检查服务器的内存占用情况,并在有需要时进行内存回收,如果回收失败,向客户端返回错误
- 如果服务器上一次执行 BGSAVE 命令时出错,并且服务器打开了 stop-writs-on-bgsave-error 功能,而且服务器即将要执行的命令是写命令,那么服务器将拒绝执行这个命令,并向客户端返回一个错误
- 如果服务器正在进行数据载入,那么客户端发送的命令必须带有 1 标识(比如 INFO、SHUTDOWN、PUBLISH)才会被服务器执行,其他命令都会被服务器拒绝
- 等等操作
- 调用命令的实现函数,并产生相应的命令回复,这些回复会被保存在客户端状态的输出缓冲区中,之后实现函数还会为客户端的套接字关联命令回复处理器,这个处理器负责将命令回复返回给客户端
- 执行后续工作,比如:
- 如果服务器开启了 AOF 持久化功能,那么AOF 持久化模块会将刚刚执行的命令请求写入到 AOF 缓冲区中
- 如果有其他从服务器正在复制当前这个服务器,那么服务器会将刚刚执行的命令传播给所有从服务器