redis · 21 3 月, 2021 1

redis AOF持久化实现原理

与RDB持久化通过保存数据库中的键值对来记录数据库状态不同, AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。

AOF持久化的实现

AOF持久化功能的实现可以分为命令追加(append),文件写入,文件同步(sync)三个步骤

命令追加

当AOF持久化功能处于打开状态时, 服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾:

 struct redisServer {
   // AOF缓冲区
   sds aof_buf;
 }

AOF文件的写入与同步

redis的服务器进程就是一个时间循环, 这个循环中的文件事件负责接收客户端的命令请求, 以及向客户端发送命令回复, 而时间事件负责执行像serverCron函数这样需要定时运行的函数.

appendfsync选项

对于向aof文件写入内容的行文, 可以通过appendfsync选项进行控制, 不同的值决定了不同的行为.

appendfsync选项的值 flushAppendOnlyFile函数的行为
always aof_buf缓冲区中的所有内容写入并同步到AOF文件
everysec(默认) aof_buf缓冲区中的所有内容写入到AOF文件, 如果上次同步AOF文件的时间距离现在超过一秒, 那么再次对AOF文件进行同步, 并且这个同步操作是由一个线程专门负责执行的
no 将aof_buf缓冲区中的所有内容写入到AOF文件, 但并不对AOF文件进行同步, 何时同步由操作系统来决定

AOF持久化的效率和安全性

  • always: 服务器在每个时间循环都要将aof_buf缓冲区中的所有内容写入到AOF文件, 并且同步AOF文件, 所以always效率是最慢的, 但是安全性是最好的.因为即使出现故障停机, AOF持久化也只会丢失一个事件循环中所产生的命令数据
  • everysec, 服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件, 并且每隔一秒就要在线程中对AOF文件进行一次同步.everysec模式足够快,就算出现故障停机, 数据库也只丢失一秒的命令数据。
  • no: 在每个事件循环都要讲aof_buf缓冲区中的所有内容写入到AOF文件, 但是对于aof文件的同步则有系统确定.这种模式会在系统缓存中积累写入数据, 因此该种模式是三个模式中单词同步时间最长的。

重写

AOF通过记录写入命令记录数据库状态, 随着时间的推移, AOF文件将会越来越大.这是我们可以通过AOF重写功能, 通过该功能, Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件, 新旧两个AOF文件所保存的数据库状态时相同, 但新AOF文件不会包含任何浪费空间的冗余命令。

限制

在实际中, 为了避免在执行命令时造成客户端输入缓冲区溢出, 重写程序在处理列表, 哈希表, 集合, 有序集合这四种可能会带有多个元素的键时, 会先检查键所包含的元素数量, 如果超过了REDIS_AOF_REWRITE_ITEMS_PER_CMD常量值, 那么重写程序将使用多条命令来记录键的值, 而不是单单使用一条命令。

AOF后台重写

redis是一个单线程方式执行redis命令,但是aof_rewrite函数是一个阻塞函数, 在执行aof_rewrite的时候, 将导致redis无法处理客户端的请求, 因此redis将aof重写程序放到子进程里执行, 这样可以达到两个目的:

  • 子进程进行AOF重写期间, 服务器进程可以继续处理命令请求
  • 子进程带有服务器进程的数据副本, 使用子进程而不是线程, 可以避免使用锁的情况下, 保证数据的安全性。

AOF 重写实现方式

  • AOF重写是通过读取数据库中的键值对来实现的, 程序无须对现有AOF文件进行任何读入, 分析或者写入操作.

面临的问题

使用子进程也有一个问题需要解决, 因为子进程在进行AOF重写期间, 服务器进程还需要继续处理命令请求, 而新的命令可能会对现有的数据库状态进行修改, 从而使得服务器当前的数据库状态和重写后的AOF文件所保存的数据库状态不一致。

解决重写后的AOF状态和数据库状态不一致的问题

Redis服务器设置了一个AOF重写缓冲区, 这个缓冲区在服务器创建子进程之后开始用, 当redis服务器执行完一个写命令之后, 它会同时将这个写命令发送到AOF缓冲区和AOF重写缓冲区.

当子进程完成AOF重写工作之后, 它会向父进程发送一个信号, 父进程在接到高信号之后, 会调用一个信号处理函数, 并执行以下工作:

  • 将AOF重写缓冲区中的所有内容写入到新AOF文件中, 这时新AOF文件所保存的数据库状态将和服务器当前的数据库状态一致
  • 对新的AOF文件进行改名, 原子地覆盖现有的AOF文件, 完成新旧两个AOF文件的替换

这个信号处理函数完毕之后, 父进程就可以继续像往常一样接收命令请求了。

NOTE: 在父进程同步AOF重写缓冲区中的内容到AOF文件中的时候, 无法继续处理客户端发来的请求, 这时将会有短暂的阻塞。

这个就是BGREWRITEAOF命令的实现原理