RDB(Redis DataBase) 和 AOF(Append Only File) 是redis持久化的两种机制。
redis将数据持久化到磁盘流程
- 客户端向服务端发送写操作(数据在客户端的内存中)
- 数据库服务端接收到写请求的数据(数据在服务端内存中)
- 服务端调用
write
系统调用,将数据往磁盘上写(数据在系统内存的缓冲区) - 操作系统将缓冲区数据转移到磁盘控制器(数据在磁盘缓存中)
- 磁盘控制器将数据写到磁盘的物理介质中(数据到磁盘)
对于redis来说,只要完成第三步,就完成了redis数据的可持久化,4-5两步由操作系统来完成。
关于redis的配置都在
redis.conf
文件中,里面也保存了 RDB 和 AOF 两种持久化机制的配置。
RDB 机制
RDB 机制其实是一个在指定时间间隔内将redis中的数据同步到磁盘中保存的操作,也是默认的持久化方式。这种方式回把内存中的数据以快照的形式写入的二进制文件中,默认文件名为dump.rdb
。
简单来说,它会把数据以快照的形式保存到磁盘中。
快照:就是把当前时刻的数据拍成一张照片保存下来。
RDB 提供了三种方式来触发RDB 的快照操作:
1. save触发方式
save
是一个阻塞式命令。
该命令会阻塞当前redis服务器,执行save
命令来 RDB 的过程中,无法处理其它命令,知道 RDB 结束。
执行完成时候如果存在老的RDB文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取。
2. bgsave触发方式
执行该命令时,redis会 fork
出一个子进程去异步进行快照操作,同时主进程还可以响应客户端请求。
不过在 fork
出子进程的阶段,主进程是阻塞的,但 fork
的时间一般很短,基本上redis内部所有的 RDB 操作都采用 bgsave
命令。
3. 自动触发
自动触发是由配置文件来决定的:
①save
用来配置触发redis的 RDB 触发条件。
比如,save m n
,表示m秒内数据集存在n次修改时,触发bgsave
。
默认配置如下:
#表示900 秒内如果至少有 1 个 key 的值变化,则保存
save 900 1
#表示300 秒内如果至少有 10 个 key 的值变化,则保存
save 300 10
#表示60 秒内如果至少有 10000 个 key 的值变化,则保存
save 60 10000
当你不需要 RDB 持久化时,可以注释掉所有 save
行来停用。
② stop-writes-on-bgsave-error
默认值为yes。
当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。
如果Redis重启了,那么又可以重新开始接收数据了。
③ rdbcompression
默认值是yes。
对于存储到磁盘中的快照,可以设置是否进行压缩存储。
④ rdbchecksum
默认值是yes。
在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
⑤ dbfilename
设置快照的文件名,默认是 dump.rdb
⑥ dir
设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。
总结:
save
与bgsave
对比
命令 | save | bgsave |
---|---|---|
IO类型 | 同步 | 异步 |
阻塞? | 是(fork和快照) | 否(fork) |
复杂度 | O(n) | O(n) |
优点 | 不消耗额外内存 | 不阻塞客户端命令 |
缺点 | 阻塞客户端命令 | 需要fork,消耗内存 |
-
RDB 机制存在的问题
由于 RDB 在本质上是一个周期性地去备份所有数据,而且周期还是分钟级别的,然后一个服务器每秒要处理的请求是相当多的,在进行快照的时候,所更新的数据无法持久化。
若当在 RDB 时,发生灾难,这些这期间更新的数据将全部丢失。
AOF 机制
AOF 持久化
RDB 全量备份是相当耗时的,所以无法进行高频备份。
于是,此时redis提供了 AOF 机制,
工作原理就是redis会将收到的写命令都通过write
函数追加到文件中,就是记录日志。
当然,它不可能每来执行一条写命令都记录到文件中,每次的磁盘IO都需要消耗性能。
所以,redis会先将需要记录的命令保存在临时缓冲区aof_buf
中,然后择机将这些数据批量写入文件。
同时,将临时缓冲区的数据批量写入文件后,操作系统会先将这些数据保存在文件系统的内核缓冲区。
AOF 会通过appendfsync
参数配置决定什么时候调用fsync
强制同步到磁盘,确保数据不丢失。
总之,AOF流程为:
- redis接收写命令并保存到临时缓冲区
aof_buf
中 - redis择机(调用
beforeSleep
函数前,即redis主时间循环结束前)将aof_buf
中的数据批量写入到磁盘 - 根据
appendfsync
参数触发fsync
同步策略
AOF重写
随着时间推移,AOF 的备份文件越来越大,不仅非常占用硬盘空间,而且难以复制移动、加载分析。
所以,AOF提供了文件重写机制,对备份文件进行压缩。
redis提供了bgrewriteaof
命令,会将内存中的数据以命令的方式保存到临时文件中, 同时fork
一条子进程来将文件重写。
它是怎么进行文件重写呢?
其实很多中间状态是无用的,只需要把最终的数据状态以命令的形式记录下来。
比如:
- RPUSH name_list ‘编程技术宇宙’
- RPUSH name_list ‘帅地玩编程’
- RPUSH name_list ‘后端技术学堂’
知道最终数据状态,可以压缩为一条- RPUSH name_list ‘编程技术宇宙’ ‘帅地玩编程’ ‘后端技术学堂’
如果在文件重写的过程中,又有写命令进来,就会出现实际数据与重写的内容不一致的情况,这种情况也需要解决。
因此,redis在创建重写子进程的那一刻起,会将这时来的写入命令先保存到 AOF重写缓冲区 中,等到子进程重写 AOF 文件结束后,再将缓冲区的命令写入到新的 AOF 文件中。
最后,替换掉原先臃肿的大文件即可。
AOF fsync
的三种触发机制
1. always
同步持久化,每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好。
2. everysec
异步操作,每秒记录 如果一秒内宕机,有数据丢失。redisfsync
的默认策略。
3. no
从不同步,由操作系统来决定。
总结:
命令 | always | everysec | no |
---|---|---|---|
优点 | 不丢失数据 | 每秒一次fsync,丢失一秒的数据 | 不用管 |
缺点 | IO开销大 | 丢一秒数据 | 不可控 |
拓展知识
RDB 机制的优缺点
优势
-
RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。
-
生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
-
RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
劣势
RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑。
当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。
AOF 机制的优缺点
优势
-
AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据。
-
AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。
-
AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
-
AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用
flushall
命令清空了所有数据,只要这个时候后台rewrite
还没有发生,那么就可以立即拷贝 AOF 文件,将最后一条flushall
命令给删了,然后再将该 AOF 文件放回去,就可以通过恢复机制,自动恢复所有数据。
劣势
-
对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大
-
AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的
-
以前AOF发生过bug(阻塞命令
BRPop``BLPop
曾引起过这样的bug),就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来,虽然这种bug几乎不可能出现,但 RDB 是不会出现这种问题的。
RDB 和 AOF 应该选择哪一个?
一般来说,如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式:
因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快,
除此之外, 使用 RDB 还可以避免之前提到的 AOF 程序的 bug 。因为以上提到的种种原因, 未来我们可能会将 AOF 和 RDB 整合成单个持久化模型。 (这是一个长期计划。)
AOF文件出错了怎么办?
服务器可能在程序正在对 AOF 文件进行写入时停机, 如果停机造成了 AOF 文件出错(corrupt), 那么 Redis 在重启时会拒绝载入这个 AOF 文件, 从而确保数据的一致性不会被破坏。
当发生这种情况时, 可以用以下方法来修复出错的 AOF 文件:
为现有的 AOF 文件创建一个备份。
使用 Redis 附带的redis-check-aof
程序,对原来的 AOF 文件进行修复。
$ redis-check-aof --fix
(可选)使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两 个文件之间的不同之处。
重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。
RDB 和 AOF 之间的相互作用
在版本号大于等于 2.4 的 Redis 中, bgsave
执行的过程中, 不可以执行 bgrewriteaof
。 反过来说, 在 bgrewriteaof
执行的过程中, 也不可以执行 bgsave
。
这可以防止两个 Redis 后台进程同时对磁盘进行大量的 I/O 操作。
如果 bgsave
正在执行, 并且用户显示地调用 bgrewriteaof
命令, 那么服务器将向用户回复一个 OK 状态, 并告知用户, BGREWRITEAOF
已经被预定执行:
一旦 bgsave
执行完毕, bgrewriteaof
就会正式开始。
当 Redis 启动时, 如果 RDB 持久化和 AOF 持久化都被打开了, 那么程序会优先使用 AOF 文件来恢复数据集, 因为 AOF 文件所保存的数据通常是最完整的。
备份数据
Redis 对于数据备份是非常友好的, 因为你可以在服务器运行的时候对 RDB 文件进行复制: RDB 文件一旦被创建, 就不会进行任何修改。
当服务器要创建一个新的 RDB 文件时, 它先将文件的内容保存在一个临时文件里面, 当临时文件写入完毕时, 程序才使用 原子地用临时文件替换原来的 RDB 文件。
这也就是说, 无论何时, 复制 RDB 文件都是绝对安全的。