第五章 内存快照:宕机后,Redis如何实现快速恢复?

第五章 内存快照:宕机后,Redis如何实现快速恢复?

RDB 是给哪些内存数据做快照 ?

RDB快照是全量快照,会将内存中的所有数据保存到磁盘中

有哪些命令可以生成 RDB 文件 ?

  • save 和 bgsave 可以生成RDB快照。
  • save在主线程中执行,所以会产生阻塞。
  • bgsave 会创建一个子进程专门写入RDB文件,所以不会产生阻塞,默认是使用 bgsave 这种方式。
  • 通过 bgsave 命令来执行全量快照,这既提供了数据的可靠性保证,也避免了对 Redis 的性能影响。

RDB 快照时 Redis里面的数据还能被修改吗 ?

  • Redis 生成 RDB 快照时会 fork 子进程。
  • Redis 借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。
  • 如果主线程对这些数据也都是读操作(例如图中的键值对 A),那么,主线程和 bgsave 子进程相互不影响。
  • 如果主线程要修改一块数据(例如图中的键值对 C),那么,这块数据就会被复制一份,生成该数据的副本(键值对 C’)。然后,主线程在这个数据副本上进行修改。同时,bgsave 子进程可以继续把原来的数据(键值对 C)写入 RDB 文件。
    • “这块数据就会被复制一份,生成该数据的副本”,这个操作在实际执行过程中,是子进程复制了主线程的页表。
    • 所以通过页表映射,能读取到主线程的原始数据,而当有新数据写入或数据修改时,主线程会把新数据或修改后的数据写到一个新的物理内存地址上,并修改主线程自己的页表映射。
    • 所以,子进程读到的类似于原始数据的一个副本,而主线程也可以正常进行修改。
  • 这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。

a2e5a3571e200cb771ed8a1cd14d5558.jpeg

注意

  • 主线程有bgsave子进程时,写数据时会开辟新的空间存储数据,并且会修改自己的虚拟表
  • 如果主线程没有bgsave子进程时,就是直接修改对应的空间数据

Redis 可以每秒做一次快照吗?

  • 如下图所示,我们先在 T0 时刻做了一次快照,
  • 然后又在 T0+t 时刻做了一次快照,在这期间,数据块 5 和 9 被修改了。
  • 如果在 t 这段时间内,机器宕机了,那么,只能按照 T0 时刻的快照进行恢复。
  • 此时,数据块 5 和 9 的修改值因为没有快照记录,就无法恢复了。

Untitled

如果频繁地执行全量快照,会带来什么影响 ?

  • 一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环。
  • 另一方面,bgsave 子进程需要通过 fork 操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程;但是,fork 这个创建过程本身会阻塞主线程,而且主线程的内存越大,阻塞时间越长。如果频繁 fork 出 bgsave 子进程,这就会频繁阻塞主线程了(所以,在 Redis 中如果有一个 bgsave 在运行,就不会再启动第二个 bgsave 子进程)。

最好的办法:Redis 4.0 提出的混合使用 AOF 日志和内存快照

  • 手动设置参数: aof-use-rdb-preamble yes
  • 简单来说,内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。
  • 前一章提到AOF日志记录所有操作记录,但有了RDB快照能力后,AOF就不用记录所有操作了,只需要记录增量记录即可,记录量就小了。
  • 若要恢复数据,可用RDB文件再加上AOF日志就可以全量恢复数据了。
  • 在速度上,因为RDB是二进制数据流,可以快速恢复出redis数据,然后在此基础上小量地执行AOF操作命令,相比于只用AOF来恢复全量数据的操作,也不会太多影响到恢复速度。

如下图所示,T1 和 T2 时刻的修改,用 AOF 日志记录,等到第二次做全量快照时,就可以清空 AOF 日志,因为此时的修改都已经记录到快照中了,恢复时就不再用日志了。

Untitled

RDB,AOF,RDB+AOF 三种方式的选择

  • 数据不能丢失时,内存快照和 AOF 的混合使用是一个很好的选择;
  • 如果允许分钟级别的数据丢失,可以只使用 RDB;
  • 如果只用 AOF,优先使用 everysec 的配置选项,因为它在可靠性和性能之间取了一个平衡。
    • 每秒写回磁盘

场景设计

我们使用一个 2 核 CPU、4GB 内存、500GB 磁盘的云主机运行 Redis,Redis 数据库的数据量大小差不多是 2GB,我们使用了 RDB 做持久化保证。当时 Redis 的运行负载以修改操作为主,写读比例差不多在 8:2 左右,也就是说,如果有 100 个请求,80 个请求执行的是修改操作。你觉得,在这个场景下,用 RDB 做持久化有什么风险吗?

2核CPU、4GB内存、500G磁盘,Redis实例占用2GB,写读比例为8:2,此时做RDB持久化,产生的风险主要在于 CPU资源 和 内存资源 这2方面:

  • a、内存资源风险:Redis fork子进程做RDB持久化,由于写的比例为80%,那么在持久化过程中,“写实复制”会重新分配整个实例80%的内存副本,大约需要重新分配1.6GB内存空间,这样整个系统的内存使用接近饱和,如果此时父进程又有大量新key写入,很快机器内存就会被吃光,如果机器开启了Swap机制,那么Redis会有一部分数据被换到磁盘上,当Redis访问这部分在磁盘上的数据时,性能会急剧下降,已经达不到高性能的标准(可以理解为武功被废)。如果机器没有开启Swap,会直接触发OOM,父子进程会面临被系统kill掉的风险。
  • b、CPU资源风险:虽然子进程在做RDB持久化,但生成RDB快照过程会消耗大量的CPU资源,虽然Redis处理请求是单线程的,但Redis Server还有其他线程在后台工作,例如AOF每秒刷盘、异步关闭文件描述符这些操作。由于机器只有2核CPU,这也就意味着父进程占用了超过一半的CPU资源,此时子进程做RDB持久化,可能会产生CPU竞争,导致的结果就是父进程处理请求延迟增大,子进程生成RDB快照的时间也会变长,整个Redis Server性能下降。
  • c、另外,可以再延伸一下,老师的问题没有提到Redis进程是否绑定了CPU,如果绑定了CPU,那么子进程会继承父进程的CPU亲和性属性,子进程必然会与父进程争夺同一个CPU资源,整个Redis Server的性能必然会受到影响!所以如果Redis需要开启定时RDB和AOF重写,进程一定不要绑定CPU。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿小羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值