尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Redis持久化详解:RDB、AOF与混合模式配置实战

Redis持久化详解:RDB、AOF与混合模式配置实战 1. 持久化到底解决了什么问题聊 Redis绕不开持久化。很多人刚接触 Redis 时有个根深蒂固的印象——这不就是个缓存嘛数据丢了重新查一遍数据库就行了。但在真实生产环境里Redis 早就不只是缓存了排行榜、分布式锁、秒杀库存、交易流水、用户会话哪个丢了都不好交代。尤其是内存里好不容易算出来的热点数据你指望它抗住一波流量结果一重启全没了那用户体验基本就是灾难现场。所以持久化的本质就一句话把内存里的数据想办法落到磁盘上让 Redis 重启之后还能恢复原样。这里不用纠结什么高深理论你只需要记住两件事一是持久化落的是什么二是怎么落最划算。前者看 RDB 和 AOF 的机制后者看你的业务对“丢多少数据可以接受”的容忍度。我见过不少 SRE 把持久化配置当成一件“配了就完事”的事结果真出事的时候复盘发现要么是save策略太激进把主线程卡死要么是 AOF 重写期间磁盘写满直接宕机。所以这篇我不打算只罗列配置项而是把 RDB、AOF、混合模式这三条路各自的工作机制、适用场景、坑点讲透再给出一套可以直接落地的组合方案。如果你正准备做 Redis 高可用改造或者线上 Redis 已经跑了一段时间但持久化这块一直没仔细看过这篇文章应该正合适。2. 环境准备与前置知识2.1 本机环境与版本选择在动手之前先把环境确认好。我这里用的是 CentOS 7.9 Redis 6.2.6这个版本算是 Redis 6.x 系列里比较稳的一个。Redis 7.0 之后对 AOF 机制做了重构把原来的aof-use-rdb-preamble变成了默认行为命令日志也统一走 RDB 格式的基线文件加增量日志思路更清晰了。如果你的版本是 7.x下面的配置项大部分仍然适用只是部分参数名有了变化我会在对应位置单独提一句。注意Redis 6.x 和 7.x 在 AOF 重写机制的内部实现上差异明显7.0 之后引入了 multi-part AOF多部分 AOF但对外暴露的appendonly、appendfsync配置语义基本一致不影响本文内容。我强烈建议你不要在 Windows 上直接跑生产 Redis。Redis 官方不支持 Windows虽然有第三方移植版本但性能、稳定性都存在不确定性特别是持久化涉及 fork、文件重命名、磁盘同步这些对系统调用依赖很强的操作Windows 环境的坑会非常多。生产环境老老实实用 Linux实在不行就 Docker 起一个# 拉镜像并启动把数据目录挂载到宿主机 docker network create redis-net docker run -d --name redis \ --network redis-net \ -p 6379:6379 \ -v /data/redis:/data \ redis:6.2.6 redis-server /etc/redis/redis.conf挂载数据目录这个动作不是为了方便而是为了让容器销毁后数据还在宿主机上。我见过不止一次容器一删数据全没的惨案。线上用 Docker 跑 Redis 时千万别把数据直接写在容器可写层里否则docker rm之后连救的机会都没有。2.2 核心概念扫盲先把几个基础概念统一一下口径后面讲细节时不再重复解释RDBRedis DataBase 文件内存数据在某个时间点的全量快照二进制格式恢复快占空间相对小。AOFAppend Only File记录写操作的追加日志按 Redis 协议格式保存恢复时需要重放日志。混合持久化以 RDB 格式作为基线再叠加增量 AOF 日志兼顾恢复速度和丢数据范围。写时复制Copy-On-WriteCOWRDB 持久化时 fork 子进程子进程共享父进程内存页只有父进程写入时才复制页面这也是 RDB 生成快照时不阻塞主线程的关键机制。fsync把内核页缓存中的数据真正刷到磁盘的操作决定 AOF 模式下最多丢多少数据。这些概念不搞懂后面配置参数就只能照抄出了问题也不知道去哪排查。3. RDB 快照全量持久化的扛把子3.1 RDB 的触发机制与参数精讲RDB 的配置文件在redis.conf里核心就是save参数。它的写法很直白save 900 1 save 300 10 save 60 10000这行的意思是900 秒内至少有 1 次写操作就触发一次快照300 秒内至少有 10 次写操作触发一次60 秒内有 10000 次写操作触发一次。三个条件是“或”的关系任何一个满足都会触发。生产环境我一般建议不要删掉默认配置而是按业务流量适当调整。如果你内存 32G、写入频率高60 秒 10000 次写其实很容易达到那 RDB 的生成频率就会很频繁磁盘 IO 压力会上去。除了save定时触发还有两个手动触发命令SAVE同步阻塞主进程直接生成快照期间 Redis 完全不可用。基本上生产环境没人敢用这个。BGSAVEfork 子进程后台生成快照主进程继续对外服务。这是正常使用的命令。RDB 生成的快照文件默认叫dump.rdb放在dir指定的目录下。启动时 Redis 会自动检查这个文件是否存在存在就自动加载恢复。所以你在测试环境改完配置BGSAVE手动落一次盘再重启数据就靠它恢复。3.2 fork 与 COW为什么快照不阻塞主线程这是 RDB 设计的核心。很多人以为BGSAVE是 Redis 把内存数据拷一份出来再写盘其实不是。Redis 调用fork()创建一个子进程子进程会复制父进程的页表然后子进程负责把内存数据写入临时 RDB 文件。父子进程共享物理内存页父进程继续正常接收请求。关键点来了——fork 的过程中子进程是阻塞的这个时间取决于内存页表的大小一般也就几十到几百毫秒。真正复杂的是后续的 COW当父进程收到写请求要修改某个内存页时操作系统会为父进程复制这个页面父进程改写副本子进程看到的还是旧页面。这意味着 fork 之后如果父进程写入非常频繁会有大量内存页被复制内存占用会短暂飙升。32G 内存的实例极端情况下可能需要额外预留 4-6G 给 COW 使用。这是我踩过的坑曾经在一个写入量很大的实例上配置了过短的save间隔结果每次BGSAVE期间内存都被 COW 机制推高触发 OOM 直接把 Redis 杀了。从那以后我配备 RDB 策略一定要看一眼used_memory_rss和系统的可用内存余量留足 COW 冗余。3.3 RDB 的触发条件有两个坑第一个坑配置了save 就完全关闭 RDB。很多人以为删掉 save 行就行其实不行如果配置文件里的默认 save 行还在Redis 还是会按默认条件触发快照。要关闭必须显式写save 。第二个坑stop-writes-on-bgsave-error参数。默认值是 yes意思是如果BGSAVE因为磁盘写满、权限问题失败了Redis 会把所有写操作直接拒绝。这个设计本意是防止磁盘坏了你还往里写数据导致后续数据全丢。但对业务来说Redis 突然变成只读这个伤害往往比丢快照更大。我的建议是如果你的 Redis 承担的是非常核心的写入链路且你有一套完整的监控告警能及时发现 BGsave 失败可以把这项设为 no让写操作继续等磁盘问题解决后再手动补一次快照。这个参数怎么取舍没有标准答案完全取决于你的业务对写入可用性和数据安全的权重。我倾向设 no 强告警因为 Redis 写失败引起的业务连锁反应往往比一次快照失败更致命。4. AOF 日志更细颗粒度的数据保障4.1 AOF 日志长什么样AOF 文件里存的不是普通的日志文本而是 Redis 协议格式的写命令。举个例子你执行SET user:1001 alice INCR page:view这两条命令会被追加到 AOF 文件里最原始的内容大概长这样*3 $3 SET $8 user:1001 $5 alice *2 $4 INCR $8 page:view这种格式的好处是恢复时直接用redis-check-aof验证格式由 Redis 伪客户端重放即可不依赖任何外部解析工具。坏处是文件体积会给常大因为每一秒的重复写命令都会逐条记录。4.2 appendfsync 三档选择AOF 的核心配置是appendfsync它决定命令写入内核缓冲后多久刷到磁盘。三档配置值行为最多丢数据性能影响always每条命令都 fsync最多丢 1 条命令最慢吞吐量明显下降everysec每秒 fsync 一次最多丢 1 秒数据兼顾性能与安全no交给操作系统决定刷盘时机最多丢数秒到数十秒不等最快但安全不可控生产环境绝大多数场景用everysec这也是 Redis 的默认值。always只有在对数据完整性有迷信级要求的极少数场景才会用而且实际性价比很低——单线程的 Redis 在always模式下每条写命令都要等磁盘确认吞吐量能掉一半以上很难接受。我遇到过有人把appendfsync设成no来追求极致性能结果一次服务器崩溃丢了几分钟数据直接引发业务事故。这档配置我只建议用在纯缓存、可完全重新构建的场景否则千万别碰。4.3 AOF 重写机制为什么 AOF 不会无限膨胀AOF 文件一直追加的话体积会越来越大。解决方式是重写rewrite。Redis 会 fork 一个子进程把当前内存数据以写命令形式重新生成一个最小的 AOF 文件再与旧文件中的增量命令合并。这个机制由两个参数控制auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb意思是当 AOF 文件体积较上次重写后增长了 100%且文件体积超过 64MB就触发自动重写。举个实际例子上次重写后文件是 200MB现在文件到 400MB 且超过 64MB 门槛就会触发重写。重写期间主进程照样对外服务子进程生成新文件父进程同时把新增的写命令写入重写缓冲区。等子进程写完父进程再把缓冲区里的增量命令补到新文件尾部最后原子替换。这个过程同样存在 COW 内存开销所以之前给 RDB 留内存余量的建议在 AOF 重写时同样适用。AOF 重写过程中如果 Redis 意外宕机旧 AOF 文件不会损坏Redis 启动时会自动加载旧文件丢失的是宕机前一小段时间的数据。重写机制本身具备一定的容错能力这一点比很多人想象得好。4.4 开启与关闭 AOF 的实际操作默认情况下 AOF 是关闭的。打开方式是在redis.conf里设置appendonly yes appendfilename appendonly.aof appendfsync everysec如果你要在不重启的情况下临时开启 AOF可以登录到 Redis 命令行执行CONFIG SET appendonly yes CONFIG SET appendfsync everysec然后手动触发一次重写把当前内存中的已有数据同步到 AOF 文件中BGREWRITEAOF这里有个很关键的注意点开启 AOF 之前最好先执行一次BGSAVE生成 RDB 快照。然后将 RDB 文件与 AOF 文件放在同一目录。Redis 启动时会优先加载 AOF 文件来恢复数据如果 AOF 文件损坏或内容缺失启动就会失败。所以你在切换持久化模式时一定要先确认 AOF 文件已经成功生成并且文件内容完好再把 RDB 文件删掉或移走否则启动时会得到错误提示。我自己就吃过这个亏在测试环境热开启 AOF没走 BGSAVE 和 BGREWRITEAOF 就直接重启结果启动失败一看日志是 AOF 加载失败最后用redis-check-aof修了半天才恢复。5. 混合持久化生产环境的最优解5.1 为什么需要混合先梳理一下两种模式各自的痛点RDB恢复快但两次快照之间如果宕机这期间写入的数据全会丢。AOF最多丢 1 秒数据但文件大恢复时重放几 GB 的写命令非常慢一个 4GB 的 AOF 文件重放可能要几十秒甚至几分钟期间 Redis 一直处于不可用状态。对于一个大内存的 Redis 实例重启恢复期间的不可用窗口在业务上往往比丢一点数据更致命。于是有了混合持久化。5.2 混合持久化的机制Redis 4.0 之后引入aof-use-rdb-preamble参数默认开启。开启之后AOF 文件的开头部分不再是一大堆命令日志而是一个 RDB 格式的内存快照这个快照后面再追加增量写命令。Redis 启动加载 AOF 时会先快速加载 RDB 部分恢复大部分数据再重放后面的增量命令补齐启动前的状态。混合模式的体验是既不需要一两分钟重放几 GB 命令又能把数据丢失控制在 1 秒以内。对我来说这是当前 Redis 持久化的最佳实践没有之一。Redis 7.0 之后把混合模式作为了默认策略新建实例基本不用额外配置。5.3 混合模式的实际配置如果你用 Redis 6.x需要确保配置aof-use-rdb-preamble yes这个参数只对 AOF 重写后的文件形态有影响。重写时生成的新 AOF 文件前面是 RDB 基线后面是增量命令。旧格式的 AOF 文件在启动时仍然可以正常加载不会因为开启混合模式就报错。需要注意的一点混合模式的 AOF 文件无法通过直接的文件查看命令看懂内容。文件前半部分不再是文本命令而是二进制格式的 RDB 数据。排查问题时不要拿tail去盯这个文件没有任何意义得用redis-check-aof或者看日志。5.4 我推荐的组合配置看完这么多机制你可能会问到底怎么配才合理分享一套我目前在多个生产环境里使用效果比较稳定的配置思路配置项推荐值原因save保留默认或适当放宽作为兜底应对 AOF 完全失效的极端场景appendonlyyes保证正常运行时最多丢 1 秒数据appendfsynceverysec性能和安全的平衡点aof-use-rdb-preambleyes加速重启恢复过程stop-writes-on-bgsave-errorno避免磁盘小故障导致整个 Redis 拒写auto-aof-rewrite-percentage100保持文件体积可控auto-aof-rewrite-min-size视数据量而定建议不要低于 256mb避免频繁重写这里的核心思路是用 AOF 保证数据安全用 RDB 兜底极端情况用混合模式加速重启恢复。三层配合起来既有安全余量又有恢复速度。6. 持久化实战从零到一的完整操作6.1 第一步确认当前持久化状态动手之前先看看当前配置到底是怎么样的redis-cli CONFIG GET save redis-cli CONFIG GET appendonly redis-cli CONFIG GET appendfsync redis-cli CONFIG GET aof-use-rdb-preamble同时看一下当前磁盘空间和数据目录df -h /data du -sh /data/redisAOF 和 RDB 文件都需要和磁盘 IO 打交道磁盘 IO 本身是共享的从info persistence可以看到最近一次持久化是否成功redis-cli info persistence重点关注这几项输出rdb_last_bgsave_status:ok最近一次 RDB 快照是否成功。aof_last_bgrewrite_status:ok最近一次 AOF 重写是否成功。aof_last_write_status:okAOF 正常写入是否能成功。rdb_last_cow_size最近一次快照 COW 的内存大小用来评估内存余量够不够。6.2 第二步配置持久化策略选定目录和文件名后在redis.conf中写清楚配置。下面是一份比较完整的示例# 数据目录 dir /data/redis # RDB 配置 save 900 1 save 300 10 save 60 10000 dbfilename dump.rdb stop-writes-on-bgsave-error no rdbcompression yes # AOF 配置 appendonly yes appendfilename appendonly.aof appendfsync everysec auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 256mb aof-use-rdb-preamble yes配置完成后重启 Redis 让配置生效redis-cli shutdown redis-server /etc/redis/redis.conf如果不想重启可以用CONFIG SET动态修改部分参数。但要注意dir、appendfilename这类参数不支持动态修改必须改配置文件重启。我的建议是配置文件统一管理避免线上配置漂移重启窗口提前和业务打招呼。6.3 第三步验证数据恢复流程光配好还不够得验证一下恢复流程真的能用。我的标准化验证方法向 Redis 写入一批测试数据带着时间戳和唯一标记。执行BGSAVE等待完成后记录dump.rdb文件大小。再写入一批新数据然后执行BGREWRITEAOF确认 AOF 文件正常生成。模拟宕机直接kill -9Redis 进程。重新启动 Redis检查启动日志中加载的是哪个文件以及加载耗时。用redis-cli检查之前写入的测试数据是否全部存在。检查info persistence中rdb_last_load_ok和aof_last_load_ok字段。只有走完这一整套流程并且数据完整才能说明持久化是真配好了。光看配置不演练等于没有。实际操作中你会发现启动日志会明确打印类似这样的加载信息DB loaded from append only file: 1.234 seconds看到这个就说明 AOF 加载成功耗时也能直接反映启动恢复速度是否符合预期。实测下来混合模式下加载一个几十 GB 的 AOF 文件耗时一般能控制在几秒到十几秒而纯 AOF 模式同体量可能要奔着一分钟去。6.4 第四步主从切换场景下的持久化联动如果你的 Redis 是高可用架构有主从节点这里要特别注意一个细节某种异常情况下从节点会触发自己的持久化流程它的磁盘和内存压力不容忽视。主库在BGSAVE生成 RDB 文件后会把 RDB 文件同步给从库从库加载 RDB 文件恢复数据即使从库本身没有开启持久化也会在加载时占用相当多的内存和 CPU。从库的全量重同步过程是主从架构下最常见的高峰期故障诱因。新从库加入或者从库网络闪断重连时主库要fork子进程生成 RDB 快照这个 fork 过程本身会阻塞主库主线程且 COW 内存开销巨大。所以在做主从扩容、替换从库之类操作之前一定要评估主库当前内存量和流量避免在写入高峰期触发全量重同步。我踩过最惨的一次在主库写入高峰正准备对从库做一次配置变更结果从库因网络抖动触发了全量重同步主库 fork 花了 1 秒多然后大量请求排队P99 延迟从 2ms 直接飙到 3 秒业务侧瞬间告警刷屏。自那以后我对主从节点变更的流程都是先避开峰值再逐个操作并且在变更前确认主库负载和内存余量都充足。7. 常见问题与排查技巧实录7.1 AOF 文件损坏怎么办Redis 启动时如果加载 AOF 文件失败会拒绝启动。这种时候不要慌用自带的修复工具先处理redis-check-aof --fix appendonly.aof它会扫描 AOF 文件截断末尾不完整的命令记录然后重建一个可用的文件。注意修复动作是有损的会丢掉文件末尾的一部分命令。在修复前先把原始文件备份一份cp appendonly.aof appendonly.aof.bak修复完后再启动 Redis确认数据完整性。如果丢的是末尾的命令说明那部分本身就是不完整的写操作实际影响有限但如果破坏发生在文件中部那可能丢的数据段就大了这种情况下最好结合主从节点的数据做对比恢复。7.2 RDB 持久化频繁失败怎么办info persistence里如果看到rdb_last_bgsave_status:err先排查以下几个点按出现频率排序磁盘空间不足。df -h一看就明了RDB 文件是瞬时生成的临时文件需要的空间约等于内存数据量要留足余量不要等到差几十 MB 才发现。磁盘 IO 饱和。如果 Redis 所在机器的磁盘本来就在被其他服务大量写BGSAVE写盘速度会非常慢fork 出来的子进程迟迟退不掉拖累整个系统。COW 内存不足。rdb_last_cow_size如果接近系统剩余内存下次 fork 就可能触发 OOM。这种情况要么加内存要么降低写入频率。7.3 AOF 文件为什么比 RDB 大这么多这是 AOF 的本质决定的。RDB 存的是数据状态AOF 存的是操作过程。同样一条INCR counter执行一万次RDB 最终只记录counter 10000这一个结果AOF 则要记一万条INCR命令。如果大量写入集中在少数几个 key 上AOF 的膨胀比例会非常夸张。解决方案也是现成的确保auto-aof-rewrite策略合理触发定期重写压缩。BGREWRITEAOF手动触发一次重写观察文件占比变化。对于极端场景可以在业务层合并写入例如把频繁的小写操作合并在管道中减少 AOF 记录条数。7.4 主从全量重同步风暴怎么避免这个问题在集群规模大、从库多的时候特别常见。一次网络抖动多个从库同时请求全量重同步主库要连续多次生成 RDB 快照每次都伴随 fork 和 COW直接把主库拖垮。几个实用手段错峰重连给从库设置合理的replication-backlog参数尽量用增量同步消化短暂断线如果重连后 backlog 不够触发全量那就避免所有从库同时重连。主库限流如果主库内存写入负载很高尽量降低持久化频率给从库全量重同步留出资源窗口。运维操作前评估替换从库、重启从库都算高风险操作不要扎堆做一个一个来。7.5 如何验证持久化的可靠性最后说一个很多团队都不会刻意去做、但真出事能救命的动作按时做恢复演练。每个月或每个季度挑一个业务低峰窗口找一台独立机器把备份的 RDB 和 AOF 文件拷贝过去启动一个临时 Redis 实例然后模拟业务查询验证数据完整性。这个演练成本很低但价值极高。一方面验证了持久化文件格式没有随版本变化而失效另一方面也量化了真实的恢复耗时提前暴露磁盘 IO 慢、文件体积失控、恢复时间过长等隐患。据我所知不少大厂 SRE 团队把持久化恢复演练列入例行巡检就是因为线上恢复失败的案例比比皆是——配置文件看着没毛病真到宕机拉起来时才发现 AOF 文件早就损坏了这种低级错误在演练里根本藏不住。8. 个人实操体会与避坑建议8.1 兜底思想要贯彻始终我不太信“一套配置走天下”但有一套兜底逻辑我建议你都配上不管你怎么看重 AOF都别把 RDB 完全关掉。RDB 文件在极端情况下起着最后的保命作用——比如 AOF 损坏且备份也没来得及做、或者用了多年突然发现 AOF 文件有潜在问题一份 RDB 备份能让你从磁盘上找回大部分数据。我甚至建议对核心 Redis 实例做“双备份”策略RDB 定时快照 AOF 实时日志两者同时启用物理文件定期归档到异地。这里的“异地”不是分布式同步而是桶、NAS、对象存储任意一种哪怕一天拷一次也比什么都不做强。8.2 监控指标清单持久化不是配完就结束的事需要持续盯指标。我日常必看以下几项info persistence中的rdb_last_bgsave_status和aof_last_bgrewrite_status。rdb_last_cow_size和aof_rewrite_cow_size评估 COW 内存开销。磁盘使用率超过 70% 就要预警超过 85% 必须扩容或清理。Redis 主线程阻塞时间latest_fork_usec如果经常超过 1 秒说明 fork 成本偏高需要考虑降低持久化频率或增加内存带宽。引入标准监控系统后这些指标统统做告警规则坏指标在影响业务之前就能被拦截住。8.3 最后分享一个小技巧如果你的 Redis 实例写入压力很大但重启时又希望恢复快可以尝试这样一个变通把 RDB 的生成频率主动调高同时让 AOF 承担“兜底增量”的角色。也就是说RDB 每隔几分钟生成一次AOF 记录期间的增量命令。启动时先加载 RDB再重放少量 AOF 增量恢复耗时和丢数据范围都会非常理想。这套做法本质上就是人为把混合持久化的“基线”频率调整成你想要的节奏实测在业务高峰期恢复场景下效果显著。我在实际搭建 Redis 持久化方案时前后调整过很多次参数组合最终稳定下来的方案就是上面表格里的那套。没有哪种策略是万能药关键是理解每种机制的特性结合自己的业务写入量、数据容忍度、磁盘能力做出平衡。希望这篇文字能帮你少走一些弯路不用把每个坑都亲自踩一遍。
返回列表