1. Redis内部实现
1.1. 单机数据库
struct redisServer{
...
redisDb *db; //数据库格式
int dbnum; //数据库个数,默认16个,通过SELECT <num>来切换。
}
reidsDb数据结构里每个dict都称作数据库键空间,下面保存该数据库的所有键值对:
1.1.1. 键的过期时间
EXPIRE key 5(精度:秒) --查看:TTL key
PEXPIRE(精度:毫秒) --查看:PTTL key
PERSIST key (取消过期设定,即删除了过期字典中关联)
内部实现:
过期键的删除策略:
1.定时删除(创建过期键时,同时创建定时器,并在到达时间执行删除操作,此方案会创建大量定时器)
2.惰性删除:放任过期键不管,只在下次再使用时再判断并删除;
3.定期删除:每个一段时间遍历并删除过期键。
redis采用惰性删除和定期删除组合的策略。
1.1.2. RDB和AOF持久化
Reids是内存数据库,并将自己的数据库状态存储在内存里面。它支持通过RDB和AOF方式进行持久化。
RDB
保存数据库的状态
redis>SAVE //创建RDB文件
OK
redis> BGSAVE //派生子进程,开始将数据库内容保存到RDB文件,此时服务不被阻塞,通过 save 800 1(800s内有一次写操作就执行BGSAVE)命令可以设置保存条件,但对于过期键不在保存。
恢复RDB文件只在服务器启动过程,没有人工命令行,且载入过程服务器会被阻塞。
数据结构:
其中pairs数据结构如下:
文件分析可以通过RDB文件分析工具查看,具体可再从网络搜索。
AOF
保存被执行过的写命令(以redis命令请求协议格式保存),通过执行过往命令来恢复数据库状态。
AOF重写功能实现:从数据库中读取现在的值,然后用一条命令去记录键值对,代替之前写这些键值对的多条命令,从而压缩AOF文件的大小。
说明:
(1)重写时,当键值对超过64个时,将分条去记录键值对,且不保存过期键。
(2)AOF过程通过创建子进程来完成,并不阻塞server的服务。
(3)appendfsync选项:always 每个命令处理后都写AOF
Everysec 每秒写一次,但可能丢失1秒内数据
no 系统调用fsync,一般30s,但可能丢失30s内数据
1.1.3. 命令执行请求过程
struct redisServer{
redisDb*db; //记录数据库状态
clients *redisClient; //记录客户信息的链表
…
}
(1)客户端发起连接(connect),服务端将创建redisClient链表节点,并记录fd,flag角色状态等信息
struct redisClient{
Fd; //文件描述符
Name; //可设置,默认为空
Flag; //两类:角色类:MASTER\SALVE\LUA_CLIENT等
状态类:MONITOR\BLOCKED等
Querybuf; //协议格式存储
argv[];
Argc;
}
(2)客户端发送命令后,服务端读取并放入输入缓存
之后,将缓存SDS数据解析成对象数组写入argv[]:
(3)命令执行器根据argv[0],到命令表中查找对应执行命令,并保存到cmd属性里,再执行命令。
(4)命令执行后产生命令回复,回复信息将保存到输出缓存区,并发送给客户端显示。
注意:
(1)如果开启漫查日志功能,那么刚执行命令如果交长时将被记录;
(2)如果开启AOF持久化功能,那么刚执行写命令将根据appendfsync参数决定是否写入AOF持久化;
1.1.4. Server的启动流程与事件处理
Redis的事件主要分为文件事件和定时器事件,作者对这两种事件处理的高端之处在于预先计算最近一个时间事件要超时的间隔,并将这个空闲时间间隔用作为poll函数监听文件事件的超时时间,之后再依次处理文件事件和定时事件。
补充:
1. main和eventLoop之间做了server的初始化工作,包括:
(1)创建redisServer实例,通过配置文件中配置给实例属性赋值;
(2)创建redisDb数据库状态;
(3)创建redisClient链表;
(4)加载RDB或AOF文件,恢复redisDB数据库状态;
2. 文件事件处理:应用实例如set key val命令请求执行过程。
3. 时间时间处理:应用实例如ServeCron() {
(1)更新server状态
(2)Client资源状态和数据库状态
(3)执行持久化
}
1.1.5. 文件事件处理机制(Reactor模式)
(待补充)