相比前面我们学习的数据结构与对象(读书笔记-《Redis设计与实现》(一)数据结构与对象(上)、读书笔记-《Redis设计与实现》(一)数据结构与对象(下)),这部分的内容可以说就是轻松 + 愉快了,只要能 Get 到这几个机制的要点就行。
01
数据库
Redis 将所有数据库都保存在 redisServer 结构中,客户端结构为 redisClient,它们的关键属性如下:
cpp
struct redisServer {
// 一个数组,保存着服务器中的所有数据库
redisDb *db;
// 服务器的数据库数量,默认是16
int dbnum;
// 记录了保存条件的数组,它包括秒数和修改次数属性
struct saveparam *saveparams;
// 修改计数器
long long dirty;
// 上一次保存的时间
time_t lastsave;
// AOF 缓冲区
sds aof_buf;
// ...
}
typedef struct redisClient {
// 客户端正在使用的数据库
redisDb *db;
// ...
} redisClient;
typedef struct redisDb {
// 数据库键空间,保存着数据库中的所有键值对。
// key 只能是字符串对象,value 则是五种类型中的一种
dict *dict;
// 过期字典,保存着键的过期时间。
// key 指向上面 dict 的 key,value 为过期时间的毫秒时间戳
dict *expires
// ...
}
这里需要重点看看 Redis 过期键的删除策略,一般来说,有三种思路:
-
定时删除:每设置一个带过期时间的键就创建一个定时器,定时器到点后执行删除。特点是 CPU 开销大,内存开销小。
-
惰性删除:不做处理,仅在对该键执行操作时进行判断,如果过期则删除。特点是 CPU 开销小,内存开销大,甚至可能导致内存泄露。
-
定期删除:每隔一段时间,对数据库进行检查并删除过期的键盘。特点是兼顾 CPU 和 内存。
Redis 的过期键删除策略使用的是惰性删除 + 定期删除。
惰性删除由 db.c/expireIfNeeded 函数实现,所有对键的读写命令在执行前,都会先调用该函数。
定期删除由 redis.c/activeExpireCycle 函数实现,每当 Redis 的服务器周期性操作函数 serverCron 执行时,就会调用该函数。
02
RDB 持久化
RDB(Redis Database)持久化就是将内存中的数据存储到磁盘,生成一个 RDB 文件,可手动执行或服务器根据配置定期执行。
手动执行 的命令为 SAVE (阻塞服务器进程)和 BGSAVE(派生出一个子进程,此时 Redis 会尽量减少别的内存开销,例如之前提到的 rehash )。
自动执行 则是根据时间 和修改次数来判断的,例如在300秒内修改了10次数据库,就执行一次 BGSAVE。相关属性在上面 redisServer 的代码中可以看到,当然,这些数字也可以通过配置修改。
RDB 文件是一个经过压缩的二进制文件,其结构如下:
-
REDIS:也就是 REDIS 这五个字符。
-
db_version:记录版本号。
-
database:包含零个或任意多个数据库。每个数据库的结构为 SELECTDB、db_numbe、key_value_pairs,分别是数据库号码标识符、数据库号码和键值对数据。
-
EOF:结束标识符。
-
check_sum:文件校验码,由前面的内容计算得出,用于检查文件是否有出错或损坏的情况。
03
AOF 持久化
除了 RDB,Redis 还提供了一种持久化机制叫 AOF(Append Only File),它是通过保存服务器所执行的命令来实现持久化的,步骤为:
-
命令追加:AOF 持久化功能开启时,服务器在执行完一个写命令后,会将其追加到缓冲区末尾(见上面代码中 redisServer 的 aof_buf 属性)。
-
文件写入与同步:Redis 服务器进程本身可看作一个事件循环,每次循环结束前会调用 flushAppendOnlyFile 函数来判断是否将缓冲区的内容持久化。其由配置项 appendfsync 来决定,包括 always、everysec、no,分别代表每次循环均同步、1秒同步1次(默认)、交由操作系统同步。很明显,另外两个选项都过于极端,前者浪费性能,后者故障时丢失过多数据。
说到这不难想到,如果先新增一个键再删除掉,那么这两个命令明显是可以"消除"掉的。
Redis 提供了 AOF 重写机制,也就是读取数据库中键当前的值,用一条命令去替代针对这个键的其他命令。
不过由于 AOF 重写会有大量写入操作,因此其被放入了子进程。当重写工作完成后,子进程会向父进程发送信号,父进程收到后会调用处理函数,将 AOF 重写缓冲区的内容写入新的 AOF 文件并替换掉旧的 AOF 文件。
总的来说,AOF 和 RDB 各有特点。前者有做优化,默认情况下最多丢1秒的数据,适合大多数场景,后者可以保存某个时间点的数据库状态,适合备份的场景。
原文链接:读书笔记-《Redis设计与实现》(二)单机数据库实现(上)
原创不易,点个关注不迷路哟,谢谢~
文章推荐: