一.AOF持久化的实现
- 命令追加
当AOF持久化功能打开状态时,服务器在完成一个命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf的缓冲区末尾:
struct redisServer{
//AOF缓冲区
sds aof_buf;
};
- AOF文件写入与同步
在服务器处理文件事件的时候可能会执行写命令,使得一些命令被添加到aof_buf中。在服务器每次结束一个循环事件之前,都会调用flushAppendOnlyFile函数,考虑是否将aof_buf缓冲区的内容写入保存到AOF文件中。
二.AOF文件的载入与数据还原
redis读取AOF文件并还原数据库状态的流程:
- 创建一个不带连接的伪客户端:因为Redis的命令只有在客户端上下文中执行,而载入AOF文时使用的命令直接来源与AOF文件而不是网络连接,所以使用一个没有连接的伪客户端来达成效果。
- 从AOF中分析并读取一条写命令。
- 使用伪客户端执行被读出的写命令。
- 重复执行2,3步骤,直到AOF中所有的写命令都被处理完成。
三.AOF重写
因为高频率的读写命令,导致AOF文件过大,使用AOF文件进行还原的事件就越来越多,为了解决文件过大的问题,redis受用重写功能,创建一个新的AOF文件来替代旧的AOF文件,新旧两个保存的数据库相同,但新的AOF文件不会包含浪费空间的冗余命令,所以新的AOF文件要比旧的AOF文件体积要小。
实现:
127.0.0.1:6379> RPUSH list 'a' 'b'
(integer) 2
127.0.0.1:6379> RPUSH list 'c'
(integer) 3
127.0.0.1:6379> RPUSH list 'd' 'e'
(integer) 5
127.0.0.1:6379> LPOP list
"a"
127.0.0.1:6379> LPOP list
"b"
127.0.0.1:6379> RPUSH 'f' 'g'
(integer) 1
127.0.0.1:6379>
redis为了保存当前list的键,就必须要保存六条命令到AOF文件中。为了减少AOF的大小,最简单的办法是直接读取数据库中list的值,直接用rpush list ‘c’ ‘d’ ‘e’ ‘f’ 'g’命令来保存AOF中的这六条命令。即可将六条命令缩减为一条。
- 后台重写:
写AOF文件是后台子进程进行操作的这样的好处有:
- 子进程在进行aof重写的时候,服务器进程能继续处理命令请求。
- 子进程带有服务器进程的数据副本,使用子进程而不是线程,能够避免使用锁的情况下,保证数据的安全性。
缺点:
- 使用子进程的方式,可能会出现AOF中保存的数据与数据库状态不一致。
为了解决数据不一致的问题,redis设置了一个AOF重写缓冲区,这个缓冲区在服务器启用子进程后开始使用,当redis执行完一个写命令后,它会同时将这个写命令发送到AOF缓冲区和AOF重写缓冲区。
也就是说在AOF重写的时候,服务器会这样做:
- 执行客户端发来的命令
- 将执行的命令追加到AOF缓冲区
- 将执行后的写命令追加到AOF重写缓冲区
优点:
- 将AOF重写缓冲区的内容写入到新的AOF文件中,能够保证新的AOF文件所保存的数据库状态和当前数据库状态是一致的。
- 对新的AOF文件进行改名,原子的覆盖现有AOF文件,完成新旧两个AOF文件的交替。