Redis 持久化 - RDB、AOF 及 混合持久化机制

2021/3/28 posted in  redis

RDB

RDB 快照持久化,将内存数据库所有数据 保存在名字为 dump.rdb 二进制文件中。
目录在 配置文件中 dir 指定的目录

可以对 Redis 进行设置,在 N 秒内数据集至少有 M 个改动,这一条件被满足是,自动保存一次数据集

比如:
save 60 1000
60秒内至少有1000个键被改动,这一个条件满足时,自动保存一次数据集

可以设置多个 save 条件。
只要满足其中一个条件 bgsave 命令就会被执行。

dirty 计数器,记录了上一次执行 save 或者 bgsave 命令成功后,对数据库状态进行了多少次修改。
lastsave 上次执行 save 或 bgsave 命令的时间戳。

通过执行 save 或 bgsave 命令创建一个新的 RDB 文件,已过期的键不会被保存到新建的 RDB 文件中。

save 是同步操作,有可能会阻塞正常请求命令。
bgsave 是异步操作,需要 fork 子线程,消耗内存。

bgsave 的写时复制机制 COW

Redis 借助操作系统提供的写时复制技术 Copy-On-Write,在生成快照的同时,依然可以正常处理写命令。
bgsave 子进程是由主线程 fork 生成的,可以共享主线程的所有内存数据。bgsave 子进程运行后,开始读取主线程中的内存数据,并写入rdb文件。此时,如果主线程有修改操作,被修改的数据就会被复制一份,生成副本,然后 bgsave 子进程会把这个副本数据写入 rdb 文件,在这个过程中,主线程仍然可以直接修改原来的数据。

RDB 存在的问题:

每次都是全量持久化,宕机会丢失数据。

AOF

AOF:append-only file
将修改的每一条指令记录进文件 appendonly.aof 中,先写入 aof_buf 缓存区,每隔一段时间 fsync 到磁盘。
配置开关 appendonly yes/no
文件中存的是操作的命令。
文件恢复的时候逐条执行文件中的操作命令。

有三个配置:

  • appendfsync always:
    将 aof_buf 缓冲区中的所有内容写入并同步到 AOF 文件
    每条命令都追加到 AOF 文件时就执行一次 fsync ,非常慢,也非常安全

  • appendfsync everysec:
    将 aof_buf 缓冲区中的所有内容写入到 AOF 文件中,如果上次同步 AOF 文件的时间距离现在超过 1 秒钟,那么再次对 AOF 文件进行同步,并且这个同步操作是由一个线程专门负责执行的。
    每秒 fsync 一次,足够快,并且在故障时只会丢失1秒钟的数据

  • appendfsync no:
    将 aof_buf 缓冲区中的所有内容写入到 AOF 文件中,但并不对 AOF 文件进行同步,何时同步由操作系统来决定。

默认选项是 everysec 可以兼顾速度和安全。

不修改数据的命令是不会追加到 aof 文件的。

AOF 重写

因为 AOF 持久化是通过保存被执行过的命令来记录数据库状态的,所以随着时间的推移,AOF 文件中的内容会越来越多,文件体积也会越来越大,并且恢复起来所需时间就越多。针对这个问题,Redis 有 AOF 重写机制。

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

例如:
INCR 命令执行多次,AOF 可以使用 set 命令来记录最终结果。

配置策略
auto-aof-rewrite-percentage 100 // aof文件自上一次重写后文件大小增长了100%则再次触发重写,也就是64M
auto-aof-rewrite-min-size 64mb // aof文件至少要达到 64M 才会自动重写,文件太小恢复速度本来就很快,重写的意义不大

bgrewriteaof 重写命令

Redis 服务器设置了一个 AOF 重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用,当 Redis 服务器执行完一个写命令之后,它会同时将这个命令发送给 AOF 缓冲区和 AOF 重写缓冲区,也就是说,在子进程执行 AOF 重写期间,服务器进程需要执行三个动作:

  1. 执行客户端发送来的命令
  2. 将执行后的写命令追加到 AOF 缓冲区
  3. 将执行后的写命令追加到 AOF 重写缓冲区

可以保证 AOF 缓冲区的内容会定期被写入和同步到 AOF 文件,从创建子进程开始,所有的写命令都会被记录到 AOF 重写缓冲区里面。

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

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

混合持久化

两种持久化方式都开启的话,会生成两个文件,重启是会选择 AOF 方式来恢复,因为 AOF 恢复的数据更多。但是 AOF 恢复数据慢。
Redis4.0 之后,有一个新的持久化选项 -- 混合持久化

开启混合持久化:aof-use-rdb-preamble yes

如果开启了混合持久化, AOF 在重写时,不在是单纯将内存数据转换为 RESP 命令写入 AOF 文件,而是将重写这一刻之前的内存做 RDB 快照处理,并且将 RDB 快照内容和增量的 AOF 修改内存数据的命令存在一起,都写入新的 AOF 文件,新的文件一开始不叫 appendonly.aof,等到重写完新的 AOF 文件才会进行改名,覆盖原有的 AOF 文件,完成新旧两个 AOF 文件的替换。

于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后在重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,因此重启效率大幅得到提升。