是什么
- 内存快照, 就是指内存中的数据在某一个时刻的状态记录,即某一时刻的状态以文件的形式写到磁盘上。
- 快照是一次的全量备份、是内存数据的二进制序列化。
- 在做数据恢复时,我们可以直接把 RDB 文件读入内存,很快地完成恢复
文件结构
基本结构
- REDIS:常量,5个字节,标识是否是RDB文件
- db_version:变量,4个字节,记录了RDB文件版本号
- databases:数据,包含着0到n个数据库,以及各个数据库中的键值对
- EOF:常量,1字节,标志RDB文件正文内容的结束
- check_sum:变量,8字节,保存校验和,对以上四部分进行计算得出来的值,用于检查RDB文件是否有出错或者损坏的情况
databases结构
databases里加入有两个数据库,结构如下
database详细结构如下
- SELECTDB:常量,1字节,代表下一个要读入的是个数据库号码
- db_number:变量,大小不定,数据库号码
- key_value_pairs:保存了数据库的所有键值对数据
两个问题
- 对哪些数据做快照关系到快照的执行效率问题:全量快照
- 快照时Redis 是否被阻塞,能否同时正常处理增删改:不阻塞,能常常增删改
两个命令
通过两个命令来生成 RDB 文件,分别是 save 和 bgsave。
- save : 在主线程中执行,会导致阻塞,直到RDB文件创建完毕
- bgsave : fork一个子进程专门用于写入 RDB 文件,避免了主线程的阻塞(默认配置)
快照数据修改(COW机制)
Redis 就会借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。
bgsave中子进程是由主线程 fork 生成的,可以共享父进程内存里面的代码段和数据段数据,进程分离的一瞬间,内存的增长几乎没有明显变化, bgsave 子进程运行后,开始读取主线程的内存数据,并把它们写入 RDB 文件。
如果主线程要修改一块数据(例如图中的键值对 C), 那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。
随着父线程修改操作的持续进行,越来越多的共享页面被分离出来,内存就会持续增长,但是不会超过内存的两倍大小。
子进程因为能看到的数据在进程产生的一瞬间就再也不会改变了,所以把RDB这种持久化叫做快照。
快照的频率
如果频繁地执行全量 快照,也会带来两方面的开销。
- 频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前后交叉,容易造成恶性循环。
- bgsave 子进程通过 fork 操作从主线程创建出来,fork 这个创建过程本身会阻塞主线程,之后不会。
- fork主线程的内存越大,阻塞时间越长
- 如果频繁 fork 出 bgsave 子进程,这就会频繁阻塞主线程了。
增量快照
做了一次全量快照后,后续的快照只对修改的数据进行快照记录,这样可以避免每次全量快照的开销。
和AOF 相比,快照的恢复速度快,但快照的频率不好把握
- 如果频率太低,两次快照间一旦宕机,就可能有比较多的数据丢失。
- 如果频率太高, 又会产生额外开销
在第一次做完全量快照后,T1 和 T2 时刻如果再做快照,我们只需要将被修改的数据写入 快照文件就行。但是,这么做的前提是,我们需要记住哪些数据被修改了。
混合日志和快照
Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。
内存快照以一 定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。
RDB文件的载入工作是在服务器启动时自动执行的,载入过程中一直处于阻塞状态,直至载入完成。