Redis 持久化
1. Redis 持久化
1.1. 提供持久化的方案
- RDB (Redis Database):以指定时间间隔执行数据集的时间点快照;
- AOF (Append Only File):持久性记录服务器接收到的每个写操作,在服务器启动的时候再次重新执行这些操作,从而达到重建原始数据集;
- 无持久化:完全禁用 Redis 的持久性;
- RDB + AOF:组合使用 RDB 和 AOF;
2. RDB 快照持久化
2.1. 简介
-
是什么?
- 类似与照片记录一样的效果,将某一时刻的数据和状态以文件的形式写在磁盘之上,即快照;
- 一旦宕机,快照文件不会丢失,数据的可靠性也得到保证,这个文件称为 RDB 文件;
- 默认开启;
-
干什么?
- 指定时间间隔内,将内存的数据集快照写入磁盘,即
SHAPSHOT
内存快照,它回复的时候再将硬盘的快照文件直接读回内存中; - 保存的文件为:
dump.rdb
文件;
- 指定时间间隔内,将内存的数据集快照写入磁盘,即
-
配置项
- 采用
redis.conf
的SNAPSHOTTING
的save
参数,来进行触发RDB
持久化条件; save m n
:6.0.16 以下配置,在 m 秒内数据集存在 n 次修改,自动触发bgsave
;save m1 n1 m2 n2...
:6.2 及 7 配置,与 6 无太大变化,就是写到了一行;dir
配置:修改dump
文件保存路径;默认是./
;dbfilename
配置:修改 dump 文件名;
- 采用
2.2. 命令触发
2.2.1. save
- 主程序执行,阻塞当前
redis
服务器;
2.2.2. bgsave
- 异步后台执行快照;
- 该触发方式会
fork
一个子进程,由子进程进行复制持久化过程;
linux fork:就是复制当前进程,在内核进程表中创建一个新的进程表象;该进程称之为子进程,被复制的称之为父进程;子进程与父进程完全相同,同时也会复制父进程的数据(堆数据,栈数据和静态数据);
2.3. 优劣势
2.3.1. 优势
- 非常紧凑的单文件时间点表示;非常适合备份;
- 非常适合灾难恢复,是一个可以传输到远程数据中心的压缩文件;
- 最大限度提高了 redis 的性能,因为父进程只做一个操作,即
fork
一个子进程; - 与
AOF
相比,RDB
加载速度快;
2.3.2. 劣势
- 一定时间间隔做备份,如果意外宕机等,则会丢失当前到最后一次快照时间的数据;
- 内存数据的全量同步,如果数据量过大,会导致 I/O 严重,影响服务器性能;
RDB
依赖与主进程fork
,更大的数据集中,可能会导致服务请求的瞬间延迟,可能会几毫秒甚至于一秒钟;fork
操作可能导致短暂的 CPU 资源占用,特别是大数据集的情况下,引起主进程的调度延迟;同时fork
通过写时复制 (COW) 操作实现共享内存,引起系统级别开销,尤其是数据量大的情况;
2.4. 触发 RDB 的情况
- 配置文件默认的快照配置;
- 手动触发命令;
- 执行
flushall
、flushdb
命令; - 执行
shutdown
命令且没有开启AOF
持久化命令; - 主从复制的时候,主节点自动触发;
2.5. RDB COW
在 bgsave
的时候,redis 仍然可以继续处理操作命令,数据仍然可以进行修改,此处用到一项关键技术,即:COW ( Copy-On-Write ),写时复制;
在执行 bgsave
命令的时候,主进程通过 fork
创建一个子进程,子进程此时与父进程使用同一块内存数据(创建子进程的时候,会进行复制父进程页表,但是页表指向的物理内存仍然是同一个);当父进程发生修改的时候,物理内存才会被复制一份;
对于主进程如果说需要修改共享数据的某一块数据,就会发生写时复制,于是**这块数据物理内存就会被复制一份,主进程在这个数据副本进行修改,而子进程继续将原来数据写入 RDB 文件之中;**这个时候会有一个问题,即主进程修改了共享数据,但是子进程保存的是原本的内存数据,主进程刚修改的数据没法第一时间写入 RDB 文件,只能交给下一次快照了;
极端情况下,如果所有的共享内存都被修改,则此时内存占用将膨胀到原先 2 倍;
2.5. 其余小知识
- 知道最后一次执行快照的时间戳:
lastsave
; - 可以通过
redis-check-rdb
检查修复dump.rdb
文件; - 禁止快照方式:动态禁止
redis-cli config set save ""
;或者直接快照配置save
参数; - 配置详解
save <seconds> <changes>
:自动触发条件;dbfilename
:RDB 文件名;dir
:存储目录;stop-writes-on-bgsave-error
:默认为yes
,如果配置成no
:表示不在乎数据不一致,或者有其他的手段发现和控制这种不一致,那么在快照写入失败的时候,也能 确保redis
继续接受新的写请求;即当 bgsave 出错的时候,是否停止 redis 的写请求;rdbcompression
:默认为yes
;对于存储到磁盘的快照,是否使用LZF
算法进行压缩;rdbchecksum
:默认为yes
;存储快照后,可以让redis
使用CRC64
算法进行数据校验,会增加 10% 的性能消耗;rdb-del-sync-files
:主从全量同步时,通过 RDB 文件传输实现;如果没有开启持久化,通过不完成后,是否要移除主从同步的 RDB 文件,默认是no
;如果设计到某些因素,可以设置为yes
;(只有当 AOF 和 RDB 都关闭的情况下,该配置才会生效;)
3. AOF 持久化
3.1. 简介
- 以日志的形式来记录每个写操作;只需追加;启动之初会读取文件重新构建数据;
- 默认情况,redis 不开启
AOF
;
3.2. 详细介绍
3.2.1. 工作流程
- 客户端会传来源源不断的请求命令;
- 命令到达服务端,并非先写入 AOF 文件之中,而是先写入 AOF 缓存中进行保存,当这些命令到达一定量,再写入磁盘,避免频繁 I/O;
- AOF 缓冲会根据 三种写回策略 将命令写入磁盘;
- 随着写入 AOF 内容增加,避免文件膨胀,会根据规则进行命令合并,又称为 AOF 重写,从而达到压缩文件的目的;
- 当服务器重启的时候,会从 AOF 文件中载入数据;
3.2.2. 三种写回策略
配置项 | 写回时机 | 优点 | 缺点 |
---|---|---|---|
Always |
同步写回 | 可靠性高,数据基本不丢失 | 性能影响大 |
Everysec |
每秒写回 | 性能适中 | 宕机时丢失 1 秒内数据 |
No |
操作系统控制 | 性能好 | 宕机时丢失数据多 |
- 默认为:
Everysec
;
3.2.3. 如何使用
-
开启
appendonly
配置项开启,默认为no
,设置为yes
即开启;- 写回策略配置:
appendfsync
; - 保存路径:redis6 时,AOF 和 RDB 一样,通过
dir
参数配置;redis7 时,最终路径由:dir + appenddirname
;
-
保存名称
-
redis6 只有一个,即
appendfilename
参数; -
redis7 分为
multi part aof
设计;拆分为三个文件;base
:基本文件,一般由子进程通过重写产生,最多只有一个;incr
:增量文件,会在aofrw
开始执行的时候被创建,可能存在多个;manifest
:清单文件,为了管理,引入一个清单文件进行管理,同时为了方便 AOF 备份和拷贝,我们将所有 AOF 文件和清单文件放入一个文件目录,即appenddirname
配置;
-
实例:
-
-
异常恢复
-
可以使用:
redis-check-aof --fix
来进行修复;tip : 如果你不小心
flushall
了;然后你有 AOF,其实你可以打开其 incr 文件,然后将最后一行flushall
删除;恢复;数据又回来了;同理,如果你在 AOF 文件后面增加了一行flushall
;那恢复最后,又被 flush 了;
-
3.2.4. 优劣势
- 优势
- 更好保护数据不丢失,性能高,可以做紧急恢复;
- 劣势
- AOF 文件和 RDB 文件相比,在相同数据集情况下,AOF 文件较大,恢复速度较慢;
- AOF 运行效率慢于 RDB,每秒同步策略较好,不同步效率与 RDB 相同;
3.3. AOF 重写机制
3.3.1. 是什么
- 原因:redis 不断将写命令追加到 AOF 文件之中,那么文件会越来越大,占用磁盘空间也会越来越大,恢复时间亦是如此;
- 解决:redis 增加重写机制,当 AOF 文件大小超过所设定的峰值的时候,会自动启动 AOF 文件的内容压缩,只保留可以恢复数据的最小数据集;或者可以手动执行命令:
bgrewriteaof
; - 即:启动 AOF 文件内容压缩,只保留可以恢复数据的最小指令集;
3.3.2. 触发机制
-
官网默认配置:
auto-aof-rewrite-percentage: 100 # 根据上次重写后的 aof 大小,当前是不是增长了一倍 auto-aof-rewrite-min-size: 64mb # 重写时满足的文件大小
- 同时满足的情况下,才会进行触发;
-
手动触发:
bgrewriteaof
; -
触发结果:
3.3.3. 重写原理
AOF 重写函数会进行大量写操作,如果在主进程中,会导致长时间被阻塞,故在子进程中完成;子进程执行 AOF 重写期间,redis 仍然可以处理命令;子进程带有父进程的内存数据拷贝副本,不使用锁的情况下,依旧可以保持数据安全;因为子进程在 AOF 重写时设置一个 AOF 重写缓冲区,这个缓冲区在服务器创建子进程后开始使用;redis 执行完一个写命令,同时将写命令发送给 AOF 缓冲区和 AOF 重写缓冲区;
当子进程完成重写工作后,会向父进程发送一个信号,父进程接受到后,会调用信号处理函数,完成一下操作:
- 将 AOF 重写缓冲区中的所有内容写入到新的 AOF 文件中,保证新 AOF 文件保存的数据库状态和服务器当前状态一致;
- 对新的 AOF 文件进行改名,原子覆盖先有 AOF 文件,完成新旧文件替换;
- 继续处理客户端请求;
整个 AOF 后台重写的过程中,只有信号处理函数执行时会对 redis 主进程造成阻塞,在其他时候,aof 后台重写都不会阻塞主进程;
时间 | redis 主进程(父进程) | 子进程 | 是否阻塞 |
---|---|---|---|
t1 | 执行命令 | NO | |
t2 | 执行命令 | 开始 AOF 重写 | NO |
t3 | 创建子进程,执行 AOF 重写 | 开始 AOF 重写 | NO |
t4 | 执行命令 | 执行重写操作 | NO |
t5 | 执行命令 | 完成重写,向父进程发送信号 | NO |
t6 | 接受到信号,将上述两个命令添加到新的 AOF 文件中 | YES | |
t7 | 用新 AOF 文件覆盖旧 AOF 文件 | YES |
整个过程不需要读取以前旧的 AOF 文件;
3.4. 详细参数配置
配置参数 | 配置含义 | 配置示例 |
---|---|---|
appendonly | 是否开启 AOF | appendonly yes |
appendfilename | 文件名 | appendfilename “aof.aof” |
appendfsync | 同步方式 | everysec/always/no |
no-appendfsync-on-rewrite | aof 重写期间是否同步 | yes / no |
auto-aof-rewrite-percentage , auto-aof-rewrite-min-size | 重写触发配置,文件重写策略;达到 | 100 , 64Mmb |
4. AOF + RDB
4.1. 数据恢复顺序和加载流程
4.2. RDB-AOF 混合
- 开启混合方式设置:
aof-use-rdb-preamble: yes
- RDB-AOF --> RDB 做全量持久化,AOF 做增量持久化
- 使用 RDB 进行快照存储,再使用 AOF 持久化记录所有写操作,重写策略满足或者手动触发重写的时候,
fork
出子进程,会以 RDB 形式将最新的数据存储写入 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,这部分增量命令,会以 AOF 形式写入 AOF 文件;这样的话,重启服务的时候会从 RDB 和 AOF 两部分恢复数据,既保证了数据完整性,又提高了恢复数据的性能; - 混合持久化方式产生的文件一部分是 RDB 格式的全量数据,一部分是 AOF 格式的增量数据;
- 使用 RDB 进行快照存储,再使用 AOF 持久化记录所有写操作,重写策略满足或者手动触发重写的时候,