机房闪断三分钟,缓存从零开始
接着讲大促系列的事故。那次机房闪断,Redis 进程重启了,几百万个缓存 key 灰飞烟灭。重启完成的那一刻,缓存命中率从 98% 直接归零,上万请求同时扑向数据库——第 6 篇讲的「冷启动二次雪崩」,我们真真切切体验了一遍。那次之后我记住了两件事:一是缓存实例的持久化必须配置到位,二是内存数据想活过断电,就得提前把账记在磁盘上。
Redis 提供两条记账思路:RDB 是「拍快照」,每隔一段时间把全量数据拍成一张二进制照片;AOF 是「记流水」,每条写命令都记进日志,重启时从头重放一遍。一个管存量、一个管增量,取舍贯穿始终。
RDB:fork 子进程,拍一张全量快照
RDB 的执行者是 fork 出来的子进程,主进程继续服务,这就是 bgsave 不阻塞读写的原因。而 fork 的魔法在于操作系统的写时复制:父子进程一开始共享同一批内存页,谁写谁复制一份,子进程看到的永远是 fork 那一瞬间的数据快照,稳稳地往磁盘写。
# redis.conf:满足任一条件自动 bgsave
save 900 1 # 900 秒内有 1 次写
save 300 10 # 300 秒内有 10 次写
save 60 10000 # 60 秒内有 1 万次写
dbfilename dump.rdbRDB 的优点很硬:二进制文件紧凑,传输和恢复都快——重启时直接把快照读进内存,几个 G 的数据几十秒完事。缺点同样直接:两次快照之间的写入,宕机就没了。另外 fork 本身虽然「瞬时」,但要复制页表,几十 G 的大实例会有明显毛刺;写越密集,写时复制额外占用的内存越多,极端情况下内存接近翻倍。
AOF:每条写命令都记账
AOF 走的是另一条路:把每条写命令以文本形式追加进日志,重启时逐条重放,数据就回来了。有个值得咂摸的细节:AOF 是写后日志——先执行命令、再记日志。好处是不阻塞当前写操作,还能避免把执行失败的错误命令记进账本;代价是命令执行完、日志没落下时宕机,这条数据就丢了。
丢多少,取决于日志什么时候真正刷到磁盘,由 appendfsync 控制:
| 配置 | 刷盘时机 | 丢失窗口 | 性能 |
|---|---|---|---|
| always | 每条命令都刷盘 | 几乎不丢 | 最差,磁盘扛不住高写入 |
| everysec | 由后台线程每秒刷一次 | 最多丢 1 秒 | 折中,生产推荐 |
| no | 交给操作系统决定 | 不可控 | 最好,但别这么配 |
AOF 还有个成长烦恼:同一个 key 改一百次,日志里就躺着一百条命令,文件越来越肥,恢复越来越慢。解法是 AOF 重写——还是 fork 子进程,按当前内存里的最终状态生成一份「最小命令集」,一百次修改压成一条 SET。重写期间的新写入由缓冲区兜着,不会丢。
# redis.conf:开启 AOF 并配置自动重写
appendonly yes
appendfsync everysec
# 文件比上次重写后大 100% 且超过 64mb 时触发重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb混合持久化:快照开头,流水续尾
RDB 恢复快但丢得多,AOF 丢得少但恢复慢——4.0 的混合持久化把它们缝在了一起:重写 AOF 时,先以 RDB 格式写入当前全量数据做文件头,之后的增量写命令继续以 AOF 格式追加。重启恢复时,RDB 头几秒加载完,再重放少量增量日志,快和全兼得。开启只需一行:
# redis.conf:AOF 重写时使用 RDB 格式的文件头
aof-use-rdb-preamble yes两种方案放在一起看:
| 维度 | RDB | AOF |
|---|---|---|
| 本质 | 某一刻的全量快照(二进制) | 每条写命令的流水(文本) |
| 数据安全 | 两次快照之间的写入会丢 | everysec 最多丢 1 秒 |
| 文件体积 | 小,紧凑 | 大,依赖重写瘦身 |
| 恢复速度 | 快,直接加载 | 慢,逐条重放 |
| 性能影响 | fork 瞬间毛刺 + 写时复制 | 持续的 fsync 磁盘开销 |
怎么选:看 Redis 在你架构里扮演谁
- 纯缓存,数据随时能从数据库重建:RDB 就够,甚至可以只开低频快照;重启后靠回源预热,别为「丢了也无所谓」的数据付出 AOF 的持续磁盘开销。记得第 6 篇的教训:重启后先预热再放量,防冷启动雪崩。
- 当准存储用(会话、排行榜):混合持久化 + everysec,恢复快、丢失少,绝大多数场景的最优解。
- 一条都不能丢的数据:说真的,别把它放 Redis。持久化再强也扛不住磁盘和机器一起没,关键数据以数据库为准,Redis 只做加速。
补一个运维细节:线上执行过 FLUSHALL 这种高危命令的话,RDB 和 AOF 会忠实地把「清空」也记录下来——所以清库不只是数据没了,连后悔药都被涂掉了一半。建议在配置里用 rename-command 把这类命令改名禁用,真出事时 AOF 文件还能用 redis-check-aof --fix 抢救到最后一条完整的命令。
一句话总结:RDB 管快、AOF 管全、混合持久化管「既要又要」,而选型的锚点只有一个——Redis 在你的架构里是缓存还是存储。下一篇聊聊主从复制:数据怎么从主库流向从库?全量同步和增量同步的分界在哪?主从延迟又该怎么办?下篇见。
咖啡凉了,记得趁热喝。
评论 (0)