Redis持久化RDB和AOF

前言

Redis的高速建立在数据存储在内存中, 但是断电的话, 就会导致数据丢失的问题.
为此我们需要对数据进行持久化到硬盘中, 用于数据恢复.
Redis提供了两种持久化存储方案, Redis在启动时会优先加载AOF文件恢复数据.

  1. AOF(Append Only File): 记录每次执行的命令到日志中, 恢复数据时重新执行一次日志文件中的命令.
  2. RDB(Redis Database Backup): 将数据库的所有数据直接写入磁盘

RDB ( Redis Database Backup )

RDB会将当前Redis中所有存储的数据持久化到dump.rdb文件中.

save 持久化 ( 阻塞 )

Redis是单线程的, 所以save命令一旦执行, 其时间复杂度是O(n), 数据量一大, Redis就会阻塞后面的请求.
所以一般不直接使用save命令进行持久化.

bgsave 持久化 ( fork子进程 )

Redis提供了bgsave命令, fork一个子进程来进行save. 这样就不会阻塞住原本的进程.
fork后的子进程执行save命令, 会创建一个临时RDB文件, 待持久化完毕后, 覆盖之前的RDB文件.
但是fork这个操作, 仍然是阻塞的,

save seconds changes ( 定时持久化 )

Redis的配置文件中, 还提供了另一种RDB持久化方式, 格式: save <seconds> <changes>

1
2
3
4
# save "" # 关闭 RDB
save 900 1
save 300 10
save 60 10000

表示seconds秒内改变了changes次数据, 则自动bgsave.
缺点也很明显, 无法控制生成RDB的频率

