对RDB持久化的实现过程进行剖析
前面讲了RDB持久化的实现,其主要保存的是数据库中的键值对。除了RDB持久化功能之外, Redis还提供了AOF(Append Only File)持久化功能。与RDB持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。
实现
服务器中的缓冲区
在Redis中服务器状态结构体redisServer
中有一个字段是aof_buf
的缓冲区,当服务器执行完一条命令以后会以协议格式将被执行的写命令追加到服务器状态的aof_buf
缓冲区的末尾:
1 | struct redisServer { |
AOF文件的写入与同步
Redis的服务器进程就是一个事件循环(loop),这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复,而时间事件则负责执行像server_cron函数这样需要定时运行的函数。
因为服务器在处理文件事件时可能会执行写命令,使得一些内容被追加到 aof_buf缓冲区里面,所以在服务器每次结束一个事件循环之前,它都会调用flushAppendOnlyFile
函数,考虑是否需要将 aof_buf缓冲区中的内容写人和保存到AOF文件里面
flushAppendOnlyFile函数的行为由服务器配置的appendfsync选项的值来决定,各个不同值产生的行为
命令写入实现:
1 | void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) { |
缓冲区人间写入AOF实现:
1 | void flushAppendOnlyFile(int force) { |
对于三种不同的策略其效率和安全性能是不一样的:
always
因为在每一个事件循环中都需要将所有的内容写入AOF文件,所以其效率是最低的,但是其安全性是最高的everysec
是一个效率和安全性的折中。从效率上来讲, everysec模式足够快,并且就算出现故障停机,数据库也只丢失一秒钟的命令数据。no
的同步时间需要有操作系统控制,这样其效率比较高但是安全性就比较差了
文件载入和数据的还原
因为AOF文件里面包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一遍AOF文件里面保存的写命令,就可以还原服务器关闭之前的数据库状态。
其底层主要通过loadAppendOnlyFile
这个函数实现:
1 | /* |
AOF重写
因为AOF持久化是通过保存被执行的写命令来记录数据库状态的,所以随着服务器运行时间的流逝,AOF文件中的内容会越来越多,文件的体积也会越来越大,如果不加以控制的话,体积过大的AOF文件很可能对 Redis服务器、甚至整个宿主计算机造成影响,并且AOF文件的体积越大,便用AOF文件来进行数据还原所需的时间就越多。
为了解决AOF文件体积膨胀的问题, Redis提供了AOF文件重写( rewrite)功能。通过该功能, Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件,新旧两个AOF文件所保存的数据库状态相同,但新AOF文件不会包含任何浪费空间的冗余命令,所以新AOF文件的体积通常会比旧AOF文件的体积要小得多。
AOF重写实现
AOF重写部分的主要实现(遍历所有的键,通过命令进行插入)如下:
1 | int rewriteAppendOnlyFile(char *filename) { |
因为新的AOF文件只包含还原当前数据库所必须的命令,所以新AOF文件不会浪费任何空间。