写在前面
redis 是一个非常优秀的 k-v 存储系统,其使用 单 Reactor 模式.最近在看 reids 设计与实现 以及 redis 实战,项目开发也使用过 redis, 但是对其底层实现不太了解,相结合书籍和源码阅读一下,在此记录一些点和不了解的地方.
源码阅读参考:http://blog.huangz.me/diary/2014/how-to-read-redis-source-code.html
黄建宏老师的书可以说是非常通俗易懂,而且让人对 redis 的架构和实现都有一定的了解.非常推荐他的书, redis 实战也是他翻译的.
另外,redis 源码相对于 nginx 源码来说太友善了,有时候一条语句会有很大篇幅的解释,让人能够看得比较明白.
redis 服务器中的数据库
源码相关:server.h
默认会创建 16 个 db.每个客户端会有自己的目标数据库.默认目标数据库为 0 号数据库.
redisDb 结构
typedef struct redisDb {
dict *dict; /* The keyspace for this DB */
dict *expires; /* Timeout of keys with a timeout set */
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/
dict *ready_keys; /* Blocked keys that received a PUSH */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
int id; /* Database ID */
long long avg_ttl; /* Average TTL, just for stats */
} redisDb;
键空间
源码相关:dict.c
dict 字典保存了数据库中的所有键值对,称其为键空间.其与用户所见的数据库直接对应.增删查改的操作都是对键空间操作的.
键过期
存储过期信息的空间
expires 中保存过期信息.key 指向键, val 为 long long 类型的过期时间.
过期策略
定时删除 (设置定时器),惰性删除(每次获取检查),定期删除(每隔一段时间删除) 前两种为主动删除策略,最后为被动删除策略.
redis 服务器实际使用的是后两种.
惰性删除的实现
源码相关:db.c/expireIfNeed 考虑键存在与否,以及是主还是从机的情况.
定期删除的实现
源码相关:expire.c/acitveExpireCycle
其实现比较复杂,在规定的时间内,分多次遍历服务器的数据库,从数据库的 expires 字典中随机检查一部分键的过期时间,并删除其中的过期键.
RDB对过期键的处理
已过期的键不会被保存到新创建的 RDB 文件中.
AOF文件对过期键的处理
不会有任何影响.该键被显示删除时,才会追加一条 DEL 命令.
复制模式下对过期键的处理(主从)
从服务器的过期键删除由主控制.访问从库,若主库没有发送删除命令,则从库则会继续返回.
RDB 持久化
源码相关:rdb.c
生成RDB 文件
源码相关:rdb.c/rdbSave /rdbSaveBackground
SAVE:阻塞 Redis 服务器,直到文件创建完成.
BGSAVE: 生成子进程,由子进程来完成.可设置自动间隔性保存.
载入 RDB 文件
源码相关: /rdbLoad
无专门命令,服务器启动自动执行.载入时服务器为阻塞状态.
ps: AOF 优先级比 RDB 高,所以 AOF 存在则使用 AOF.
自动执行 BGSAVE
源码相关:server.c/serverCron
检查条件首满足,满足就执行 BGSAVE.
RDB 文件结构 (二进制文件)
REDIS db_version databases EOF checker_sum
REDIS:标识为 RDB 文件
databases: 可存放多个数据库的信息,具体为 SELECTDB db_number key_value_pairs.SELECTDB 标识接下来读取的是一个数据库号码.
EOF:正文结束
完整的数据库 RDB 文件结构(包括数据库0和数据库3的信息):
REDIS db_vsersion SELECTDB 0 pairs SELECTDB 3 pairs EOF checker_sum