Redis持久化:AOF

AOF持久化是通过保存Redis服务器所执行的写命令(set,sadd,push)来记录数据库状态

被写入AOF文件的所有命令都是以Redis的命令请求协议格式保存的

一、AOF持久化实现

三个步骤:命令追加、文件写入、文件同步

1.1 命令追加

服务器执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的末尾

1.2 AOF文件的写入与同步

Redis的服务器进程就是一个事件循环,在每一个事件循环之前,它都会调用flushAppendOnlyFIle函数,考虑是否要将aof_buf缓冲区中的内容写入和保存(同步)到AOF文件里面。

def eventloop():
    while True:
        #处理文件是写,接受命令以及发送命令回复
        #处理命令请求时,将新的写命令追加到 aof_buf 缓冲区中
        processFileEvents()

        #处理时间事件
        processTimeEvents()
        
        #考虑是否将 aof_buf 中的内容写入和保存到AOF文件里面
        flushAppendOnlyFile()

flushAppendOnlyFile 函数都会被调用, 这个函数执行以下两个工作:

  • WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件(从aof_buf 写入到内存缓冲区)。
  • SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件(从内存缓冲区)保存到磁盘中。

两个步骤都需要根据一定的条件来执行, 而这些条件由 AOF 所使用的三种保存模式来决定。

1.2.1 三种保存模式

  • AOF_FSYNC_NO :不保存。
  • AOF_FSYNC_EVERYSEC :每一秒钟保存一次。
  • AOF_FSYNC_ALWAYS :每执行一个命令保存一次。

不保存

在这种模式下, 每次调用 flushAppendOnlyFile 函数, WRITE 都会被执行, 但 SAVE 会被略过。在这种模式下, SAVE 只会在以下任意一种情况中被执行:

  • Redis 被关闭
  • AOF 功能被关闭
  • 系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行)

这三种情况下的 SAVE 操作都会引起 Redis 主进程阻塞。

每一秒钟保存一次

在这种模式中, SAVE 原则上每隔一秒钟就会执行一次, 因为 SAVE 操作是由后台子线程调用的, 所以它不会引起服务器主进程阻塞

注意, 在上一句的说明里面使用了词语“原则上”, 在实际运行中, 程序在这种模式下对 fsync 或 fdatasync 的调用并不是每秒一次, 它和调用 flushAppendOnlyFile 函数时 Redis 所处的状态有关。

每当 flushAppendOnlyFile 函数被调用时, 可能会出现以下四种情况

  • 子线程正在执行 SAVE ,并且:
    • 1. 这个 SAVE 的执行时间未超过 2 秒,那么程序直接返回,并不执行 WRITE 或新的 SAVE 
    • 2. 这个 SAVE 已经执行超过 2 秒,那么程序执行 WRITE ,但不执行新的 SAVE 。注意,因为这时 WRITE 的写入必须等待子线程先完成(旧的) SAVE ,因此这里 WRITE 会比平时阻塞更长时间。
  • 子线程没有在执行 SAVE ,并且:
    • 3. 上次成功执行 SAVE 距今不超过 1 秒,那么程序执行 WRITE ,但不执行 SAVE 。
    • 4. 上次成功执行 SAVE 距今已经超过 1 秒,那么程序执行 WRITE 和 SAVE 。

每执行一个命令保存一次

在这种模式下,每次执行完一个命令之后, WRITE 和 SAVE 都会被执行。

另外,因为 SAVE 是由 Redis 主进程执行的,所以在 SAVE 执行期间,主进程会被阻塞,不能接受命令请求。

1.2.2 保存模式对性能的影响

1.3 AOF 文件的读取和数据还原

Redis 读取 AOF 文件并还原数据库的详细步骤如下:

  • 创建一个不带网络连接的伪客户端(fake client)。
  • 读取 AOF 所保存的文本,并根据内容还原出命令、命令的参数以及命令的个数。
  • 根据命令、命令的参数和命令的个数,使用伪客户端执行该命令。
  • 执行 2 和 3 ,直到 AOF 文件中的所有命令执行完毕。

二、AOF重写

随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis提供AOF重写功能,通过创建一个新的AOF文件代替现有的AOF文件。

AOF重写方式

  • 手动重写:bgrewriteaof
  • 自动重写:配置文件

2.1 AOF文件重写的实现

AOF文件重写并不会对现有的AOF文件进行任何读取、分析或者写入操作,这个功能是通过读取服务器当前的数据库状态来实现的。

AOF重写功能的实现原理:首先从数据库中读取键现在的值,然后有一条命令去记录键值对代替之前记录这个键值对的多条命令。

2.2 AOF后台重写

AOF 重写程序可以很好地完成创建一个新 AOF 文件的任务,然而由于Redis服务器使用单个线程来处理命令请求,在执行这个程序的时候, 调用者线程会被阻塞。所以Redis 决定将 AOF 重写程序放到(后台)子进程里执行, 这样处理的最大好处是:

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

不过, 使用子进程也有一个问题需要解决: 因为子进程在进行 AOF 重写期间, 主进程还需要继续处理命令, 而新的命令可能对现有的数据进行修改, 这会让当前数据库的数据和重写后的 AOF 文件中的数据不一致。

为了解决这个问题, Redis 增加了一个 AOF 重写缓存, 这个缓存在 fork 出子进程之后开始启用, Redis 主进程在接到新的写命令之后, 除了会将这个写命令的协议内容追加到现有的 AOF 文件之外, 还会追加到这个缓存中:

换言之, 当子进程在执行 AOF 重写时, 主进程需要执行以下三个工作:

  1. 处理命令请求。
  2. 将写命令追加到现有的 AOF 缓冲区中。
  3. 将写命令追加到 AOF 重写缓存区中。

这样一来可以保证:

  1. 现有的 AOF 功能会继续执行,AOF缓冲区的内容会定期写入和同步到AOF文件
  2. 所有对数据库进行修改的命令都会被记录到 AOF 重写缓存中。

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

  1. 将 AOF 重写缓存区中的内容全部写入到新 AOF 文件中。
  2. 对新的 AOF 文件进行改名,覆盖原有的 AOF 文件。

当步骤 1 执行完毕之后, 现有 AOF 文件、新 AOF 文件和数据库三者的状态就完全一致了。

当步骤 2 执行完毕之后, 程序就完成了新旧两个 AOF 文件的交替。

这个信号处理函数执行完毕之后, 主进程就可以继续像往常一样接受命令请求了。 在整个 AOF 后台重写过程中, 只有最后的写入缓存和改名操作会造成主进程阻塞, 在其他时候, AOF 后台重写都不会对主进程造成阻塞, 这将 AOF 重写对性能造成的影响降到了最低。

以上就是 AOF 后台重写, 也即是 BGREWRITEAOF 命令的工作原理。

2.3 小结

  • AOF 文件通过保存所有修改数据库的命令来记录数据库的状态。
  • AOF 文件中的所有命令都以 Redis 通讯协议的格式保存。
  • 不同的 AOF 保存模式对数据的安全性、以及 Redis 的性能有很大的影响。
  • AOF 重写的目的是用更小的体积来保存数据库状态,整个重写过程基本上不影响 Redis 主进程处理命令请求。
  • AOF 重写是一个有歧义的名字,实际的重写工作是针对数据库的当前值来进行的,程序既不读写、也不使用原有的 AOF 文件。
  • AOF 可以由用户手动触发,也可以由服务器自动触发。


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值