redis-缓存-持久化
一、来因宫
1、啥叫持久化?为何需要持久化?
持久化:将数据从 “易失性存储(如内存)” 转移到 “非易失性存储(如磁盘、SSD、云存储)”,并确保数据在断电、进程崩溃、设备故障后仍能被完整保留和恢复的技术过程。
1、存储在缓存中的数据,出现关机、关闭程序情况,缓存中的数据失效,导致数据缺失,业务流程出现缺陷;
2、将缓存中的数据同步储存到磁盘中,再次出现关机、关闭程序情况下,可以根据磁盘的数据进行修复,保证业务流程安全性
2、redis持久化方案
2.1、RDB - 快照持久化
A、定义原理
Redis Database Backup简称RDB ,在特定时间点,将 Redis 内存中的所有数据(键值对、数据结构)以 “二进制压缩格式” 写入磁盘文件(默认 dump.rdb)
我之前学习时,死记硬背RDB-AOF,RDB-AOF,RDB-AOF…
而忽略了它的全称更方便我们记忆,学习讲究方法,弄清楚从哪来、到哪去的整体流程,对技术的理解更加的深刻。
触发机制:“手动触发” 与 “自动触发” 结合
RDB **不会实时写入** ,而是通过 “触发条件” 执行快照,避免频繁 I/O 开销:
a、手动触发:用户通过命令主动生成快照,适用于备份场景:
SAVE:主线程直接执行快照,期间会阻塞所有客户端请求(不推荐生产环境)
BGSAVE:主线程创建一个子进程,由子进程负责生成快照,主线程继续处理客户端请求。
b、自动触发:通过 Redis 配置文件(redis.conf)设置 “时间窗口 + 写操作次数” 的阈值,
满足条件时自动执行 BGSAVE
save 900 1 # 900秒内至少1次写操作,触发BGSAVE
save 300 10 # 300秒内至少10次写操作,触发BGSAVE
save 60 10000 # 60秒内至少10000次写操作,触发BGSAVE
多个 save 配置是 “或” 的关系,满足任意一个条件即触发 RDB
若不需要自动 RDB,可在配置文件中注释所有 save 行,或添加 save ""
B、快照生成流程:Copy-on-Write(写时复制)
为了解决"快照生成期间,主线程还需要同步修改数据" 的矛盾,RDB基于Copy-on-Write(写时复制)机制实现:
a、主线程执行BGSAVE时,通过fork()创建一个子线程,此时的子线程和主线程都能同时读写一块内存空间
b、子线程开始遍历获取内存中的数据,将其写入到磁盘的dump.rdb文件
c、若出现子线程读取过程中,主线程进行修改数据,这个时候OS会复制一份创建对应的副本内容,主线程修改副本的内容,子线程正常继续读取fork()时的数据
d、子线程全部写完后,用新的dump.rdb覆盖旧文件,快照生成结束
C、dump.rdb文件说明
- 使用二进制的压缩格式,文件体积小(比AOF小),便于传输和存储
- 不可以直接读取里面的内容,需要使用 redis-check-rdb 工具校验完整性
- redis出现问题重启时,直接将文件的二进制数据加载到内存,速度远快于AOF
D、RDB 数据恢复流程
当 Redis 服务启动时,会自动检测路径下是否存在配置的 RDB 文件,若存在则加载 RDB 恢复数据
- redis启动时,先读取对应的配置文件,确定RDB文件的路径以及名称
- 验证RDB文件,确保文件完整性
- 解析文件中的数据结构,将键值对逐一加载到内存中
- 加载完成后,监听端口,开始处理客户端请求
E、RDB的优缺点
- 高性能备份:内存数据库需定期将全量数据同步到磁盘,避免单点故障导致数据丢失;
- 高效数据恢复:当 Redis 重启时,需快速加载全量数据到内存,避免恢复时间过长影响服务;
- 低资源占用:持久化过程不能过度占用 CPU / 内存,以免影响 Redis 对外提供的读写服务。
缺点
- 数据丢失风险:RDB 是周期性快照(按照设置备份周期),若两次快照间隔期间 Redis 服务崩溃(如机器断电),会丢失 “上次快照到崩溃前” 的所有数据;
- fork 子进程的开销:触发 BGSAVE 时,主线程需 fork 子进程,fork 过程会阻塞主线程(阻塞时间与内存数据量正相关); – 触发时堵塞
- 不适合实时持久化:无法满足 “数据零丢失” 的场景(如涉及到钱的交易数据)
2.2、AOF - 快照持久化
A、定义原理
Append Only File简称AOF,翻译过来的意思 只追加文件;
原理是记录 Redis 服务器处理的每一条写命令(如 SET、HSET、LPUSH 等),并以日志文件(.aof 文件)的形式追加存储。
当 Redis 重启时,通过重头执行 AOF 文件中的命令,即可恢复内存中的数据。
AOF 适合对数据完整性要求高的场景,是 Redis 实现 “近实时持久化” 的关键方案(也只是接近)
B、处理流程
接收到写命令时,现将执行命令写入到日志中,再去执行命令
a、接收命令并校验:在接收到执行命令时,校验对应的命令格式是否正确
b、命令追加到aof_buf:现将命令追加写入到内存中的 aof_buf 缓冲区
c、执行命令并响应:主线程执行命令,更新内存中的数据结构,然后向客户端返回 结果
d、根据配置的 “刷盘策略”,将 aof_buf 中的数据同步到磁盘上的 .aof 文件
刷盘策略:
一起了解下AOF的配置, redis.conf 中
appendfsync参数:默认值everysec
可选值:
- always:每执行一条写命令,立即刷盘(最安全,性能最差,IO 密集)
- everysec:每秒刷盘一次(平衡安全与性能,默认推荐,最多丢失 1 秒数据)
- no:由操作系统决定刷盘时机(性能最好,最不安全,依赖 OS 页缓存,可能丢失大量数据)
C、 AOF 文件格式
AOF 文件是文本格式(可直接编辑器查看),内容为 Redis 可解析的命令序列,格式遵循 Redis 通信协议(RESP 协议),结构清晰且易于调试,实例:
*3\r\n$3\r\nSET\r\n$4\r\nname\r\n$5\r\nredis\r\n # 对应命令:SET name redis
*4\r\n$4\r\nHSET\r\n$6\r\nuser:1\r\n$4\r\nage\r\n$2\r\n20\r\n # 对应命令:HSET user:1 age 20
*2\r\n$6\r\nEXPIRE\r\n$4\r\nname\r\n$5\r\n3600\r\n # 对应命令:EXPIRE name 3600
- *N:表示命令包含 N 个参数(如 SET name redis 有 3 个参数:SET、name、redis,故开头为 *3)
- $M:表示下一个参数的长度为 M 字节(如 $3\r\nSET 表示参数 SET 长度为 3)
- \r\n:换行符,用于分隔参数,确保 Redis 解析时没问题
D、数据重写机制(Rewrite)机制
AOF 以 “追加命令” 的方式记录数据,若长期运行,会导致以下问题:
- 文件体积膨胀:例如多次执行 INCR counter,AOF 会记录所有 INCR 命令(如 1000 次 INCR 会占 1000 行),但实际内存中仅需存储最终结果 SET counter 1000
- 恢复速度变慢:重启时需重放大量冗余命令,延长服务不可用时间
为了解决这些问题,Redis 引入 AOF 日志重写 机制:
通过遍历当前内存中的数据结构,直接生成 “重建这些数据所需的最小命令集”,替换原有的冗余 AOF 日志,实现文件瘦身
重写流程(基于 COW 机制,非阻塞):
- 主线程执行 BGREWRITEAOF 命令(或自动触发),先 fork() 一个子进程(与 RDB 类似,共享内存页表)
- 子进程遍历内存中的所有键值对,生成对应的 “重建命令”(如 SET key value、HMSET hash key1),写入一个临时 AOF 文件
- 主线程继续处理客户端请求,同时将新的写命令追加到两个地方:原 aof_buf 缓冲区、临时的 aof_rewrite_buf 缓冲区
- 子进程完成临时文件写入后,向主线程发送信号
- 主线程将 aof_rewrite_buf 中的所有新命令追加到临时文件,然后原子替换原 AOF 文件
- 释放旧 AOF 文件的磁盘空间,重写完成
E、补充一下配置参数
1、是否开启 AOF 持久化
appendonly: 默认no
- yes:开启;
- no:关闭(默认仅开启 RDB)
2、重写期间是否暂停
no-appendfsync-on-rewrite: 默认yes
- yes:重写时暂停刷盘,仅写入缓冲区(减少 IO 竞争,推荐);
- no:重写时仍按 appendfsync 策略刷盘(可能导致 IO 瓶颈)
3、启动时若 AOF 文件末尾存在截断(如异常崩溃导致),是否继续加载
aof-load-truncated 默认yes
- yes:忽略截断部分,加载可用数据(避免服务启动失败);
- no:加载失败,需手动修复文件
4、是否在AOF文件开头嵌入RDB快照
aof-use-rdb-preamble yes(Redis 4.0+)
- yes:开启 “混合持久化”(AOF 头部是 RDB 二进制数据,尾部是增量命令),兼顾 RDB 恢复快和 AOF 数据全的优势;
- no:纯 AOF 格式(仅命令日志)
F、AOF 数据恢复流程
当 Redis 启动且开启 AOF,会优先加载 AOF 文件(若同时开启 RDB,AOF 优先级更高,因数据更完整)
文件验证:Redis 读取 dir 目录下的 appendfilename 文件,先检查文件头部标识(若开启混合持久化,头部是 RDB 的 Magic Number;否则是 Redis 命令协议)
处理截断:若文件末尾存在截断(如机器断电导致命令未写完),根据 aof-load-truncated 配置决定是否继续加载(默认忽略截断部分)
数据恢复
- 若开启混合持久化,先加载头部的 RDB 快照,再重放尾部的增量 AOF 命令
- 若纯 AOF 格式:逐行解析 AOF 文件中的命令,按顺序执行,重建内存数据
启动服务:恢复完成后,Redis 监听端口,开始处理客户端请求(恢复期间服务不可用,时间与 AOF 文件大小正相关)
G、AOF 的优缺点
1、数据安全性高:配置 appendfsync always 可实现 “命令级持久化”,理论上零数据丢失(仅在 Redis 崩溃且未刷盘时丢失当前命令)
2、文件可读性强:AOF 是文本格式,可直接查看或编辑
3、混合持久化优势(Redis 4.0+):开启 aof-use-rdb-preamble yes 后,兼顾 RDB 的 “快速恢复”(头部快照)和 AOF 的 “数据完整”(尾部增量命令),解决纯 AOF 恢复慢的问题
4、无数据丢失风险累积:RDB 依赖周期性快照,间隔内风险累积;AOF 实时记录命令,风险仅局限于 “刷盘间隔”(如 1 秒)
缺点
1、文件体积大:纯 AOF 格式会记录所有写命令,即使是冗余操作(如多次 INCR),文件体积远大于 RDB(相同数据量下,AOF 体积可能是 RDB 的 2-5 倍)
2、恢复速度慢(纯 AOF 场景):重启时需逐行重放所有命令,若文件体积达 GB 级,恢复时间可能长达数分钟(混合持久化可缓解此问题)
3、性能开销较高:appendfsync always 会导致大量同步 IO,主线程频繁阻塞,QPS 大幅下降;
即使是 everysec,每秒一次的刷盘也会带来一定 IO 开销(相比 RDB 的周期性 fork,AOF 刷盘更频繁)
4、重写可能阻塞:虽然重写是异步的,但 fork 子进程时会短暂阻塞主线程(内存越大,阻塞时间越长),且重写过程中若有大量写命令,会导致临时文件体积增大
结尾
Redis 持久化的核心目标是在 “数据可靠性” 与 “服务性能” 之间找到平衡
单独使用 RDB 或 AOF 都有短板
因此 生产环境推荐优先采用 “RDB + AOF 混合持久化” 方案
通用最佳配置
# ========================== 基础开关配置 ==========================
# 1. 开启 AOF 持久化(核心,保证数据完整性)
appendonly yes
# 2. 开启 RDB + AOF 混合持久化(AOF 头部嵌入 RDB 快照,兼顾恢复速度与数据完整)
aof-use-rdb-preamble yes
# 3. 关闭 RDB 自动触发(避免 RDB 与 AOF 刷盘/重写竞争 IO,RDB 仅用于手动全量备份)
save "" # 注释所有默认 save 配置,或用 save "" 覆盖
# ========================== AOF 关键配置 ==========================
# 1. AOF 文件名(按端口区分,避免多实例冲突)
appendfilename "appendonly-6379.aof"
# 2. 刷盘策略(平衡安全与性能,最多丢失 1 秒数据)
appendfsync everysec
# 3. 重写期间暂停刷盘(减少 IO 竞争,重写时仅写缓冲区,不阻塞主线程)
no-appendfsync-on-rewrite yes
# 4. 自动重写触发条件(避免文件膨胀,兼顾性能)
auto-aof-rewrite-min-size 64mb # AOF 最小 64MB 才触发重写(避免小文件频繁重写)
auto-aof-rewrite-percentage 100 # 比上次重写后体积增长 100%(翻倍)时触发
# 5. 允许加载截断的 AOF 文件(避免服务启动失败,后续手动修复)
aof-load-truncated yes
# ========================== RDB 关键配置(仅用于手动备份) ==========================
# 1. RDB 文件名(按端口区分)
dbfilename "dump-6379.rdb"
# 2. 关闭 RDB 压缩(手动备份时可按需开启,减少 CPU 开销;若追求文件小,可设为 yes)
rdbcompression no
# 3. 关闭 RDB 校验(手动备份场景,优先保证备份速度;若担心文件损坏,可设为 yes)
rdbchecksum no
# ========================== 通用存储配置 ==========================
# 1. 持久化文件存储路径(使用绝对路径,独立磁盘/SSD,避免与系统盘、业务盘竞争 IO)
dir "/var/redis/data/6379"
# 2. RDB 失败时禁止写入(避免数据丢失,生产环境推荐开启)
stop-writes-on-bgsave-error yes
场景 1:核心数据存储(如订单、用户余额,零丢失优先)
需求:数据丢失不超过 100ms,服务可用性 99.99%,恢复速度快。
# 1. AOF 刷盘策略升级为 near实时(牺牲部分性能,降低丢失风险)
appendfsync always # 每条命令刷盘,理论零丢失(需搭配 SSD 降低 IO 延迟)
# 2. 开启 AOF 重写时的紧急刷盘(避免重写期间数据堆积)
no-appendfsync-on-rewrite no # 重写时仍按 always 策略刷盘(IO 压力大,需用 SSD 承载)
# 3. RDB 手动备份频率提升(如每 6 小时一次,作为 AOF 故障的兜底)
# (通过 crontab 脚本执行:0 */6 * * * redis-cli BGSAVE)
# 4. 开启 AOF 文件校验(避免损坏文件加载)
aof-load-truncated no # 若 AOF 截断,拒绝加载,需手动修复(确保数据一致性)
配套措施:
- 使用 SSD 磁盘存储持久化文件(dir 路径指向 SSD 分区),appendfsync always 下 SSD 可将 IO延迟从毫秒级降至微秒级,减少主线程阻塞;
- 部署 Redis 主从集群,从节点开启 AOF(与主节点一致),避免主节点单点故障导致数据丢失。
场景 2:非核心缓存(如商品列表、热点数据,性能优先)
需求:数据可容忍丢失(如丢失 5 分钟内数据),服务 QPS 极高(10 万 +),避免持久化占用过多资源。
# 1. 关闭 AOF(减少刷盘、重写的性能开销)
appendonly no
# 2. 开启 RDB 自动触发(周期性快照,平衡性能与数据安全性)
save 300 100 # 5 分钟内修改 100 次触发 RDB(根据缓存更新频率调整)
save 600 10 # 10 分钟内修改 10 次触发 RDB(兜底)
# 3. 优化 RDB 性能(减少 CPU/IO 消耗)
rdbcompression no # 关闭 RDB 压缩(节省子进程 CPU 开销,文件体积略大)
rdbchecksum no # 关闭 RDB 校验(加载速度更快,缓存数据损坏影响小)
# 4. RDB 失败时允许写入(避免持久化失败导致服务不可写)
stop-writes-on-bgsave-error no
场景 3:超大规模数据(如 100GB+ 内存,恢复速度优先)
需求:数据量庞大,重启时需快速恢复服务,同时避免数据丢失过多。
# 1. 保留混合持久化(AOF 头部 RDB 快照,加速恢复)
aof-use-rdb-preamble yes
# 2. 调整 AOF 重写阈值(避免大文件频繁重写)
auto-aof-rewrite-min-size 256mb # AOF 达到 256MB 才触发重写
auto-aof-rewrite-percentage 150 # 体积增长 150%(即 1.5 倍)时触发
# 3. RDB 手动备份(每日凌晨执行,作为 AOF 恢复的兜底)
# (脚本:redis-cli BGSAVE && cp /var/redis/data/dump-6379.rdb /backup/redis/$(date +%Y%m%d).rdb)
# 4. 优化 AOF 加载速度(跳过冗余命令)
aof-load-truncated yes # 允许加载截断的 AOF,减少启动失败概率
配套措施:
- 持久化文件存储在 NVMe SSD 或分布式存储(如 Ceph),提升读写速度;
- 主从复制时,从节点先加载 RDB 快照,再同步增量命令,减少主节点压力。