我们通常将 Redis 作为缓存使用,提高读取响应性能,一旦 Redis 宕机,内存中的数据全部丢失,假如现在直接访问数据库大量流量打到 MySQL 可能会带来更加严重的问题。
另外慢慢的从数据库读取放到 Redis 性能必然比不过从 Redis 获取快,也会导致响应变慢。
Redis 为了实现无畏宕机快速恢复,设计了两大杀手锏,分别是 AOF(Append Only FIle)日志和 RDB 快照。
学习一个技术,通常只接触了零散的技术点,没有在脑海里建立一个完整的知识框架和架构体系,没有系统观。这样会很吃力,而且会出现一看好像自己会,过后就忘记,一脸懵逼。
跟着「码哥字节」一起吃透 Redis,深层次的掌握 Redis 核心原理以及实战技巧。搭建一套完整的知识框架,学会全局观去整理整个知识体系。
本文硬核,建议收藏点赞,静下心来阅读,我相信都会有很多收获。
上一篇《Redis 核心篇:唯快不破的秘密》分析了 Redis 的核心数据结构、IO 模型、线程模型、根据不同数据使用合适的数据编码。深层次掌握真正快的原因!
本篇将围绕如下几点展开:
- 宕机后,如何快速恢复?
- 宕机了,Redis 如何避免数据丢失?
- 什么是 RDB 内存快照?
- AOF 日志实现机制
- 什么是 写时复制技术?
- ….
涉及的知识点如图所示:
Redis 全景图
全景图可以围绕两个维度展开,分别是:
应用维度:缓存使用、集群运用、数据结构的巧妙使用
系统维度:可以归类为三高
- 高性能:线程模型、网络 IO 模型、数据结构、持久化机制;
- 高可用:主从复制、哨兵集群、Cluster 分片集群;
- 高拓展:负载均衡
Redis 系列篇章围绕如下思维导图展开,这次一起探索 Redis 的高性能、持久化机制的秘密。
拥有全景图,掌握系统观。
系统观其实是至关重要的,从某种程度上说,在解决问题时,拥有了系统观,就意味着你能有依据、有章法地定位和解决问题。
RDB 内存快照,让宕机快速恢复
65 哥:Redis 因为某些原因宕机了,会导致所有的流量会打到后端 MySQL,我立马重启 Redis,可是它的数据存在内存里面,重启后如何还是没有任何数据,如何防止重启数据丢失呢?
65 哥别急,「码哥字节」带你一步步深入理解到底 Redis 宕机后如何快速恢复的。
Redis 数据存储在内存中,是否可以考虑将内存中的数据写到磁盘上呢?当 Redis 重启的时候就把保存在磁盘上的数据快速恢复到内存中,这样就能实现重启后正常提供服务了。
65 哥:我想到一个方案,每次执行「写」操作操作内存的同时写入到磁盘
这个方案有一个致命问题:每次写指令不仅写内存还是写入磁盘,磁盘的性能相对内存太慢,会导致 Redis 性能大大降低。
内存快照
65 哥:那如何规避这个同时写入的问题呢?
我们通常将 Redis 当作缓存使用,所以即使 Redis 没有保存全部数据,还可以通过数据库获取,所以 Redis 不会保存所有的数据, Redis 的数据持久化使用了「RDB 数据快照」的方式来实现宕机快速恢复。
65 哥:那什么是 RDB 内存快照呢?
在 Redis 执行「写」指令过程中,内存数据会一直变化。所谓的内存快照,指的就是 Redis 内存中的数据在某一刻的状态数据。
好比时间定格在某一刻,当我们拍照的,通过照片就能把某一刻的瞬间画面完全记录下来。
Redis 跟这个类似,就是把某一刻的数据以文件的形式拍下来,写到磁盘上。这个快照文件叫做 RDB 文件,RDB 就是 Redis DataBase 的缩写。
Redis 通过定时执行 RDB 内存快照,这样就不必每次执行「写」指令都写磁盘,只需要在执行内存快照的时候写磁盘。既保证了唯快不破,还实现了持久化,宕机快速恢复。
在做数据恢复时,直接将 RDB 文件读入内存完成恢复。
65 哥:对哪些数据做快照呢?或者多久做一次快照呢?这个会影响快照的执行效率。
65 哥不错呀,开始考虑数据效率问题了。在《Redis 核心篇:唯快不破的秘密》中我们知道他的单线程模型决定了我们要尽可能的避免会阻塞主线程的操作,避免 RDB 文件生成阻塞主线程。
生成 RDB 策略
Redis 提供了两个指令用于生成 RDB 文件:
- save: 主线程执行,会阻塞;
- bgsave:调用 glibc 的函数
fork
产生一个子进程用于写入 RDB 文件,快照持久化完全交给子进程来处理,父进程继续处理客户端请求,生成 RDB 文件的默认配置。
65 哥:那在对内存数据做「快照」的时候,内存数据还能修改么?也就是写指令能否正常处理?
首先我们要明确一点,避免阻塞和 RDB 文件生成期间能处理写操作不是一回事。虽然主线程没有阻塞,到那时为了保证快照的数据的一致性,只能处理读操作,不能修改正在执行快照的数据。
很明显,为了生成 RDB 而暂停写操作,Redis 是不答应的。
65 哥:那 Redis 如何实现一边处理写请求,同时生成 RDB 文件呢?
Redis 使用操作系统的多进程写时复制技术 COW(Copy On Write) 来实现快照持久化,这个机制很有意思,也很少人知道。多进程 COW 也是鉴定程序员知识广度的一个重要指标。
Redis 在持久化时会调用 glibc 的函数fork
产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。
子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段。这时你可以将父子进程想像成一个连体婴儿,共享身体。
这是 Linux 操作系统的机制,为了节约内存资源,所以尽可能让它们共享起来。在进程分离的一瞬间,内存的增长几乎没有明显变化。
bgsave
子进程可以共享主线程的所有内存数据,读取主线程的数据并写入到 RDB 文件。
在执行 SAVE
命令或者BGSAVE
命令创建一个新的 RDB 文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新创建的 RDB 文件中。
当主线程执行写指令修改数据的时候,这个数据就会复制一份副本, bgsave
子进程读取这个副本数据写到 RDB 文件,所以主线程就可以直接修改原来的数据。
这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。
Redis 会使用 bgsave 对当前内存中的所有数据做快照,这个操作是子进程在后台完成的,这就允许主线程同时可以修改数据。
65 哥:那可以每秒都执行 RDB 文件么,这样即使发生宕机最多丢失 1 秒的数据。
过于频繁的执行全量数据快照,有两个严重性能开销:
- 频繁生成 RDB 文件写入磁盘,磁盘压力过大。会出现上一个 RDB 还未执行完,下一个又开始生成,陷入死循环。
- fork 出 bgsave 子进程会阻塞主线程,主线程的内存越大,阻塞时间越长。
优缺点
快照的恢复速度快,但是生成 RDB 文件频率不好把握,频率过低宕机丢失的数据就会比较多;太快,又会消耗额外开销。
RDB 采用二进制 + 数据压缩的方式写磁盘,文件体积小,数据恢复速度快。
Redis 除了 RDB 全量快照以外,还设计了 AOF 写后日志,接下来我们一起来聊下什么是 AOF 日志。