Skip to content

AOF 持久化是怎么实现的?

AOF 日志

Append Only File持久化功能,只记录写操作命令,不记录读操作

默认不开启,需要修改 redis.conf 配置文件

AOF 日志文件其实就是普通的文本,我们可以通过 cat 命令查看里面的内容,不过里面的内容如果不知道一定的规则的话,可能会看不懂。

Redis 是先执行写操作命令后,才将该命令记录到 AOF 日志里的,这么做其实有两个好处。

  • 1、避免额外检查开销:不会记录错误命令(错误的话redis执行失败,不会记录)
  • 2、不会阻塞当前写操作:执行成功后才会记录 两个风险:
  • 1、执行写操作命令和记录日志是两个过程,中间发生宕机,数据有丢失风险。
  • 2、对写一个命令可能有阻塞风险。(都是主进程执行)

写回策略 Redis 执行完写操作命令后,会将命令追加到 server.aof_buf 缓冲区,之后通过 write() 写入内核缓冲区,提供了三种写会磁盘的策略

  • Always,每次写操作后将日志数据落盘;
    • 数据最可靠,性能最差
  • Everysec,每隔一秒落盘;
    • 比较均衡,最多丢掉一秒的数据
  • No,由操作系统控制写回的时机。
    • 数据可靠性不确定,性能相对最好

重写机制 随着写操作越来越多,文件会不断变大,后续如果通过该文件恢复数据,会很慢。 当文件大小超过阈值,会执行重写压缩 AOF 文件 重新会读取当前库中所有数据,以写入的模式记录到新的 AOF 文件,全部记录完替换旧的 AOF 文件(同一个数据多次修改只记录一次,删除的数据不用记录),记录完替换是为了避免新的 AOF 重写失败,污染数据。 Redis使用子进程进行重写,避免阻塞主进程,同时避免多线程共享内容带来的竞争,父子进程会用写时复制避免,但是复制还是主进程操作,大key容易出现阻塞问题。在重写过程中,会增加一个 AOF 重写缓冲区 记录重写过程中的新的写命令,过程中写操作主进程需要

  • 执行客户端发来的命令;
  • 将执行后的写命令追加到「AOF 缓冲区」;
  • 将执行后的写命令追加到「AOF 重写缓冲区」; 重写结束后会将 AOF 重写缓冲区 数据追加到新的 AOF 文件中,然后改名并使用新的 AOF 文件(会阻塞主进程正常执行,在这个过程中也就不会出现新的写操作了) Redis启动时会对 AOF 文件进行校验和检查,如果文件损坏,会拒绝启动,并提供错误信息。

RDS 快照

  • save 由主进程写入,会阻塞主线程
  • bgsave 由子进程创建(默认),这个期间主线程修改了数据,无法写入到正在写入的 RDS 快照中
  • Redis自动加载,无手动加载命令
  • 是全量快照,可以配置每隔一段时间质性
  • 因为是二进制数据(AOF是操作命令),所以恢复更快
    • 但是因为每次写入全量,所以创建很慢
    • 如果快照创建后立刻宕机,会丢失创建快照后写入的说有数据
  • 因此提出混合持久化
    • 当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。

也就是说,使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据

这样的好处在于,重启 Redis 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快

加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失

并没有显式的边界标识来直接区分RDB和AOF的边界。但是,由于RDB和AOF的格式和内容本质上是不同的(RDB是二进制格式,AOF是文本格式),因此在解析混合持久化文件时,Redis能够根据文件格式和内容的不同来自动区分RDB和AOF部分。

大 Key 问题

  • 单线程写入或者删除大 Key 会比较慢,会阻塞(删除可以使用unlink异步删除避免阻塞)
  • 获取大 Key 造成高网络负载
  • AOF 日志写入很多大 Key,文件大小会很大,会频繁触发重写
  • 如果配置了 Always 写回策略,写操作容易阻塞很久
  • AOF 和 RDB 生成过程中,父进程修改了共享数据,会触发写时复制,大 Key 容易阻塞父进程

如果 Redis 只是缓存,可以关闭 AOF 经常监控大 Key,将大 Key 进行拆分


正在精进