Chapter 9 - 数据库
Created by : Mr Dk.
2020 / 06 / 02 14:15
Nanjing, Jiangsu, China
Databases
Redis 服务器将所有的数据库都保存在如下结构体中。初始化服务时,会根据其中的 dbnum
属性来决定应创建多少个数据库。这个属性可以在服务器配置中指定,默认 16。
struct redisServer {
// ...
redisDb *db;
// ...
int dbnum;
// ...
}
在服务器内部,维护客户端状态的结构体中的一个指针记录了当前客户端正在使用的数据库:
typedef struct redisClient {
// ...
redisDb *db;
// ...
} redisClient;
Key Space
Redis 是一个 key-value 数据库服务器。每个数据库中都有一个 key space,是一个字典,保存了数据库中所有的键值对:
typedef struct redisDb {
// ...
dict *dict;
// ...
} redisDb;
之后的增删改查全部通过对 key space 进行增删改查完成。在进行读写操作的同时,Redis 还需要进行一些额外的维护操作:
- 服务器根据读写访问的 key 是否存在,更新 hit/miss 的次数
- 更新 key 的 LRU 时间
- 如果 key 已过期,则先删除这个 key
- 如果客户端监视该 key,那么会将该 key 标记为脏
- 服务器修改 key 后都会对 dirty 计数器 +1 (用于触发持久化操作或复制操作)
- 如果开启了数据库通知,那么对 key 修改后,服务器按配置推送通知
Key Expiration
客户端可以以秒或毫秒精度为数据库中的 key 设置 TTL (Time To Live)。在到期后,服务器自动删除过期的 key。
在 redisDb
中有一个 expires
字典专门用于保存数据库中所有 key 的过期时间。过期时间以 long long
类型的整数保存,是一个毫秒精度的 UNIX 时间戳。
typedef struct redisDb {
// ...
dict *expires;
// ...
} redisDb;
过期 key 的判定:
- key 存在于过期字典
- 当前 UNIX 时间戳是否大于过期时间
Removing Strategy Candidate
几种可能的删除机制:
定时删除
在设置 key 过期时间的同时,设置一个 timer,在 timer 到期时删除 key。
- CPU 不友好 - 时间浪费在与任务无关的过期 key 上,影响吞吐量
- 内存友好 - 过期的 key 会被尽快删除
- 创建定时器的内部实现是无序链表 - 查找时间复杂度为 O(N)
惰性删除
只有在取出 key 时才对 key 进行过期检查。
- 节省 CPU 到了最大程度
- 对于内存十分不友好,因为有些 key 如果不被访问就永远不会释放
定期删除
是上述两种策略的折衷。每隔一段时间执行一次删除过期 key 的操作,并限制删除操作执行的时间与频率。最重要的点是控制好时间与频率,如果时间太长、频率高,那么就退化为定时删除;反之,则退化为惰性删除。
Removing Strategy Implementation
Redis 服务器实际使用了惰性删除和定期删除两种策略。
惰性删除实现
在所有读写数据库的命令之前,都会对操作的 key 进行检查:
- 如果 key 已经过期,那么将该 key 从数据库中删除
- 如果 key 未过期,那么检查函数直接通过
定期删除实现
在 规定时间 内,周期性地 分多次 遍历服务器中的各个数据库,从每个数据库的过期字典中 随机检查 一部分过期时间,并删除其中的过期 key。
需要用一个值记录当前的检查进度 - 假设 16 个数据库,第一次检查了前四个,第二次就从第五个开始检查,一轮检查完毕后,开启新的一轮。
持久化过期策略
对于 RDB 持久化功能,已过期的 key 将不会被保存到文件中;载入 RDB 文件时,过期的 key 将不会被载入到数据库中 (主数据库)。从数据库将载入所有 key。
对于 AOF 持久化,当过期的 key 被删除后,程序会向 AOF 文件追加 DEL 命令来显式标记删除。
当服务器运行在复制模式时,从服务器 的过期 key 由 主服务器 控制 (为了保证两者的一致性)。
- 主服务器删除过期 key 后,将显式通知从服务器删除
- 从服务器不会主动删除过期 key
数据库通知
两个类型的通知:
- Key-space notification - 键空间通知 - 某个 key 执行了什么命令
- Key-event notification - 键事件通知 - 某个命令被什么 key 执行了