最佳配置 ( 开启 RDB 时 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 开启默认的 save 策略, save 60 10000 视数据量而定
save 900 1
save 300 10
save 60 10000

# 2. 指定 RDB 文件名
dbfilename dump-6379.rdb
# 3. 指定存储文件夹, 放RDB文件、AOF文件、log文件
dir /data
# 4. 指定 bgsave 发生错误时停止写入
stop-writes-on-bgsave-error yes
# 5. 压缩 RDB 文件
rdbcompression yes
# 6. 校验 RDB 文件
rdbchecksum yes

值得注意的是, 即使在配置文件中关闭RDB自动持久化, 在以下情况, 仍会产生RDB文件.

  1. 主从复制之全量复制时, 会生成RDB文件
  2. debug reload重启Redis, 会生成RDB文件
  3. redis-cli SHUTDOWN保存退出时, 如果没有开启AOF, 则自动执行bgsave

AOF(Append Only File)

Redis开启AOF持久化的时候, 每次写操作都会追加到日志文件中. 这个顺序写操作是很快的.
并且, AOF记录是直接写在操作系统层面的cache内存上的, 不是直接写入磁盘的. 基于内存的操作也是很快的.

1
2
3
4
5
6
7
# 1. 打开 AOF 持久化
appendonly no

# 2. 三种 fsync 方式
# appendfsync always # 每次执行命令都会 fsync, 性能超级差
appendfsync everysec # 每秒执行 fsync
# appendfsync no # 取决于操作系统执行 fsync (不可控)

这里有三种配置方式, 一般生产环境都用everysec.
因为你既然用了Redis, 那肯定是为了应对超高并发的读写操作. 如果你用了always, 那相当于每次都写入磁盘, 就失去了读写内存带来的高性能优势了.

当然AOF日志文件不可能无限增长, 下面介绍下如何压缩重写AOF文件.

手动重写

假设要执行以下命令

1
2
3
set a a
set a b
set a c

那么AOF肯定不会傻傻的将这3条命令写到AOF文件中, 因为只要保证set a c即可.
忽略中间态, 这就是AOF重写.
可以极大的减少AOF文件大小, 加快AOF恢复速度.

要手动重写, 只需要执行bgrewriteaof命令即可.

  1. 首先, Redisfork一个子进程.
  2. 基于当前内存中的数据, 而不是之前的AOF文件, 来生成一个新的AOF文件.
  3. 此时Redis仍在接收写请求, 会往内存、旧AOF文件、新AOF文件, 三个地方写入日志.
  4. AOF文件生成完毕, 则会将内存中的AOF日志追加写入新AOF文件.
  5. 用新AOF文件覆盖旧AOF文件, 保证只有一个AOF文件.

这里为什么要基于当前内存中的数据, 而不是之前的AOF文件, 来生成一个新的AOF文件呢?
因为这个AOF很容易出现破损的情况, 比如断电, 磁盘满了.
然后新AOF文件是根据破损的旧AOF文件生成的, 又是破损的AOF文件.
所以干脆基于内存已有数据生成新AOF文件.

然后Redis还提供了一个命令修复破损的AOF记录, redis-check-aof --fix my.aof

自动重写

Redis2.4之后提供了自动重写AOF的功能, 就不用再去手动执行bgrewriteaof了.
配置文件/etc/redis.conf中提供了满足一定条件就自动重写的配置.

1
2
3
4
# 1. 当 AOF 文件大于某个值时进行重写
auto-aof-rewrite-min-size 64mb
# 2. AOF 文件增长率, 下次就是到达 128mb、 256mb 就会重写.
auto-aof-rewrite-percentage 100

我们可以在redis-cli客户端查看info信息.

1
2
3
4
info persistence
# AOF开启, 才会追加以下信息
# aof_current_size AOF 文件当前大小
# aof_base_size 上次AOF文件重写时的大小

当同时满足以下条件时, AOF文件会自动重写.

1
2
aof_current_size > auto_aof_rewrite_min_size
aof_current_size - aof_base_size / aof_base_size > auto_aof_rewrite_percentage

最佳配置 ( 开启 AOF 时 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 开启 AOF
appendonly yes
# 2. 指定 AOF 文件名
appendfilename appendonly-6379.aof
# 3. 指定存储文件夹, 放RDB文件、AOF文件、log文件
dir /data
# 4. AOF 每秒保存一次, 宕机最多丢失一秒数据
appendfsync everysec
# 5. AOF 重写时是否正常执行 AOF
no-appendfsync-on-rewrite yes
# 6. 当 AOF 文件大于 64mb 时进行重写, 视数据量而定
auto-aof-rewrite-min-size 64mb
# 7. AOF 文件增长率, 下次就是到达 128mb、 256mb 就会重写
auto-aof-rewrite-percentage 100

当 RDB 遇到 AOF

  1. RDB快照生成和AOF重写操作是互斥的, 同一时间只能有一个执行.
  2. 当同时有RDBAOF文件的时候, 优先使用AOF文件进行数据恢复.

数据备份和恢复

不管是RDB还是AOF, 每次生成都会覆盖掉原有的旧文件.
那么我们就需要定时的把旧的备份文件移动到别的目录下, 避免被覆盖掉.

  1. 每小时复制一份RDB到备份目录下, 只保留48小时的数据
  2. 每天复制一份RDB到备份目录下, 只保留1个月的数据
  3. 每晚将服务器上所有RDB发送到远程的云存储上.

既然有备份, 就会要恢复. 恢复AOF数据只要将AOF文件复制到Redis数据目录下, 然后重启Redis即可.
恢复RDB数据的方案就有点复杂了.

  1. 停止Redis, 修改配置文件appendonly no.
  2. 复制RDB备份到Redis数据目录下, 重启Redis, 确认数据恢复.
  3. 在命令行热修改配置config set appendonly yes, 开启AOF.
  4. 停止Redis, 修改配置文件appendonly yes, 重启Redis

Redis开启AOF的时候, 是优先使用AOF恢复数据的. 即使AOF文件被删除, 也会基于重启后的数据生成一份新的AOF文件, 然后覆盖掉你复制进来的有数据的RDB文件, 变成一个空的RDB文件.
所以我们才需要提前先手动把AOF关闭. 上面的恢复步骤这么复杂, 就是为了规避这个case带来的问题.

参考资料