Redis核心技术与实战
蒋德钧
中科院计算所副研究员
新⼈⾸单¥29.9
5943 人已学习
课程目录
已更新 9 讲 / 共 50 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 这样学Redis,才能技高一筹
免费
基础篇 (8讲)
01 | 基本架构:一个键值数据库包含什么?
02 | 数据结构:快速的Redis有哪些慢操作?
03 | 高性能IO模型:为什么单线程Redis能那么快?
04 | AOF日志:宕机了,Redis如何避免数据丢失?
05 | 内存快照:宕机后,Redis如何实现快速恢复?
06 | 数据同步:主从库如何实现数据一致?
07 | 哨兵机制:主库挂了,如何不间断服务?
08 | 哨兵集群:哨兵挂了,主从库还能切换吗?
Redis核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

05 | 内存快照:宕机后,Redis如何实现快速恢复?

蒋德钧 2020-08-14
你好,我是蒋德钧。
上节课,我们学习了 Redis 避免数据丢失的 AOF 方法。这个方法的好处,是每次执行只需要记录操作命令,需要持久化的数据量不大。一般而言,只要你采用的不是 always 的持久化策略,就不会对性能造成太大影响。
但是,也正因为记录的是操作命令,而不是实际的数据,所以,用 AOF 方法进行故障恢复的时候,需要逐一把操作日志都执行一遍。如果操作日志非常多,Redis 就会恢复得很缓慢,影响到正常使用。这当然不是理想的结果。那么,还有没有既可以保证可靠性,还能在宕机时实现快速恢复的其他方法呢?
当然有了,这就是我们今天要一起学习的另一种持久化方法:内存快照。所谓内存快照,就是指内存中的数据在某一个时刻的状态记录。这就类似于照片,当你给朋友拍照时,一张照片就能把朋友一瞬间的形象完全记下来。
对 Redis 来说,它实现类似照片记录效果的方式,就是把某一时刻的状态以文件的形式写到磁盘上,也就是快照。这样一来,即使宕机,快照文件也不会丢失,数据的可靠性也就得到了保证。这个快照文件就称为 RDB 文件,其中,RDB 就是 Redis DataBase 的缩写。
和 AOF 相比,RDB 记录的是某一时刻的数据,并不是操作,所以,在做数据恢复时,我们可以直接把 RDB 文件读入内存,很快地完成恢复。听起来好像很不错,但内存快照也并不是最优选项。为什么这么说呢?
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Redis核心技术与实战》,如需阅读全部文章,
请订阅文章所属专栏新⼈⾸单¥29.9
立即订阅
登录 后留言

