:RDB 与 AOF 持久化,重启之后数据还在吗)
个人主页 我不会起名字322 欢迎各位大佬莅临其他栏目 技术栈学习笔记 其他栏目 力扣Hot100题目解析 其他栏目 Go项目学习笔记 Redis 入门四RDB 与 AOF 持久化重启之后数据还在吗文章目录Redis 入门四RDB 与 AOF 持久化重启之后数据还在吗一、内存数据库为什么要考虑落盘二、RDB给内存拍一张全量快照1. 三种触发方式2. fork 与写时复制为什么 bgsave 不卡主线程3. RDB 的取舍三、AOF把每一条写命令记下来1. appendfsync 的三档取舍2. AOF 重写别让文件无限膨胀3. AOF 文件坏了怎么修四、混合持久化RDB 打底 AOF 增量五、RDB 与 AOF 对比六、备份与恢复实操1. 打一份本地冷备2. 远程拉一份 RDB3. 恢复把文件放回去再启动4. 停机迁移的大致步骤七、生产上怎么选小结上一篇我们用 Spring Boot 把 Redis 接了起来也把 RedisTemplate 那串看不懂的序列化乱码收拾干净了。代码能跑通之后很多人心里会冒出一个更朴素的疑问Redis 的数据全在内存里那我重启一下服务或者机房突然断电刚写进去的东西还在不在答案取决于落盘这件事你有没有配置明白。这一篇就把 Redis 的两套持久化机制拆开讲清楚顺带把备份与恢复的实操步骤完整走一遍。一、内存数据库为什么要考虑落盘Redis 快核心原因之一就是读写都在内存里完成绕开了磁盘 IO。但内存有个天然短板进程一退出、操作系统一重启这块内存就被回收数据随之消失。持久化要做的就是给内存里的数据在磁盘上留一份副本让进程下次启动时能照着副本把内存重新填回来。它的价值至少体现在三个层面一是服务重启或崩溃后能自愈二是硬件损坏、磁盘报废时能从备份里把数据捞回来三是做数据迁移时搬一个文件远比把 key 一条条读出来再写进去省事。这里先分清一个概念持久化解决的是数据别丢它并不解决服务别挂。二、RDB给内存拍一张全量快照RDBRedis Database的思路非常直白在某个瞬间把整个数据集做一次全量快照以紧凑的二进制形式写进一个文件默认文件名是 dump.rdb。1. 三种触发方式save在主线程里同步执行做完之前 Redis 不处理任何其他请求。实例内存大的时候可能卡住好几秒生产环境基本不用。bgsavefork 出一个子进程由子进程负责写文件父进程继续接收请求只在 fork 那一小段时间有停顿。自动规则save m n表示在 m 秒内至少有 n 个 key 发生变化就自动触发一次 bgsave。此外主从做全量复制时主节点会生成 RDB 再发给从节点执行shutdown关闭实例时也会做一次落盘。# 900 秒内至少有 1 个 key 被改动触发一次 bgsave save 900 1 # 300 秒内至少有 10 个 key 被改动 save 300 10 # 60 秒内至少有 10000 个 key 被改动 save 60 10000 # 快照文件名与存放目录 dbfilename dump.rdb dir /var/lib/redis # 是否对生成的 RDB 做 LZF 压缩默认开启 rdbcompression yes如果想彻底关掉自动快照把规则清空即可save 。注意 RDB 文件会落在dir指向的目录下这个目录同时也是后续备份、恢复时最关键的定位信息。# 同步生成快照会阻塞当前实例127.0.0.1:6379save OK# 后台生成快照命令立刻返回127.0.0.1:6379bgsave Background saving started# 运维最该看的两条上次快照是否成功、最近一次 fork 耗时多少微秒127.0.0.1:6379info persistence127.0.0.1:6379info stats# 运行期临时改目录或文件名注意重启后失效要写进配置文件才持久127.0.0.1:6379configsetdir/data/redis127.0.0.1:6379configsetdbfilename dump.rdb2. fork 与写时复制为什么 bgsave 不卡主线程bgsave 的停顿只出现在 fork 这一刻。fork 会让内核给子进程复制一份父进程的页表子进程从此看到的是一份定格在 fork 瞬间的内存视图。真正的内存页并不会立刻被复制父子进程先共享同一批物理页只有当父进程要修改某一页时内核才把那一页单独复制一份交给父进程用——这就是写时复制Copy-On-Write。所以 fork 通常很快但如果实例占用内存极大、页表本身就很庞大fork 也可能耗费几十甚至上百毫秒这个数字可以直接用info stats里的latest_fork_usec观察。另外还要留意一点快照执行期间如果写入很密集被复制出来的页会持续增加内存占用可能明显上涨容量规划时得给这种膨胀留出余量。3. RDB 的取舍拿得出手的地方文件是紧凑二进制体积小非常适合做冷备和整机迁移恢复时把文件直接读进内存速度远快于 AOF对主进程性能干扰小。需要接受的地方两次快照之间写入的数据会丢fork 和写时复制会带来时间与内存开销RDB 是二进制格式跨大版本尤其是降级存在兼容风险。三、AOF把每一条写命令记下来AOFAppend Only File走的是另一条路把所有会改变数据的写命令按顺序追加到文件末尾。恢复时把文件里的命令从头重放一遍内存就回到了原来的状态。默认文件名 appendonly.aof存放目录同样由dir决定。AOF 默认是关闭的需要显式打开。# 打开 AOF appendonly yes appendfilename appendonly.aof # 刷盘策略默认就是 everysec appendfsync everysec命令并不是直接落到磁盘上的。Redis 先把它写进 aof_buf 缓冲区再按appendfsync的策略决定什么时候调 fsync 真正刷盘。多这一层缓冲的意义在于单线程模型下如果每条命令都同步等磁盘瓶颈会立刻从内存转移到磁盘 IO。1. appendfsync 的三档取舍always每个写命令都立刻 fsync。丢失窗口最小代价也最大普通机械盘上可能只能撑住几百 TPS固态盘还要额外考虑写入寿命。除非数据极其关键一般不选。everysec默认值也是绝大多数场景的推荐值。后台线程每秒刷一次性能损失很小理论上最多丢 1 秒的写入。noRedis 不主动 fsync完全交给操作系统调度。吞吐最高但一次宕机可能丢掉好几秒甚至更多的数据缓冲区被写满时还可能反过来拖慢写入生产上慎用。2. AOF 重写别让文件无限膨胀AOF 只做追加文件必然越来越大。同一个 key 被 set 了一百次文件里就躺着九十九条已经作废的历史。Redis 用重写来解决这个问题而且它是照着当前内存里的数据反向生成一组最小的写命令而不是去读旧文件做整理。重写后能瘦下来的原因有几条已经过期的数据不会再写进去被覆盖或被删除的旧命令直接不生成只保留最终状态对同一个集合的多次操作可以合并成一条。文件小了磁盘占用下降重启重放的速度也更快。# AOF 体积至少达到 64MB 才考虑重写 auto-aof-rewrite-min-size 64mb # 且当前体积比上次重写完成后增长了 100% 才触发 auto-aof-rewrite-percentage 100# 手动触发一次 AOF 重写同样不阻塞主进程127.0.0.1:6379bgrewriteaof Background append onlyfilerewriting started重写的底层机制和 bgsave 类似也靠 fork 子进程完成。子进程拿着 fork 时刻的内存视图生成新文件父进程在这期间收到的写命令一边照常追加进旧 AOF保证老文件始终可用一边额外记进 AOF 重写缓冲区。子进程写完后父进程把重写缓冲区里的增量补进新文件最后用新文件替换掉旧的。所以重写期间的写入不会丢。3. AOF 文件坏了怎么修如果断电或磁盘写满导致 AOF 尾部只写了一半Redis 启动时可能会直接拒绝拉起。这时先用官方工具看一眼再决定怎么处理。# 只做检查不改文件输出是否完整redis-check-aof appendonly.aof# 从第一个出错的位置开始截断只保留前面完整可用的命令redis-check-aof--fixappendonly.aof# RDB 坏了也有对应工具会给出错误信息和大致出错位置redis-check-rdb dump.rdb要清楚--fix的本质是砍掉后半段它能救回大部分数据但出错点之后的内容就永久没了。所以修复前一定先复制一份原始文件留底不要在唯一的文件上直接操作。四、混合持久化RDB 打底 AOF 增量Redis 4.0 之后多了第三种选择由aof-use-rdb-preamble控制4.0 中默认关闭5.0 起默认开启具体以手上的 redis.conf 为准。它没有发明新格式而是在 AOF 重写的那一刻做了一件事把内存数据先按 RDB 的二进制格式写到新 AOF 文件的开头之后新产生的写命令再以原来的文本格式追加在后面。appendonly yes # 让 AOF 文件以 RDB 格式开头即混合持久化 aof-use-rdb-preamble yes好处是两头的好处都占加载时先读前半段的 RDB速度接近纯 RDB剩下的增量命令再重放丢失窗口又和 AOF 一样小。代价是文件开头那部分不再是可以直接阅读的文本命令排查问题时不那么直观而且它依赖重写动作触发如果 AOF 一直没重写过文件仍然是纯 AOF 的样子。五、RDB 与 AOF 对比维度RDBAOF记录内容某一时刻的全量数据快照所有写命令的追加日志文件格式压缩二进制文本命令混合持久化下开头为 RDB触发方式save / bgsave / save m n 规则 / shutdown / 主从全量复制打开 appendonly 后自动记录重写靠 bgrewriteaof 与自动规则丢失窗口两次快照之间可能长达数分钟everysec 下最多 1 秒always 下基本不丢恢复速度快直接把文件读进内存慢需要逐条重放命令文件体积小偏大重写后才收缩性能影响fork 瞬间有停顿快照期间可能有 COW 内存膨胀everysec 影响很小always 影响明显适合场景冷备、整机迁移、能容忍少量丢失数据敏感、要求丢失窗口极小六、备份与恢复实操1. 打一份本地冷备# 1) 先确认文件到底会生成在哪个目录、叫什么名字127.0.0.1:6379config getdir1)dir2)/var/lib/redis127.0.0.1:6379config get dbfilename1)dbfilename2)dump.rdb# 2) 在业务低峰期主动触发一次快照不要用 save127.0.0.1:6379bgsave Background saving started# 3) 确认这次快照确实成功了再动文件127.0.0.1:6379info persistence# 4) 复制到备份目录文件名带上时间戳方便回滚cp/var/lib/redis/dump.rdb /backup/redis/dump-$(date%F-%H%M).rdb只放在同一台机器上的备份等于没有备份——机器一挂数据和副本一起走。至少要同步到另一台主机或者对象存储并且最好在脚本里加校验文件大小是否合理、redis-check-rdb能不能通过都过了再上报成功。2. 远程拉一份 RDB不想登录服务器也能备份redis-cli 自带--rdb参数它会向目标实例发起一次同步请求把收到的 RDB 流直接写到本地文件。# 把远端实例的数据拉到本地存成一个 rdb 文件redis-cli-h10.0.0.12-p6379-ayourpassword--rdb/backup/redis/remote-dump.rdb这条命令背后是一次全量同步对大实例而言会带来 CPU、IO 和带宽开销同样不要挤在业务高峰期做。3. 恢复把文件放回去再启动# 1) 先停掉 Redis避免运行中文件被替换systemctl stop redis# 2) 从配置文件确认目录与文件名grep-E^(dir|dbfilename|appendonly)/etc/redis/redis.conf# 3) 把备份文件放回 dir 指向的目录并改回约定文件名cp/backup/redis/dump-2025-07-12-0300.rdb /var/lib/redis/dump.rdbchownredis:redis /var/lib/redis/dump.rdb# 4) 启动并验证数据量systemctl start redis redis-cli dbsize这里有个特别容易踩的坑如果实例同时开着 AOF启动时会优先用 appendonly.aof 来重建数据你放回去的 dump.rdb 根本不会被读取。所以打算用 RDB 做恢复或迁移时先把目标实例的 AOF 关掉改配置appendonly no或临时CONFIG SET appendonly no确认数据无误后再决定要不要重新打开。反过来如果是从 AOF 恢复就把 appendonly.aof 放回dir目录启动后 Redis 会自动重放里面的命令AOF 是纯文本必要时还能人工检查甚至删掉最后几条残缺命令再启动。AOF 体积大、重放慢恢复时间通常比 RDB 长不少切换机器时要有心理预期。4. 停机迁移的大致步骤# 源实例关掉 AOF手动做一次干净快照并确认结果redis-cli-h10.0.0.12 configsetappendonly no redis-cli-h10.0.0.12 bgsave redis-cli-h10.0.0.12 info persistence# 把文件拷到目标机器scp/var/lib/redis/dump.rdb root10.0.0.20:/var/lib/redis/dump.rdb# 目标实例先校验文件再启动redis-check-rdb /var/lib/redis/dump.rdb systemctl start redis redis-cli-h10.0.0.20 dbsize迁移期间要停写否则源端在快照之后产生的新数据不会出现在文件里。业务停不下来就别用这种冷迁移方式改用主从复制在线同步更稳妥。七、生产上怎么选说到底选哪种持久化就是在回答一个问题这套业务最多能容忍丢多少数据丢几秒都无所谓且更看重恢复速度可以只开 RDB把 save 规则调密一些。缓存类数据大多属于这一类数据本来就能回源重建。完全不能接受丢数据订单、账户、任务状态这类RDB 与 AOF 一起开appendfsync everysec起步重要到极致再考虑 always同时打开混合持久化。只开 AOF 不太推荐少了 RDB 就等于少了一份适合做冷备和整机迁移的全量快照而 AOF 的重放速度又慢极端情况下还可能遇到 AOF 自身的实现问题。纯内存、重启即重建的场景也可以把两种都关掉省下磁盘开销和 fork 带来的抖动。最后还有一句话必须说清楚持久化不等于高可用。它只保证磁盘上有一份数据副本并不保证服务不中断——单机 Redis 崩了从重启到数据加载完成这段时间服务就是不可用的硬盘损坏时更是直接起不来。要解决服务别挂得靠主从复制、哨兵或者集群来提供副本和故障转移那是另一条线上的事情。小结这一篇我们把数据别丢这条线走完了RDB 用定期全量快照换恢复速度AOF 用逐条记录换更小的丢失窗口混合持久化把两者的优势拼在一起再配上 bgsave 触发快照、文件校验、异地存放才算有一套真正能救命的备份方案。不过持久化只回答了数据还在不在内存终究是有限的。当 Redis 的内存被写满时它该淘汰谁、保留谁为什么线上偶尔会出现大量 key 在同一时刻集体失效把后面的数据库瞬间打穿下一篇我们聊过期与淘汰策略以及缓存穿透、击穿、雪崩这三个绕不开的坑。