持久化方式
Redis 提供了两种方式,实现数据的持久化到硬盘。
1、【全量】RDB 持久化,是指在指定的时间间隔内将内存中的数据集快照写入磁盘。实际操作过程是,fork 一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
2、【增量】AOF持久化,以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
- RDB 优缺点
① 优点
- 灵活设置备份频率和周期。你可能打算每个小时归档一次最近 24 小时的数据,同时还要每天归档一次最近 30 天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
- 非常适合冷备份,对于灾难恢复而言,RDB 是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。推荐,可以将这种完整的数据文件发送到一些远程的安全存储上去,比如说 Amazon 的 S3 云服务上去,在国内可以是阿里云的 OSS 分布式存储上。
- 性能最大化。对于 Redis 的服务进程而言,在开始持久化时,它唯一需要做的只是 fork 出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行 IO 操作了。也就是说,RDB 对 Redis 对外提供的读写服务,影响非常小,可以让 Redis 保持高性能。
- 恢复更快。相比于 AOF 机制,RDB 的恢复速度更更快,更适合恢复数据,特别是在数据集非常大的情况。
② 缺点
-
如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么 RDB 将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
所以,RDB 实际场景下,需要和 AOF 一起使用。
由于 RDB 是通过 fork 子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是 1 秒钟。
所以,RDB 建议在业务低估,例如在半夜执行。
-
AOF 优缺点
① 优点
-
该机制可以带来更高的数据安全性,即数据持久性。Redis 中提供了 3 种同步策略,即每秒同步、每修改(执行一个命令)同步和不同步。
事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。
而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。
至于不同步,无需多言,我想大家都能正确的理解它。由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。
因为以 append-only 模式写入,所以没有任何磁盘寻址的开销,写入性能非常高。
另外,如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在 Redis 下一次启动之前,我们可以通过 redis-check-aof 工具来帮助我们解决数据一致性的问题。
如果 AOF 日志过大,Redis 可以自动启用 rewrite 机制。即使出现后台重写操作,也不会影响客户端的读写。因为在 rewrite log 的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。再创建新日志文件的时候,老的日志文件还是照常写入。当新的 merge 后的日志文件 ready 的时候,再交换新老日志文件即可。 -
注意,AOF rewrite 机制,和 RDB 一样,也需要 fork 出一次子进程,如果 Redis 内存比较大,可能会因为 fork 阻塞下主进程。
-
AOF 包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。
② 缺点
-
对于相同数量的数据集而言,AOF 文件通常要大于 RDB 文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
-
根据同步策略的不同,AOF 在运行效率上往往会慢于 RDB 。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和 RDB 一样高效。
以前 AOF 发生过 bug ,就是通过 AOF 记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。所以说,类似 AOF 这种较为复杂的基于命令日志/merge/回放的方式,比基于 RDB 每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有 bug 。不过 AOF 就是为了避免 rewrite 过程导致的 bug ,因此每次 rewrite 并不是基于旧的指令日志进行 merge 的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。
-
如何选择
不要仅仅使用 RDB,因为那样会导致你丢失很多数据。
也不要仅仅使用 AOF,因为那样有两个问题,第一,你通过 AOF 做冷备,没有 RDB 做冷备,来的恢复速度更快; 第二,RDB 每次简单粗暴生成数据快照,更加健壮,可以避免 AOF 这种复杂的备份和恢复机制的 bug 。
-
Redis 支持同时开启开启两种持久化方式,我们可以综合使用 AOF 和 RDB 两种持久化机制,用 AOF 来保证数据不丢失,作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备,在 AOF 文件都丢失或损坏不可用的时候,还可以使用 RDB 来进行快速的数据恢复。
如果同时使用 RDB 和 AOF 两种持久化机制,那么在 Redis 重启的时候,会使用 AOF 来重新构建数据,因为 AOF 中的数据更加完整。
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化,但并不推荐这种方式:因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用 RDB 还可以避免之前提到的 AOF 程序的 bug。
在 Redis4.0 版本开始,允许你使用 RDB-AOF 混合持久化方式,详细可见 《Redis4.0 之 RDB-AOF 混合持久化》 。也因此,RDB 和 AOF 同时使用,是希望达到安全的持久化的推荐方式。
另外,RDB 和 AOF 涉及的知识点蛮多的,可以看看:
《Redis 设计与实现 —— RDB》
《Redis 设计与实现 —— AOF》 -
如下是老钱对这块的总结,可能更加适合面试的场景:
bgsave 做镜像全量持久化,AOF 做增量持久化。因为 bgsave 会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要 AOF 来配合使用。在 Redis 实例重启时,会使用 bgsave 持久化文件重新构建内存,再使用 AOF 重放近期的操作指令来实现完整恢复重启之前的状态。
和老钱沟通了下,最后一句重启恢复,使用的是 RDB-AOF 的混合方案。
对方追问那如果突然机器掉电会怎样?取决于 AOF 日志 sync 属性的配置,如果不要求性能,在每条写指令时都 sync 一下磁盘,就不会丢失数据。但是在高性能的要求下每次都 sync 是不现实的,一般都使用定时 sync ,比如 1 秒 1 次,这个时候最多就会丢失 1 秒的数据。
实际上,极端情况下,是最多丢失 2 秒的数据。因为 AOF 线程,负责每秒执行一次 fsync 操作,操作完成后,记录最后同步时间。主线程,负责对比上次同步时间,如果超过 2 秒,阻塞等待成功。
对方追问 bgsave 的原理是什么?你给出两个词汇就可以了,fork 和 cow 。fork 是指 Redis 通过创建子进程来进行 bgsave 操作。cow 指的是 copy on write ,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。
这里 bgsave 操作后,会产生 RDB 快照文件。
为什么不建议在主 Redis 节点开启 RDB 功能呢?因为会带来一定时间的阻塞,特别是数据量大的时候。
-
如下来自球友【jian】的回答
【重点】子进程 fork 相关的阻塞:在 bgsave 的时候,Redis 主进程会 fork 一个子进程,利用操作系统的写时复制技术,这个子进程在拷贝父进程的时候理论上是很快的,因为并不需要全拷贝,比如主进程虽然占了 10G 内存,但子进程拷贝他可能只要 200 毫秒,我认为也就阻塞了 200 毫秒(此耗时基本跟主进程占用的内存是成正比的),这个具体的时间可以通过统计项 info stats 里的 last_fork_usec 查看。
CPU 单线程相关的阻塞:Redis 主进程是单线程跑在单核 CPU 上的,如果显示绑定了CPU ,则子进程会与主进程共享一个 CPU ,而子进程进行持久化的时候是非常占CPU(强势 90%),因此这种情况也可能导致提供服务的主进程发生阻塞(因此如果需要持久化功能,不建议绑定CPU)。
内存相关的阻塞:虽然利用写时复制技术可以大大降低进程拷贝的内存消耗,但这也导致了父进程在处理写请求时需要维护修改的内存页,因此这部分内存过大的话(修改页数多或每页占空间大)也会导致父进程的写操作阻塞。(而不巧的是,Linux中TransparentHugePage 会将复制内存页面单位有 4K 变成 2M ,这对于 Redis 来说是比较不友好的,也是建议优化的,具体可百度之)
磁盘相关的阻塞:极端情况下,假设整个机器的内存已经所剩无几,触发了内存交换(SWAP),则整个 Redis的效率将会非常低下(显然这不仅仅针对 save/bgsave ),因此,关注系统的 io 情况,也是定位阻塞问题的一种方法。