精选留言(30)

  • Kaito 置顶
    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。

    作者回复: 除了考虑了内存风险,还考虑了CPU风险。赞!先置个顶 :D

    关于绑核的操作,后面再和Kaito同学聊聊,绑核也有些值得探讨的地方的。

    2020-08-14
    27
    113
  • Darren
    Kaito的回答为啥老让我觉得自己那么菜呢������

    我稍微补充下老师对于 ”混合使用 AOF 日志和内存快照“这块的东西:
    在redis4.0以前,redis AOF的重写机制是指令整合(老师上一节课已经说过),但是在redis4.0以后,redis的 AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头,将增量的以指令的方式Append到AOF,这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分就是压缩格式不再是 AOF 格式,可读性较差。Redis服务在读取AOF文件的怎么判断是否AOF文件中是否包含RDB,它会查看是否以 REDIS 开头;人为的看的话,也可以看到以REDIS开头,RDB的文件也打开也是乱码。

    可以通过aof-use-rdb-preamble 配置去设置改功能。

    # When rewriting the AOF file, Redis is able to use an RDB preamble in the
    # AOF file for faster rewrites and recoveries. When this option is turned
    # on the rewritten AOF file is composed of two different stanzas:
    #
    # [RDB file][AOF tail]
    #
    # When loading Redis recognizes that the AOF file starts with the "REDIS"
    # string and loads the prefixed RDB file, and continues loading the AOF
    # tail.
    aof-use-rdb-preamble yes
    2020-08-14
    4
    13
  • 注定非凡
    1,作者讲了什么?
    作者在本章讲了redis两种持久化方式中的RDB方式
    2,作者是怎么把这事给讲明白的?
    为了让大家明白RDB的快照的概念,作者举了拍照片,照合影的例子
    3,为了讲明白,作者讲了哪些要点,有哪些亮点?
    (1)亮点1:作者解释快照使用了拍合影的例子,让我很好的理解快照的概念,以及内存数据大小对快照产生的影响
    (2)要点1:RDB快照,将此时内存中的所有的数据写入磁盘
    (3)要点2:生成快照有两种方式:sava和bgsava,save是主进程执行,生成时会阻塞redis,只能执行查找。bgsave是由主进程fork出子进程执行,
    (4)要点3:子进程在被fork处理时,与主进程共享同一份内存,但在生成快照时采取COW机制,确保不会阻塞主进程的数据读写
    (5)要点4:RDB的执行频率很重要,这会影响到数据的完整性和Redis的性能稳定性。所以4.0后有了aof和rdb混合的数据持久化机制
    4,对于作者所讲,我有哪些发散性思考?
    作者开篇提到的两个问题:快照什么数据,快照有何影响,具体的场景,才能讨论出具体的技术方案,我个人认为,脱离场景谈方案是在自嗨

    5,将来有哪些场景,我能够使用到它?
    我们项目的redis持久化使用的方式就是aof和rdb混合,前一段时间,做过集群升级扩容。把每台8c,30G内存,5主5从,升级改造成为8c,15G内存,15主15从。这样搞主要是因为之前的集群内存占用太高,导致数据持久化失败
    6,读者评论的收获:
    定这个专栏,真是觉得捡到宝了,大神@Kaito写的评论实在漂亮,每次都要读好几遍,读完都有赏心悦目的愉悦感,期望自己有一天也可像他那样出色
    2020-08-15
    8
  • 扩散性百万咸面包
    很奇怪,对于RDB和AOF混合搭配的策略,为什么不把AOF应用于RDB生成增量快照呢?而非要再次生成全量快照?
    2020-08-14
    3
    4
  • 曾轼麟
    【内存风险】:2 核 CPU、4GB 内存、500GB 磁盘, 2GB数据,在操作系统虚拟内存的支持下,fork出一个子进程会贡献主进程的虚拟内存映射的物理空间,这个是MMU实现的不属于Redis的产物,但是当发生数据修改的时候,MMU会将子进程的物理内存复制独立出来(写时拷贝技术)。在 8:2的独写比例中实际需要的物理内存可能会达到 1.6 +1.6 = 3.2 。假设开启swap的情况下,在物理内存不能满足程序运行的时候,虚拟内存技术会将内存中的数据保存在磁盘上,这样会导致Redis性能下降。

    对于绑核问题,我认同Kaito同学的说法。不过我认为的问题是因为云主机的原因:一般大型服务器是使用QPI总线,NMUA架构。NUMA中,虽然内存直接绑定在CPU上,但是由于内存被平均分配在了各个组上。只有当CPU访问自身直接绑定的内存对应的物理地址时,才会有较短的响应时间。而如果需要访问其他CPU 绑定的内存的数据时,就需要通过特殊的通道访问,响应时间就相比之前变慢了(甚至有些服务器宁愿使用swap也不走特殊通道),假如当前云主机比较繁忙的情况下,这样就会导致性能下降。(大部分互联网公司都使用了服务器虚拟化技术)
    2020-08-17
    1
  • test
    1.写太多,COW需要复制的东西太多,内存占用问题;
    2.CPU太少,redis后台还有很多线程在后台工作,会产生CPU竞争。
    2020-08-14
    1
  • 漫步oo0云端
    我想提一个傻问题,我作为初学者想问,如果redis服务挂了,备份有什么用?能恢复的前提不是服务还存活吗?难道服务挂了会自动拉起服务?自动还原吗?

    作者回复: 如果服务挂了,我们可以让Redis实例自动重启。此时,如果没有数据备份的话,再启动时,所有的数据都需要重新写入,这个过程会比较耗时。而如果有备份的话,Redis再启动后,可以直接读入备份数据,对于这些数据的读写操作就可以很快服务了。不知道有没有解答你的疑惑。

    愿意提出来的问题都是好问题哈 :)

    2020-08-14
    10
    1
  • Lemon
    老师您好,有个细节像请教一下:bgsave 子进程会把这个修改过后的副本数据写入 RDB 文件。

    假设有A,B,C三个数据,bgsave 子进程已经读到 B 数据了,此时主线程要修改 A 的数据,这个时候生成的副本 bgsave 会去读取再重新写入 RDB 吗?还是要像文中的图上所示,只有 bgsave 未读到的数据才会生成副本被记录呢?
    2020-08-22
  • 美美
    写操作频繁,使用RDB作为持久化,会因为写时复制机智对主线程频繁造成阻塞,拉低吞吐量。

    有一个不清晰的点还请老师解答。如果采用RDB+AOF,当RDB完成后清空AOF文件,那写盘的这一段时间内的变更岂不是会丢失?还是说在fork子进程后会写新的AOF文件,备份完成后删除旧AOF文件。
    2020-08-20
  • 不负青春不负己🤘
    打卡 第二遍

    作者回复: 赞!

    2020-08-18
  • akka
    老师,文章最后你说的方案“Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法”,
    就是redis 4.0 的AOF重写?
    但是从描述上来看,各个操作的顺序和AOF重写又有点不像
    2020-08-17
  • riryoutexi
    请问蒋老师,线上一般通过什么策略或依据去配置备份方式。比如 数据不能丢失时,多久RDB 一次?
    2020-08-17
  • 每天晒白牙
    Redis持久化
    AOF
    AOF是一种通过记录操作命令的的方式来达到持久化的目的,正是因为记录操作命令而不是数据,所以在恢复数据的时候需要 redo 这些操作命令(当然 AOF 也有重写机制从而使命令减少),如果操作日志太多,恢复过程就会很慢,可能会影响 Redis 的正常使用

    RDB
    RDB 是一种内存快照,把内存的数据在某一时刻的状态记录下来,所以通过 RDB 恢复数据只需要把 RDB 文件读入内存就可以恢复数据(具体实现细节还没去了解)

    但这里有两个需要注意的问题
    1.对哪些数据做快照,关系到快照的执行顺序
    2.做快照时,还能对数据做增删改吗?这会关系到 Redis 是否阻塞,因为如果在做快照时,还能对数据做修改,说明 Redis 还能处理写请求,如果不能对数据做修改,就不能处理写请求,要等执行完快照才能处理写请求,这样会影响性能

    来看第一个问题
    RDB 是对全量数据需要快照,全量数据会使 RDB 文件大,发文件写到磁盘就会耗时,因为 Redis 是单线程,会不会阻塞主线程?(这一点始终都要考虑的点)
    Redis 实现 RDB 的方式有两种
    ①save:在主线程中执行,会导致阻塞
    ②bgsave:创建子线程来执行,不会阻塞,这是默认的
    所以可以使用 bgsave 来对全量内存做快照,不影响主进程

    来看第二个问题
    在做快照时,我们是不希望数据被修改的,但是如果在做快照过程中,Redis 不能处理写操作,这对业务是会造成影响的,但上面刚说完 bgsave 进行快照是非阻塞的呀,这是一个常见的误区:避免阻塞和正常的处理写操作不是一回事。用 bgsave 时,主线程是没有被阻塞,可以正常处理请求,但为了保证快照的完整性,只能处理读请求,因为不能修改正在执行快照的数据。显然为了快照而暂停写是不能被接受的。Redis 采用操作系统提供的写时复制技术(Copy-On-Write 即 COW),在执行快照的同时,可以正常的处理写操作
    bgsave 子线程是由主线程 fork 生成,可以共享主线程的所有内存数据,所以 bgsave 子线程是读取主线程的内存数据并把他们写入 RDB 文件的
    如果主线程对这些数据只执行读操作,两个线程是互不影响的。如果主线程要执行写造作,那么这个数据就会被复制一份,生成一个副本,然后 bgsave 子线程会把这个副本数据写入 RDB 文件,这样主线程就可以修改原来的数据了。这样既保证了快照的完整性,也保证了正常的业务进行

    那如何更好的使用 RDB 呢?
    RDB 的频率不好把握,如果频率太低,两次快照间一旦有宕机,就可能会丢失很多数据。如果频率太高,又会产生额外开销,主要体现在两个方面
    ①频繁将全量数据写入磁盘,会给磁盘带来压力,多个快照竞争有效的磁盘带宽
    ②bgsave 子线程是通过 fork 主线程得来,前面也说了,bgsave 子线程是不会阻塞主线程的,但 fork 这个创建过程是会阻塞主线程的,而且主线程内存越大,阻塞时间越长

    最好的方法是全量快照+增量快照,即进行一次 RDB 后,后面的增量修改用 AOF 记录两次全量快照之间的写操作,这样可以避免频繁 fork 对主线程的影响。同时避免 AOF 文件过大,重写的开销
    2020-08-17
  • Geek_Lin
    文章中写时复制那里,复制的是主线程修改之前的数据还是主线程修改之后的呢?

    作者回复: 子进程读到的是主线程修改前的数据。

    我在文章中介绍写时复制时,说法上有点偏它能达到的效果了,可能让大家理解有误了,抱歉!

    我再解释下,文章中说“这块数据就会被复制一份,生成该数据的副本”,这个操作在实际执行过程中,是子进程复制了主线程的页表,所以通过页表映射,能读到主线程的原始数据,而当有新数据写入或数据修改时,主线程会把新数据或修改后的数据写到一个新的物理内存地址上,并修改主线程自己的页表映射。所以,子进程读到的类似于原始数据的一个副本,而主线程也可以正常进行修改。

    希望能解答你的疑惑。

    2020-08-16
  • u
    写时复制?那20%读操作相关的数据不就没法RDB了么?
    2020-08-16
  • Jackey
    评论中有大神,看完文章再看评论收获更多
    2020-08-16
  • Lionheart
    BGSAVE生成新的RDB文件,替换老的RDB文件,这一步是怎么实现的?
    2020-08-15
  • yyl
    提问:
    1. RDB文件损坏的话,是否影响Redis数据恢复?是否有人工干预方法?
    2. fork进程,父子进程遵循写时复制,为什么父进程内存越大,阻塞时间越长呢?
    2020-08-15
  • yyl
    解答:
    系统的QPS未知,两种情况吧:
    1. QPS较低,不会有什么问题
    2. QPS较高,首先由于写多读少,造成更多的写时拷贝,导致更多的内存占用。如果采用增量快照,需要增加额外的内存开销;再则,写RDB文件,OS会分配一些Cache用于磁盘写,进一步加剧内存资源的消耗。
    由于频繁的写RDB文件,造成较大的磁盘IO开销,消耗CPU

    作者回复: 非常赞!考虑到了根据不同的QPS进行分析。

    我再提一个维度,你可以考虑下,就是修改的键值对的范围,也就是说写操作是针对一小部分键值对,还是针对大部分键值对的。你觉得这个维度会有影响么?

    2020-08-15
    1
  • 第四范式
    临时表内容太多。会浪费内存资源。这个问题。怎么解呢?老师也没说解决方案
    2020-08-15
收起评论
30
返回
顶部