redis持久化(RDB、AOF、混合持久化)
- 1. RDB快照(snapshot)
在默认情况下, Redis 将内存数据库快照保存在名字为 dump.rdb
的二进制文件中。
你可以对 Redis 进行设置, 让它在“N 秒内数据集至少有 M 个改动”这一条件被满足时,
自动保存一次数据集。
比如说, 以下设置会让 Redis 在满足“60 秒内有至少有 1000 个键被改动”这一条件时,
自动保存一次数据集:
save 60 1000
优点:
RDB 是一个非常紧凑(compact)的文件,体积小,因此在传输速度上比较快,因此适合灾难恢复。
RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快得多。
缺点:
RDB是一个快照过程,无法完整的保存所以数据,尤其在数据量比较大时候,一旦出现故障丢失的数据将更多。
RDB文件是特定的格式,阅读性差,由于格式固定,可能存在不兼容情况。
- 2. AOF(append-only file)
快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化,将修改的每一条指令记录进文件
你可以通过修改配置文件来打开 AOF 功能:
appendonly yes
从现在开始, 每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文件的末尾。
这样的话, 当 Redis 重新启动时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。
你可以配置 Redis 多久才将数据 fsync 到磁盘一次。
有三个选项:
- appendfsync always。每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全。
- appendfsync everysec。每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。
- appendfsync no。从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择。
推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。
优点:
数据更完整,秒级数据丢失(取决于设置fsync策略)。、
兼容性较高,由于是基于redis通讯协议而形成的命令追加方式,无论何种版本的redis都兼容。
aof文件是明文的,可阅读性较好。
缺点:
数据文件体积较大,即使有重写机制,但是在相同的数据集情况下,AOF文件通常比RDB文件大。
相对RDB方式,AOF速度慢于RDB,并且在数据量大时候,恢复速度AOF速度也是慢于RDB。
由于频繁地将命令同步到文件中,AOF持久化对性能的影响相对RDB较大。
- AOF重写
aof 文件记录的是每一条redis命令,有时候我们会对某一个key进行多次set,中间会产生很多条命令,但是结果只有一个。
set name 1
...
set name 12
set name 123
...
set name 1234
set name 12345
例如我们执行了上述命令,aof 文件会记录着每一条命令。在redis重启时,会逐条执行上述的命令。但是其实可以精简为set name 12345
,其余的几条命令其实没有意义。AOF重写就是实现了这个功能。
相关配置:
auto-aof-rewrite-percentage 100 # 指当前aof文件比上次重写的增长比例大小,达到这个大小就进行 aof 重写
auto-aof-rewrite-min-size 64mb # 最开始aof文件必须要达到这个文件时才触发,后面的每次重写就不会根据这个变量了
以上配置的意思是:
在 aof 文件小于64mb的时候不进行重写,当到达64mb的时候,就重写一次。重写后的 aof 文件可能是10mb。上面配置了auto-aof-rewrite-percentage 100
,即 aof 文件到了20mb的时候,又开始重写一次。以此类推。
AOF 是在后台自动重写(redis会fork一个子进程来进行备份,保证主进程不会阻塞),也可以人为执行命令 bgrewriteaof
重写 AOF。
- 使用子进程来进行AOF重写时会遇到的问题
问题:
子进程在进行AOF重写期间,服务器进程还要继续处理命令请求,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的数据不一致。
解决方案:
要知道redis是怎么处理这个问题的,需要先了解下AOF重写的具体实现:
AOF文件重写过程与RDB快照bgsave工作过程有点相似,都是通过fork子进程,由子进程完成相应的操作,同样的在fork子进程简短的时间内,redis是阻塞的。
(1)开始bgrewriteaof
,判断当前有没有bgsave命令(RDB持久化)/bgrewriteaof
在执行,倘若有,则这些命令执行完成以后在执行。
(2)主进程fork
出子进程,在这一个短暂的时间内,redis是阻塞的。
(3)主进程fork
完子进程继续接受客户端请求。此时,客户端的写请求不仅仅写入aof_buf
缓冲区,还写入aof_rewrite_buf
重写缓冲区。一方面是写入aof_buf
缓冲区并根据appendfsync策略同步到磁盘,保证原有AOF文件完整和正确。另一方面写入aof_rewrite_buf
重写缓冲区,保存fork之后的客户端的写请求,防止新AOF文件生成期间丢失这部分数据。
(4.1)子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息。
(4.2)主进程把aof_rewrite_buf
中的数据写入到新的AOF文件。
(5.)使用新的AOF文件覆盖旧的AOF文件,标志AOF重写完成。
- Redis 4.0 混合持久化
重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。
如果使用 AOF 日志重放,性能则相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动的时候需要花费很长的时间。
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。
混合持久化同样也是通过bgrewriteaof
完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将aof_rewrite_buf
重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据,如下图:
在redis重启的时候,加载 aof 文件进行恢复数据:先加载 rdb 内容再加载剩余的 aof。
混合持久化配置:
aof-use-rdb-preamble yes # yes:开启,no:关闭
- RDB和AOF,应该用哪一个?
如果你可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。如果只用AOF持久化,数据量很大时,在redis启动的时候需要花费大量的时间。
如果你非常关心你的数据,建议使用 redis 4.0 以后的混合持久化特性。