(五)内存快照:宕机后,Redis如何实现快速恢复?
mhr18 2024-11-28 08:32 14 浏览 0 评论
上节课,我们学习了避免丢失数据的AOF方法。这个方法的好处是,只记录每次执行的操作命令,需要持久化的数据量不大。一般而言,只要你采用的不是always的持久化策略,就不会对性能造成太大的影响。
但是,也正因为记录的是操作命令,而不是实际的数据,所以,用AOF方法进行故障恢复的时候,需要逐一把操作日志执行一遍。如果操作日志非常多,Redis就会恢复的很缓慢,影响到正常使用。这当然不是理想的结果,那有没有既可以保证可靠性,又能在宕机后实现快速恢复的其他方法呢?
当然有了,这就是我们今天要一起学习的另一种持久化方法:内存快照。所谓的内存快照,就是指内存中数据某一时刻的状态记录。这就类似于照片,当你给朋友拍照时,一张照片就能把朋友一瞬间的形象完全记下来。
对Redis来说,它实现类似于照片记录效果的方式,就是把某一个时刻的状态以文件的形式记录到磁盘上,也就是快照。这样一来,即使宕机,快照文件也不会丢,数据的可靠性也就得到了保证。这个快照文件就称为RDB文件,其中,RDB就是“Redis DataBase”的缩写。
和AOF相比,RDB记录的是某一时刻的数据,并不是操作,所以,在做数据恢复时,我们可以直接把RDB文件读入内存,很快的完成恢复。听起来好像很不错,但内存快照也不是最优选项,为什么这么说呢?
我们要考虑两个问题:
- 对哪些数据做快照?这关系到快照的执行效率问题;
- 做快照时,数据还能被增删改吗?这关系到Redis是否被阻塞,能否同时正常处理请求;
这么说可能你还不太好理解,我还是拿拍照来举例子。我们在拍照时,通常要关注两个问题:
- 如何取景?也就是说,我们打算把哪些人,哪些物拍到照片中;
- 在按快门前,要提醒朋友不要乱动,否则拍出来的照片就会模糊了;
你看,这两个问题是不是很重要呢,接下来,我们具体来聊一聊,先说“取景”问题,也就是我们对哪些数做快照。
给哪些数据做快照?
Redis的数据都在内存中,为了提供所有数据的可靠性,它执行的是全量快照,也就是说,把内存中所有的数据都记录到磁盘中,这就类似于给100个人拍合影,把每个人都拍进照片里。这样做的好处是,一次性记录了所有数据,一个都不少。
当你给一个人拍照时,只要协调一个人就够了,但是,拍100个人的大合影,却要协调100个人的位置,状态等等,这当然会更费力。同样,给内存做全量快照,把它们全部都写入磁盘也会花费很多时间。而且,全量数据越多,RDB文件就越大,往磁盘上写数据的时间开销就越大。
对于Redis而言,它的单线程模型就决定了,我们要尽量避免所有阻塞主线程的操作,所以,针对任何操作,我们都会提一个灵魂之问:“它会阻塞主线程吗?”RDB文件的生成是否会阻塞主线程,这就关系到是否会降低Redis的性能。
Redis提供了两个命令来生成RDB文件,分别是save和bgsave。
- save:在主线程中执行,会导致阻塞;
- bgsave:创建一个子进程,专门用于写入RDB文件,避免了主线程的阻塞,这也是Redis RDB文件生成的默认配置。
好了,这个时候,我们就可以通过bgsave命令来执行全量快照,这既提供了数据的可靠性保证,也避免了对Redis的性能影响。
接下来,我们要关注的问题就是,在对内存数据做快照时,这些数据还能“动”吗?也就是说,这些数据还能被修改吗?这个问题非常重要,这是因为,如果数据能被修改,那就意味着Redis还能正常处理写操作。否则,所有写操作必须等到快照完了才能执行,性能一下就降低了。
快照时数据能修改吗?
在给别人拍照时,一旦对方动了,那么这张照片就拍糊了,我们就需要重拍,所以,我们当然希望对方保持不动。对于内存快照而言,我们也同样不希望数据”动“。
举个例子,我们在时刻T给内存做快照,假设内存数据量时4GB,磁盘写入带宽时0.2GB/s,至少需要20s才能做完。如果在时刻T+5时,一个还没有被写入磁盘的内存数据A,被修改成了A+,那么就会破坏快照的完整性,因为A+不是T时刻的状态。因此,和拍照类似,我们在做快照时也不希望数据”动“,也就是不能被修改。
但是,如果数据在快照期间不能被修改,时会有潜在问题的。对于刚刚的例子来说,在做快照的20s时间里,如果这4GB的数据不能被修改,Redis就不能处理这些数据的写操作,那无疑就会给业务服务造成巨大的影响。
你可能会想到,可以用bgsave避免阻塞啊。这里我就要说一个常见的误区了,避免阻塞和正常处理写操作并不是一回事。此时,主线程的确没有被阻塞,可以正常接收请求,但是,为了保证快照的完整性,他只能处理读操作,因为不能修改正在执行快照的数据。
为了快照而暂停写操作,肯定时不能接受的。所以这个时候,Redis就会借助操作系统提供的写时复制(Copy-On-Write)技术,在执行快照时,正常处理写操作。
简单来说,bgsave子进程是由主线程fork生成的,可以共享主线程中的所有内存数据。bgsave子进程运行后,开始读取主线程的内存数据,并把他们写入RDB文件。
此时,如果主线程对这些数据也都是读操作(例如图中的键值对A),那么主线程和bgsave子进程相互不影响。但是,如果主线程要修改一块数据(例如图中的键值对C),那么,这块数据就会被复制一份,生成该数据的副本(键值对C’)。然后,主线程在这个数据副本上进行修改。同时,bgsave子进程可以继续把原来的数据(键值对C)写入RDB文件。
这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。
到这里,我们就解决了对“哪些数据做快照”以及“做快照时数据能是否修改”这两大问题:Redis会使用bgsave对内存中的所有数据做快照,这个操作时子进程在后台完成的,这就允许主线程同时可以修改数据。
现在,我们再来看另一个问题:多久做一次快照?我们在拍照的时候,有一项技术叫“连拍”,可以记录人或物连续多个瞬间状态。那么,快照也适合“连拍”吗?
可以每秒做一次快照吗?
对于快照来说,所谓:“连拍”就是指连续的做快照。这样一来,快照的间隔时间很短,即使某一个时刻发生宕机了,因为上一时刻快照刚执行,丢失的数据也不会太多。但是,这其中的快照间隔时间就很关键了。
如下图所示,我们先在T0时刻做了一次快照,然后又在T0+t时刻做了一次快照,在这期间,数据块5和9被修改了。如果在t这段时间内,机器宕机了,那么,只能按照T0时刻的快照数据进行恢复。此时,数据块5和9的修改值因为没有快照记录,就无法修复了。
所有,想要尽可能的恢复数据,t值就要尽可能的小,t越小,就越像“连拍”。那么,t可以小到什么程度呢,比如说是不是可以每秒做一次快照?毕竟,每次快照都是由bgsave子进程在后台进行,也不会阻塞主线程。
这种想法其实是错误的。虽然bgsave执行时不阻塞主线程,但是,如果频繁的执行全量快照,也会带来两方面的开销。
一方面,频繁的将全量数据写入磁盘,会给磁盘带来很大的压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环。
另一方面,bgsave子进程需要通过fork操作从主线程中创建出来。虽然,子进程在创建后不会阻塞主线程,但是,fork创建这个过程本身会阻塞主线程,而且主线程的内存越大,阻塞时间越长。如果频繁fork出bgsave子进程,这就会频繁阻塞主线程了(所以,在Redis中如果有一个bgsave子进程在运行,就不会再启用第二个bgsave子进程)。那么,有什么其他好方法吗?
此时,我们可以做增量快照,所谓增量快照就是指做了一次全量快照后,后续的快照只对修改的数据做快照记录,这样可以避免每次全量快照的开销。
在第一次做完全量快照后,T1和T2时刻如果再做快照,我们只需要将被修改的数据写入快照文件就行。但是,这么做的前提是,我们需要记住哪些数据被修改了。你可以不要小瞧这个”记住“功能,它需要我们使用额外的元数据信息去记录哪些数据被修改了,这回带来额外的空间开销问题。如下图所示:
如果我们对每一个键值对的修改,都做个记录,那么,如果有1万个键值对被修改,我们就需要1万条额外的记录。而且,有的时候,键值对非常小,比如只有32字节,而记录它被修改的元数据信息,就可能需要8字节,这样的话,为了”记住“修改,引入的额外空间开销比较大。这对于内存资源宝贵的Redis来说,有些得不偿失。
到这里,你可以发现,虽然跟AOF相比,快照的恢复速度快,但是,快照的频率不好把握,如果频道太低,两次快照间一旦宕机,丢失的数据就会比较多。如果频率太高,又会产生额外开销,那么,还有什么办法既能利用RDB的快速恢复,又能以较小的开销做到尽量少丢失数据呢?
Redis4.0中提出了一个混合使用AOF日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用AOF日志记录这期间所有的命令操作。
这样一来,快照就不用很频繁的执行,这就避免了频繁fork对主线程的影响。而且,AOF日志也只记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。
如下图所示,T1和T2时刻的修改,用AOF日志记录,等到第二次做全量快照时,就可以清空AOF日志,因为此时的修改都已经记录到快照中了,恢复时就不再用日志了。
这个方法既能享受到RDB文件快速恢复的好处,又能享受到AOF只记录操作命令的简单优势,颇有点”鱼和熊掌可以兼得“的感觉,建议你在实践中用起来。
小结
这节课,我们学习了Redis用于避免数据丢失的内存快照方法。这个方法的优势在于可以快速恢复数据库,也就是只需要把RDB文件直接读入内存,这就避免了AOF需要顺序,逐一重新执行操作带来的低效性能问题。
不过,内存快照也有它的局限性。它拍的是内存的”大合影“,不可避免的会耗时耗力。虽然,Redis设计了bgsave和写时复制方式,尽可能减少了内存快照对正常读写的影响,但是,频繁快照仍然是不太能接受的。而混合使用RDB和AOF,正好可以取两者之长,避两者之短,以较小的性能开销保证数据的可靠性和性能。
最后,关于AOF和RDB的选择问题,我再给你提三点建议:
数据不能丢失,内存快照和AOF的混合使用是一个很好的选择;
如果允许分钟级别的数据丢失,可以只使用RDB;
如果只用AOF,优先使用everysec的配置选项,因为它在性能和可靠性之间取了一个平衡。
到这里,关于持久化我们就讲完了,这块儿内容是熟练掌握 Redis 的基础,建议你一定好好学习下这两节课。如果你觉得有收获,希望你能帮我分享给更多的人,帮助更多人解决持久化的问题。
如果这篇文章提升了你对Redis的知识,请关注我会持续更新。
相关推荐
- B站收藏视频失效?mybili 收藏夹备份神器完整部署指南
-
本内容来源于@什么值得买APP,观点仅代表作者本人|作者:羊刀仙很多B站用户都有过类似经历:自己精心收藏的视频突然“消失”,点开一看不是“已被删除”,就是“因UP主设置不可见”。而B站并不会主动通知...
- 中间件推荐初始化配置
-
Redis推荐初始化配置bind0.0.0.0protected-modeyesport6379tcp-backlog511timeout300tcp-keepalive300...
- Redis中缓存穿透问题与解决方法
-
缓存穿透问题概述在Redis作为缓存使用时,缓存穿透是常见问题。正常查询流程是先从Redis缓存获取数据,若有则直接使用;若没有则去数据库查询,查到后存入缓存。但当请求的数据在缓存和数据库中都...
- 后端开发必看!Redis 哨兵机制如何保障系统高可用?
-
你是否曾在项目中遇到过Redis主服务器突然宕机,导致整个业务系统出现数据读取异常、响应延迟甚至服务中断的情况?面对这样的突发状况,作为互联网大厂的后端开发人员,如何快速恢复服务、保障系统的高可用...
- Redis合集-大Key处理建议
-
以下是Redis大Key问题的全流程解决方案,涵盖检测、处理、优化及预防策略,结合代码示例和最佳实践:一、大Key的定义与风险1.大Key判定标准数据类型大Key阈值风险场景S...
- 深入解析跳跃表:Redis里的"老六"数据结构,专治各种不服
-
大家好,我是你们的码农段子手,今天要给大家讲一个Redis世界里最会"跳科目三"的数据结构——跳跃表(SkipList)。这货表面上是个青铜,实际上是个王者,连红黑树见了都要喊声大哥。...
- Redis 中 AOF 持久化技术原理全解析,看完你就懂了!
-
你在使用Redis的过程中,有没有担心过数据丢失的问题?尤其是在服务器突然宕机、意外断电等情况发生时,那些还没来得及持久化的数据,是不是让你夜不能寐?别担心,Redis的AOF持久化技术就是...
- Redis合集-必备的几款运维工具
-
Redis在应用Redis时,经常会面临的运维工作,包括Redis的运行状态监控,数据迁移,主从集群、切片集群的部署和运维。接下来,从这三个方面,介绍一些工具。先来学习下监控Redis实时...
- 别再纠结线程池大小 + 线程数量了,没有固定公式的!
-
我们在百度上能很轻易地搜索到以下线程池设置大小的理论:在一台服务器上我们按照以下设置CPU密集型的程序-核心数+1I/O密集型的程序-核心数*2你不会真的按照这个理论来设置线程池的...
- 网络编程—IO多路复用详解
-
假如你想了解IO多路复用,那本文或许可以帮助你本文的最大目的就是想要把select、epoll在执行过程中干了什么叙述出来,所以具体的代码不会涉及,毕竟不同语言的接口有所区别。基础知识IO多路复用涉及...
- 5分钟学会C/C++多线程编程进程和线程
-
前言对线程有基本的理解简单的C++面向过程编程能力创造单个简单的线程。创造单个带参数的线程。如何等待线程结束。创造多个线程,并使用互斥量来防止资源抢占。会使用之后,直接跳到“汇总”,复制模板来用就行...
- 尽情阅读,技术进阶,详解mmap的原理
-
1.一句话概括mmapmmap的作用,在应用这一层,是让你把文件的某一段,当作内存一样来访问。将文件映射到物理内存,将进程虚拟空间映射到那块内存。这样,进程不仅能像访问内存一样读写文件,多个进程...
- C++11多线程知识点总结
-
一、多线程的基本概念1、进程与线程的区别和联系进程:进程是一个动态的过程,是一个活动的实体。简单来说,一个应用程序的运行就可以被看做是一个进程;线程:是运行中的实际的任务执行者。可以说,进程中包含了多...
- 微服务高可用的2个关键技巧,你一定用得上
-
概述上一篇文章讲了一个朋友公司使用SpringCloud架构遇到问题的一个真实案例,虽然不是什么大的技术问题,但如果对一些东西理解的不深刻,还真会犯一些错误。这篇文章我们来聊聊在微服务架构中,到底如...
- Java线程间如何共享与传递数据
-
1、背景在日常SpringBoot应用或者Java应用开发中,使用多线程编程有很多好处,比如可以同时处理多个任务,提高程序的并发性;可以充分利用计算机的多核处理器,使得程序能够更好地利用计算机的资源,...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)