Redis-AOF重写,AOF后台重写实现原理
why
AOF(append only file)对Redis进行持久化是通过保存被执行的写命令来记录数据库状态的,随着服务器运行,AOF文件内容越来越多,载入AOF文件的时间会越来越长,影响Redis服务。
所以有必要对’冗余‘的AOF文件进行优化,即AOF文件重写。
那为什么要进行AOF后台重写?因为Redis单线程特性,AOF重写操作会引入大量写操作,引起stop the world,所以fork出子进程执行AOF后台重写,这样父进程可以继续处理命令请求。
AOF重写原理
怎么重写?是对旧的AOF文件进行读取、分析后进行优化吗?Redis没有采用,Redis是通过读取服务器当前数据库状态实现该功能的。
举个例子:
对一个animals键执行以下命令:
SADD animals "Cat"
SADD animals "Dog" "Tiger"
SREM animals "Tiger"
为了保存上述状态,AOF需要进行3次命令保存。
AOF重写后只需要读取animals键的所有值,再用SADD animals “Cat” "Dog"一条命令即可。
从3条减少为1条,减少了文件体积。
AOF后台重写原理(BGREWRITEAOF命令)
使用子进程(而不是开启一个线程)进行AOF重写虽然可以避免使用锁的情况下,保证数据安全性,但是会带来子进程和父进程一致性问题。
例如在开始重写之后父进程又接收了新的键值对此时子进程是无法知晓的,当子进程重写完成后的数据库和父进程的数据库状态是不一致的。
见下表:
时间 | 服务器进程(父进程) | 子进程 |
---|---|---|
T1 | 执行命令 SET K1 V1 | |
T2 | 执行命令 SET K1 V1 | |
T3 | 执行命令 SET K1 V1 | |
T4 | 创建子进程,执行AOF文件重写 | 开始AOF重写 |
T5 | 执行命令 SET K2 V2 | 执行重写 |
T6 | 执行命令 SET K3 V3 | 执行重写 |
T7 | 执行命令 SET K4 V4 | 完成AOF重写 |
在T7时刻服务器进程有了4个键,而子进程只有1个键,即所谓的漏追加。
为了解决这种不一致性,redis设置了一个AOF重写缓冲区。
在子进程执行AOF重写期间。服务器进程需要执行以下3个动作:
- 执行客户端命令
- 执行后追加到AOF缓冲区
- 执行后追加到AOF重写缓冲区
执行完上述3个操作后可以保证:
- AOF缓冲区内容会定期被写入和同步到AOF文件,对现有AOF文件处理正常进行
- 子进程开始,服务器执行的所有写命令都会进入AOF重写缓冲区
子进程完成AOF重写后,它向父进程发送一个信号,父进程收到信号后会调用一个信号处理函数,该函数把AOF重写缓冲区的命令追加到新AOF文件中然后替换掉现有AOF文件。父进程处理完毕后可以继续接受客户端命令调用,可以看出在AOF后台重写过程中只有这个信号处理函数会阻塞服务器进程。
下表是完整的AOF后台重写过程:
时间 | 服务器进程(父进程) | 子进程 |
---|---|---|
T1 | 执行命令 SET K1 V1 | |
T2 | 执行命令 SET K1 V1 | |
T3 | 执行命令 SET K1 V1 | |
T4 | 创建子进程,执行AOF文件重写 | 开始AOF重写 |
T5 | 执行命令 SET K2 V2 | 执行重写 |
T6 | 执行命令 SET K3 V3 | 执行重写 |
T7 | 执行命令 SET K4 V4 | 完成AOF重写,向父进程发送信号 |
T8 | 接收到信号,将T5 T6 T7 服务器的写命令追加到新的AOF文件末尾 | |
T9 | 用新的AOF替换旧的AOF |
T8 T9执行的任务会阻塞服务器处理命令。