数据库
书中第九章介绍了数据库实现的一些基础概念,简单总结一下值得注意的点
切换数据库
在服务器内部,客户端状态 redisClient 结构的 db 属性记录了客户端当前的目标数据库,这个属性是一个指向 redisDb 结构的指针:
typedef struct redisClient {
// ....
redisDb *db // 记录客户端当前正在使用的数据库
// ....
} redisClient
redisClietn.db 指针指向 redisServer.db 数组中的一个元素,而被指向的元素就是客户端的目标数据库,通过修改 redisClient.db 指针,让它指向服务器中的不同数据库,即可实现切换目标数据库的目的
键空间
维护操作
当使用 Redis 命令进行读写时,还有执行一些额外的维护操作
- 读取一个键之后(读写都需要进行读取操作),服务器会根据键是否存在,更新 hit 和 miss 次数
- 在读取一个键之后,更新键的 LRU
- 如果在读取键时发现键已过期,则服务器先删除这个键,在执行余下操作
- 如果 Client 端使用 WATCH 命令监视了某个键,服务器在对被监视的键进行修改之后,会将这个键标记为脏(dirty),从而让事务程序注意到这个键已经被修改过
- 每修改一次键,dirty计数器 + 1, 用于服务器持久化和复制操作
- 数据库通知
过期键删除策略
- 定时删除:对内存最友好,同时使用定时器,定时删除策略可以保证过期键会尽可能快的被删除,并释放过期键所占用的内存,同时对CPU最不友好,因为在过期键较多时,删除过期键会占用相当一部分CPU时间
- 惰性删除:对CPU时间最友好,程序只有在取出键时,才对键进行过期检查
- 定期删除:是前两种方法的折中
- 如果删除操作执行的太频繁,或者执行的时间太长,定期删除策略就会退化成定时删除策略
- 如果删除操作执行的太少,或执行的事件太长,定期删除策略就会和惰性删除操作一样
RDB持久化
由于 Redis 是内存数据库,一旦服务器进程退出,服务器中的数据库状态也会消失,因此为了实现持久化,Redis 提供了 RDB 持久化功能。
RDB 持久化功能生成的 RDB 文件是一个经过压缩的二进制文件,通过该文件可以还原生成 RDB 文件时的数据库状态。
RDB文件的创建与载入
Redis 支持两种命令生成 RDB 文件: SAVE 以及 BGSAVE (一个阻塞服务器,一个另起子进程创建)
# SAVE()
def SAVE():
# 创建 RDB 文件
rdbSave()
# BGSAVE()
def BGSAVE():
# 创建子进程
pid = fork()
# 如果是子进程则创建 RDB 文件
if pid == 0:
rdbSave()
# 创建完毕后向父进程发送信号
singnalParent()
else pid >