Skip to content

Redis 断电就丢,怎么在"快"和"不丢"之间权衡?—— 持久化与高可用 ​

属于 S2 Redis 深入 · 第二篇 上一篇:数据结构底层 下一篇:缓存问题与一致性

Redis 数据在内存里,服务器一重启,数据就没了。要让它"记得住",就得把内存数据落到磁盘——这就是持久化。而单机持久化还不够,机器会坏,所以还要主从、哨兵、集群来保证可用性。这一篇把这两条线讲清。

持久化:RDB 快照 vs AOF 日志 ​

Redis 提供两种持久化,思路截然不同。

RDB(快照) 是"某一时刻给内存拍张照",把全量数据写到一个 .rdb 文件。它靠 bgsave 后台 fork 一个子进程来做,父进程继续服务。这里有个关键机制——COW(写时复制):fork 出来的子进程和父进程共享内存,只有数据页被修改时才复制,所以快照期间内存不会翻倍。RDB 的优点是文件紧凑、恢复快,缺点是两次快照之间的数据会丢(非实时)。

AOF(追加日志) 是"每次写命令都追加到日志文件",类似 redo log 的"先写日志"。它有个刷盘策略选项(appendfsync):always 每条都刷(最安全最慢)、everysec 每秒刷一次(默认,最多丢 1 秒)、no 交给系统(最快最不安全)。AOF 会越写越大,所以有 AOF 重写:后台 fork 子进程,把无效命令压缩成等价的最小命令集。

两者怎么选?追求恢复快、能接受分钟级丢数据,用 RDB;追求秒级安全,用 AOF everysec;想兼顾,4.0 之后有混合持久化(AOF 重写时把当前数据以 RDB 格式写开头,之后增量 AOF 追加)。

主从复制:数据冗余 + 读写分离 ​

持久化解决"重启不丢",但解决不了"机器坏了怎么办"。主从复制让从库复制主库的数据,主库挂了从库能顶上,也能分担读压力。

复制的流程分两步:首次是全量同步(主库 bgsave 生成 RDB 发给从库,期间的增量命令先存进缓冲区再补),之后是增量同步(主库把 repl_backlog 这个环形缓冲区里的命令发给从库)。从库默认只读。

哨兵:自动故障转移 ​

主从解决了数据冗余,但主库挂了还得手动把从库提上来。哨兵(Sentinel)就是干这个的——它是一组监控进程,职责是:监控主从节点、发现异常告警、以及自动把宕机主库的某个从库提升为新主库。

这里有两个关键概念:SDOWN(主观下线) 是单个哨兵认为节点挂了(可能是网络抖动误判),ODOWN(客观下线) 是多数哨兵都认为挂了才真判定。故障转移时,多个哨兵要先选出一个 leader 来执行。所以哨兵至少 3 个(奇数)才能可靠达成共识。

Cluster:数据分片 ​

主从 + 哨兵解决的是"高可用",但单机内存有限,数据量大了存不下。Cluster 解决的是"分片"——把数据分散到多个节点。

Cluster 把数据空间划成 16384 个哈希槽,slot = CRC16(key) % 16384,每个节点负责一部分槽。访问一个 key 时,如果槽不在本节点,会返回 MOVED(带目标节点地址,客户端永久更新路由)或 ASK(槽正在迁移,临时跳转)。

分片带来一个限制:跨槽的多 key 操作不支持(比如 mget 落在不同槽的 key),要用 hash tag({user}:1、{user}:2 保证同槽)来规避。


串起来 ​

"快"和"不丢"是一对矛盾:RDB 快照和 AOF 日志在持久化的两个极端之间提供选择,混合持久化取平衡;主从复制做数据冗余和读写分离,哨兵在宕机时自动切换,Cluster 在数据量超单机时横向分片。这一层一层,都是同一个问题——让一个内存数据库在故障面前依然可用。

下一篇讲缓存问题与一致性:Redis 最常被用在"缓存"角色,而缓存会带来穿透、击穿、雪崩三大问题,以及"缓存和数据库怎么保持一致"这个经典难题。

持续学习,持续构建。