你未必出类拔萃,但一定与众不同
Redis AOF持久化
AOF持久化通过保存Redis服务器所执行的写命令来记录数据库状态的
RDB保存的是键值对,而AOF保存的则是redis 命令请求协议的格式
AOF持久化的实现
当AOF持久化功能处于打开状态时,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾
如果客户端向服务器发送
set key value
那么服务器在执行这个set命令之后,会将以下协议内容追加到aof_buf缓存区的末尾(纯文本格式)
AOF文件的写入与同步
Redis的服务器进程就是一个事件循环(loop),这个循环中的文件事件负责接收客户端的命令请求,以及向用客户端发送命令回复,而时间事件则负责执行像serverCron函数这样需要定时运行的函数
服务器在处理文件事件时可能会执行写命令,使得一些内容被追加到aof_buf缓冲区里面,所以在服务器每次结束一个事件循环之前,都会调用flushAppendOnlyFile函数,考虑是否需要将aof_buf缓冲池汇的内容写入和保存到aof文件中。
flushAppendOnlyFile函数的行为则由服务器配置的appendfsync选项的值来决定的,不同值产生不同行为
appendfsync选项的值 | flushAppendOnlyFile函数的行为 |
---|---|
always | 将aof_buf缓冲区中的所有内容写入并且同步到AOF文件中 |
everysec | 将aof_buf缓冲区中的所有内容写入到AOF文件中,如果上次同步距离现在超过一秒钟,则再次进行同步 |
no | 将aof_buf缓冲区中的所有内容写入到AOF文件中,但不对AOF文件进行同步 |
appendfsync默认为everysec
AOF文件的载入和数据还原
图解
Redis 读取AOF文件并且还原数据库状态的详细步骤如下
- 创建一个不带网络连接的伪客户端,因为Redis的命令只能在客户端上下文执行,而载入AOF文件的命令来源于AOF文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端执行AOF文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样。
- 从AOF文件中分析并且读取除一条写命令
- 使用伪客户端执行读出的命令
- 一直执行步骤2和3直到AOF文件的所有写命令都被执行完成
完成以后 AOF所保存的数据库状态就会被完整还原出来。
AOF重写
因为AOF持久化是通过保存被执行的写命令来记录数据库状态的,所以命令越来越多那么文件的体积也会越来越大,不加以控制 迟早会对redis服务器 或者宿主计算机造成影响
为了解决AOF文件体积膨胀的问题,Redis提供了AOF文件重写来减小AOF文件的体积,通过创建一个新的AOF文件来代替原来的AOF文件
通过直接从数据库中读取相关键的值 就可以减少重写的指令,那么久可以缩短aof文件的体积
实现思想概括
比如通过了六个set去更新一个键的值,旧的AOF文件就会保存六个指令而新的AOF文件则只需要去数据库读取相应的值,然后将一条sadd指令保存即可。
AOF 的后台重写
因为重写需要进行大量的写入操作,那么调用这个函数的线程就会长时间被阻塞,而redis使用的是单线程来处理命令请求,由服务器直接调用的话,重写过程就无法接受命令请求
因此redis采用子进程来执行aof 的重写
这又引发出了一个问题
如果刚好子进程在执行aof重写过程,但是此时服务器又接收了一些写的指令,岂不是会出现数据不一致的情况?
为了解决这个问题,redis设置了一个aof重写的缓冲区,这个缓冲区在服务器创建子进程之后开始使用,当redis服务器执行完一个写命令之后,它会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区
子进程执行AOF重写过程 服务器进程需要执行三个工作
- 执行客户端发来的命令
- 将执行后的写命令追加到AOF缓冲区
- 将执行后的写命令追加到AOF重写缓冲区
以上就可以保证
- AOF缓冲区的内容会被定期写入和同步到AOF文件中,对现有AOF文件的处理工作会正常执行
- 创建子进程开始,服务器执行的所有写命令都会被记录到AOF重写缓冲区里
而子进程完成重写之后,则会通知父进程,父进程接到信号,就
1.将AOF重写缓冲区的内容写入到新aof文件中
2.对新文件改名,然后替换旧文件
AOF重写过程到此结束!