2021/02/06 Redis 共 2972 字,约 9 分钟 数据库
- Redis服务器的所有数据都保存在
redisServer.db
数组中,而数据库的数量则由redisServer.dbnum
属性保存 - 客户端通过修改目标数据库指针,让它指向
redisServer.db
数组中的不同元素来切换不同的数据库 - 数据库主要由
dict
和expires
两个字典构成,其中dict
字典负责保存键值对,而expires
字典则负责保存键的过期时间 - 对数据库的操作都是建立在字典操作之上的
- 数据库的键总是一个字符串对象,而值则可以是任意一种Redis对象类型,包括字符串对象、哈希表对象、集合对象、列表对象和有序集合对象,分别对应字符串键、哈希表键、集合键、列表键和有序集合键
expires
字典的键指向数据库中的某个键,而值则记录了数据库键的过期时间,过期时间是一个以毫秒为单位的UNIX时间戳- Redis使用惰性删除和定期删除两种策略来删除过期的键:惰性删除策略只在碰到过期键时才进行删除操作,定期删除策略则每隔一段时间主动查找并删除过期键
- 执行
SAVE
命令或BGSAVE
命令所产生的新RDB文件不会包含已经过期的键 - 执行
BGREWRITEAOF
命令所产生的重写AOF文件不会包含已经过期的键 - 当一个过期键被删除后,服务器会追加一条
DEL
命令到现有AOF文件的末尾,显示地删除过期键 - 当主服务器删除一个过期键之后,它会向所有从服务器发送一条
DEL
命令,显示地删除过期键 - 从服务器即使发现过期键也不会自作主张地删除它,而是等待主节点发来
DEL
命令,这种统一、中心化的过期键删除策略可以保证主从服务器数据的一致性 - 当Redis命令对数据库进行修改之后,服务器会根据配置向客户端发送数据库通知
持久化
RDB
- RDB文件用于保存和还原Redis服务器所有数据库中的所有键值对数据
SAVE
命令由服务器直接执行保存操作,所以该命令会阻塞服务器BGSAVE
命令由子进程执行保存操作,所以该命令不会阻塞服务器- 服务器状态中会保存所有用
SAVE
选项设置的保存条件,当任意一个保存条件被满足时,服务器会自动执行BGSAVE
命令 - RDB文件是一个经过压缩的二进制文件,由多个部分组成
- 对不同类型的键值对,RDB文件会使用不同的方式来保存它们
- 服务器载入RDB文件期间,会一直处于阻塞状态,直到载入工作完成为止
BGSAVE
命令执行期间,服务器会拒绝客户端发送的BGSAVE
命令(竞争条件),而客服端发送的BGREWRITEAOF
命令会被延迟到BGSAVE
命令执行完毕之后执行(性能考虑)BGREWRITEAOF
执行期间,客户端发送的BGSAVE
会被服务器拒绝(性能考虑)
AOF
- AOF文件通过保存所有修改数据库的写命令请求来记录服务器的数据库状态
- AOF文件中的所有目录都以Redis命令请求协议的格式保存
- 命令请求会先保存到AOF缓冲区里面,之后再定期写入并同步到AOF文件
APPENDFSYNC
选项的不同值对AOF持久化功能的安全性以及Redis服务器的性能有很大的影响- 服务器只要载入并重新执行保存在AOF文件中的命令,就可以还原数据库本来的状态
- AOF重写可以产生一个新的AOF文件,这个新的AOF文件和原有的AOF文件所保存的数据库状态一样,但体积更小
- AOF重写是一个有歧义的名字,该功能是通过读取数据库的键值对来实现的,程序无须对现有AOF文件进行任何读入、分析或写入操作
- 在执行
BGREWRITEAOF
命令时,Redis服务器会维护一个AOF重写缓冲区,该缓冲区会在子进程创建新AOF文件期间,记录服务器执行的所有写命令。当子进程完成创建新AOF文件的工作之后,服务器会将缓冲区的所有内容追加到新的AOF文件的末尾,使得新旧两个AOF文件所保存的数据库状态一致。最后服务器用新的AOF文件替换旧的AOF文件,以此来完成AOF文件重写操作 - 在整个AOF后台重写过程中,信号处理函数执行时会对服务器(父)进程造成阻塞,在其他时候,AOF后台重写都不会阻塞服务器(父)进程 ,信号处理函数有两个工作
- 将AOF重写缓冲区的所有内容写入到新AOF文件中
- 对新的AOF文件进行改名,并原子地覆盖现有的AOF文件,完成新旧两个AOF文件的替换
- 子进程在执行AOF重写期间,服务器进程需要执行三个工作
- 执行客户端发送来的命令
- 将执行后的写命令追加到AOF缓冲区(保证现有AOF文件的处理工作会如常进行)
- 将执行后的写命令追加到AOF重写缓冲区(保证从创建子进程开始,服务器执行的所有写命令都会被记录到AOF重写缓冲区里面)
事件
- Redis服务器是一个事件驱动程序,服务器处理的事件分为时间事件和文件事件
- 文件事件处理器是基于Reactor模式实现的网络通信程序
- 文件事件是对套接字操作的抽象:每次套接字变为acceptable、writeable、readable时,相应的文件事件就会产生
- 文件事件分为AE_READABLE事件和AE_WRITEABLE事件两类
- 时间事件分为定时事件和周期性事件:定时事件只在指定的时间达到一次,而周期性事件则每隔一段时间达到一次
- 服务器在一般情况下只执行
serverCron
函数一个时间事件,并且这个事件是周期性事件 - 文件事件和时间事件之间是合作关系,服务器会轮流处理这两种事件,并且处理事件的过程也不会进行抢占
- 时间事件的实际处理时间通常会比设定的到达时间晚一些
客户端
- 服务器状态结构使用clients链表连接起多个客户端状态,新添加的客户端状态会被放到链表的末尾
- 客户端状态的flags属性使用不同标志来表示客户端的角色,以及客户端当前所处的状态
- 输入缓冲区记录了客户端发送的命令请求,这个缓冲区的大小不能超过1GB
- 命令的参数和个数会被记录在客户端的
argv
和argc
属性里面,而cmd
属性则记录了客户端要执行命令的实现函数 - 客户端有固定大小缓冲区和可变大小缓冲区两种缓冲区可用,其中固定大小缓冲区的最大大小为16KB,而可变大小缓冲区的最大大小不能超过服务器设置的硬性限制值
- 输出缓冲区限制值有两种,如果输出缓冲区的大小超过了服务器设置的硬性限制,那么客户端会被立即关闭;除此之外,如果客户端在一定时间内,一直超过服务器设置的软性限制,那么客户端也会被关闭
- 当一个客户端通过网络连接连上服务器时,服务器会为这个客服端创建相应的客户端状态。网络连接关闭、发送了不符合协议格式的命令请求、成为
CLIENT KILL
命令的目标、空转时间超时、输出缓冲区的大小超出限制,以上原因都会造成客户端被关闭 - 处理Lua脚本的伪客户端在服务器初始化时创建,这个客户端会一直存在,直到服务器关闭
- 载入AOF文件时使用的伪客户端在载入工作开始时动态创建,载入工作完毕之后关闭
服务器
- 一个命令请求从发送到完成主要包括以下步骤
- 客户端将命令请求发送给服务器
- 服务器读取命令请求,并分析出命令参数
- 命令执行器根据参数查找命令的实现函数,然后执行实现函数并得出命令回复
- 服务器将命令回复返回给客户端
serverCron
函数默认每隔100毫秒执行一次,它的工作主要包括更新服务器状态信息,处理服务器接收的SIGTERM信号,管理客户端资源和数据库状态,检查并执行持久化操作等等- 服务器从启动到能够处理客户端的命令请求需要执行以下步骤
- 初始化服务器状态
- 载入服务器配置
- 初始化服务器数据结构
- 还原数据库状态
- 执行事件循环