这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战
思维导图:
零、前言
RDB(Redis DataBase):内存快照,即使宕机,快照文件也不会丢失。
优点:
-
RDB是二进制压缩文件, 占用空间小, 便于传输(传给slaver) -
主进程
fork子进程, 可以最大化Redis性能, 主进程不能太大,Redis的数据量不能太大, 复制过程中主进程阻塞
缺点: 不保证数据完整性, 会丢失最后一次快照以后更改的所有数据
考虑三个关键问题:
-
对哪些数据做快照? 这关系到快照的执行效率问题;
Redis的数据都在内存中, 为了提供所有数据的可靠性保证, 它执行的是全量快照, 也就是说, 把内存中的所有数据都记录到磁盘中。 -
做快照时, 数据还能被增删改吗? 这关系到
Redis是否被阻塞, 能否同时正常处理请求。 -
多久做一次快照?
1. 对哪些数据做快照?
Redis 的数据都在内存中,为了提供所有数据的可靠性保证,它执行的是全量快照。
也就是说,把内存中的所有数据都记录到磁盘中。
Redis 提供了两个命令来生成 RDB 文件:
save:在主线程中执行,会导致阻塞。bgsave:创建一个子进程,专门用于写入RDB文件,避免了主线程的阻塞,这也是Redis RDB文件生成的默认配置。
那当然使用 bgsave 命令来执行全量快照,这既提供了数据的可靠性保证,也避免了对 Redis 的性能影响。
2. 做快照时,数据还能被增删改吗?
那么当然了,做快照时,
Redis中数据还能被增删了,要不然这技术就白瞎了。
先来了解下 Redis 中的快照:在某个时间点,保存内存中的数据到磁盘。
那么再来看下,数据操作有哪些:
- 读操作:只是读数据,不会影响内存中数据,就不会影响快照。
- 写操作:会影响,为了快照而暂停写操作,肯定是不能接受的。
那么是什么技术保障了快照一致性?
Redis 借助操作系统提供的 写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。
说白了,就是数据要被修改了的时候,会被复制一份,生成该数据的副本。
讲深一点:
子进程复制了主进程的页表,所以通过页表映射,能读到主线程的原始数据,而当有新数据写入或数据修改时,主线程会把新数据或修改后的数据写到一个新的物理内存地址上,并修改主线程自己的页表映射。所以,子进程读到的类似于原始数据的一个副本,而主线程也可以正常进行修改。
举个栗子:
- 主进程:修改 键值对C ,会新开辟内存生成 键值对C',再把引用指向 键值对C' 的内存地址。
- 子进程:
fork主进程,会复制一份主进程里的 页表 (可以通过页表映射找到数据) - 主进程:修改 键值对C ,同时其 页表 的映射也会相应改变,变为 页表'
- 子进程:还保持原有的 页表 ,就能找到原先的数据。所以不会被影响到。
3. 多久做一次快照?
这个考察方面比较多,多久同步一次得根据实际的情况:
- 读写请求比例
- 机器内存大小
- 实际每分钟写请求次数
可以每秒做一次快照吗?不可以。原因如下:
- 频繁地执行全量快照,也会带来两方面的开销:
- 磁盘压力过大:频繁将全量数据写入磁盘
- 阻塞主进程:
bgsave子进程需要通过fork操作从主线程创建出来,这个过程会阻塞。
那如何做呢?
- 配置参数定期执行:比如 15分钟 内至少有1个键被修改,那就生成快照
- 定时:用脚本触发命令
Redis 4.0 中提出了一个混合使用 AOF 日志和 RDB内存快照的方法:
- 内存快照以一定的频率执行。
- 在两次快照之间,使用
AOF日志记录这期间的所有命令操作。
一、触发快照的方式
Redis提供了两个命令来生成RDB文件, 分别是save和bgsave。
触发方式:
- 符合自定义配置的快照规则
- 执行
save或者bgsave命令 - 执行
flushall命令 - 执行主从复制操作 (第一次)
(1)配置参数定期执行
save : 在主线程中执行, 会导致阻塞;
在 redis.conf 中配置: save 多少秒内,数据变了多少
漏斗设计,提供性能
save "" # 不使用RDB存储,不能主从
save 900 1 # 表示15分钟(900秒钟)内至少1个键被更改则进行快照。
save 300 10 # 表示5分钟(300秒)内至少10个键被更改则进行快照。
save 60 10000 # 表示1分钟内至少10000个键被更改则进行快照。
(2)命令显式触发
bgsave : 创建一个子进程, 专门用于写入 RDB 文件, 避免了主线程的阻塞, 这也是 Redis RDB 文件生成的默认配置。
bgsave子进程是由主线程fork生成的, 可以共享主线程的所有内存数据。bgsave子进程运行后, 开始读取主线程的内存数据, 并把它们写入RDB文件。
Redis 就会借助操作系统提供的写时复制技术(Copy-On-Write, COW), 在执行快照的同时, 正常处理写操作。
在客户端输入 bgsave 命令
127.0.0.1:6379> bgsave
Background saving started
二、RDB 执行流程
执行流程如图:
-
Redis父进程首先判断: 当前是否在执行save, 或bgsave/bgrewriteaof(aof文件重写命令)的子进程, 如果在执行则bgsave命令直接返回。 -
父进程执行
fork(调用OS函数复制主进程)操作创建子进程, 这个复制过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令。 -
父进程
fork后,bgsave命令返回“Background saving started”信息并不再阻塞父进程,并可以响应其他命令。 -
子进程创建
RDB文件, 根据父进程内存快照生成临时快照文件, 完成后对原有文件进行原子替换。(RDB始终完整) -
子进程发送信号给父进程表示完成, 父进程更新统计信息。
-
父进程
fork子进程后, 继续工作。
三、RDB 文件结构
文件结构如图:
文件结构字段说明:
- 头部 5字节 固定为
REDIS字符串 - 4字节
“RDB”版本号(不是Redis版本号), 当前为9, 填充后为0009 - 辅助字段, 以
key-value的形式,如下图:
- 存储数据库号码
- 字典大小
- 过期
key - 主要数据, 以
key-value的形式存储 - 结束标志
- 校验和, 就是看文件是否损坏, 或者是否被修改。
查看 dump.rdb 文件查看(可以用 winhex 工具查看)
这里用
vi打开,输入命令:%!xxd -g 1切换到十六进制模式显示
00000000: 52 45 44 49 53 30 30 30 39 ef bf bd 09 72 65 64 REDIS0009....red
00000010: 69 73 2d 76 65 72 35 2e 30 2e 35 ef bf bd 0a 72 is-ver5.0.5....r
00000020: 65 64 69 73 2d 62 69 74 73 ef bf bd 40 ef bf bd edis-bits...@...
00000030: 63 74 69 6d 65 ef bf bd 01 ef bf bd 5d 75 73 65 ctime.......]use
00000040: 64 2d 6d 65 6d ef bf bd ef bf bd 18 0a 20 20 20 d-mem........
00000050: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
00000060: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 ef bf ..
00000070: bd 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 ..
00000080: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
00000090: 20 20 20 20 61 6f 66 2d 70 72 65 61 6d 62 6c 65 aof-preamble
000000a0: ef bf bd ef bf bd ef bf bd 01 70 72 6f 64 75 63 ..........produc
000000b0: 74 5f 73 6b 75 5f 79 79 ef bf bd ef bf bd 34 ef t_sku_yy......4.
000000c0: bf bd 25 ef bf bd 40 ef bf bd 37 ef bf bd 0a ..%...@...7....