Redis 持久化与高可用实战:RDB/AOF 取舍、主从复制、哨兵故障转移与内存淘汰全流程

Redis 用在建站里的角色,以及为什么必须聊持久化和高可用

在个人站长圈里,Redis 通常扮演三个角色:缓存(撞库、会话、页面片段)、队列(异步任务、发信)、计数器(限流、PV/UV)。很多人把它当成"一个更快的数据库"直接装上就用,默认配置跑到底。直到某天服务器重启,登录态全部失效、限流计数归零、队列里的任务凭空消失,才发现 Redis 的持久化默认配得有多"随缘"。

本文从运维视角讲清楚:RDB 和 AOF 到底怎么选、主从复制怎么搭、哨兵怎么做到自动故障转移,以及内存打满时淘汰策略该怎么定。这些都是自建 Redis 绕不开的基本功。

RDB 与 AOF:两种持久化各自的脾气

Redis 有两套落盘机制,理解它们的特点比背参数重要。

RDB(快照)是把某一时刻的内存数据整体序列化成一个二进制文件。特点是文件紧凑、恢复快、适合备份和迁移;缺点是两次快照之间宕机会丢数据。默认配置里 save 900 1 的意思是"900 秒内至少有 1 个 key 变化就落一次快照",最坏情况下丢失的数据窗口可以接近 15 分钟。

AOF(追加日志)是把每一条写命令追加到日志文件,重启时重放。默认 appendfsync everysec 之下,最坏丢失 1 秒数据;如果设成 always 每条命令都 fsync,最坏不丢但性能骤降;设成 no 则交给操作系统,一般能到 30 秒。缺点是 AOF 文件比 RDB 大,恢复慢,且需要重写(rewrite)来压缩。

实用建议:两个都开。RDB 用于定期备份和快速恢复,AOF 用于把数据丢失窗口压到 1 秒。开启 AOF:

appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec

关于重写,需要设置两条触发线:

# 文件体积比上次重写后增长 100% 且超过 64MB 时重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

重写期间,Redis 会 fork 一个子进程把当前内存数据"重新写成"一份最简命令集,主进程同时把新写入追加到一个缓冲区,重写完成后合并。fork 的瞬间会占用相当于数据量大小的内存(copy-on-write 机制下实际更少,但写多的场景可能翻倍),所以在内存紧张的机器上要留足 maxmemory 与实际内存之间的余量,否则 fork 会因内存不足失败,日志里出现 Can't save in background: fork: Cannot allocate memory。

主从复制:读写分离与备份不阻塞主库

单机 Redis 有两个硬伤:宕机不可用、备份时会 fork 占用主库资源。加一个从库能同时缓解这两点。

在从库的配置里加一行即可:

# 从库 redis.conf
replicaof 10.0.0.11 6379
masterauth "主库密码"
replica-read-only yes

或者在运行时动态指定(重启后失效):

redis-cli -h 10.0.0.12 REPLICAOF 10.0.0.11 6379

复制建立时,从库会先和主库做一次全量同步(主库 fork 生成 RDB 再传输),之后进入增量同步,主库把后续写命令通过复制缓冲区持续推给从库。几个要盯的点:

  • repl-backlog-size(默认 1MB):主库的复制积压缓冲区。从库短暂断线重连时,如果断线期间主库的写入量超过了积压区,就得重新全量同步。写 QPS 高的站点务必调大,建议 64MB 起步。
  • client-output-buffer-limit replica:如果从库消费太慢导致主库积压超过限制,主库会主动断开从库。全量同步期间这个缓冲区尤其容易爆,可适当调大。
  • 从库默认只读,应用要读写分离的话,读走从库、写走主库,注意主从延迟——写后立刻读从库可能读不到,这就是经典的"复制延迟"问题,对一致性敏感的读必须走主库。

用 redis-cli -h 主库 info replication 能看到每个从库的 lag(延迟字节数)和 offset,lag 长期为 0 才比较健康。

哨兵(Sentinel):自动故障转移

主从只解决了"数据有副本",没解决"主库挂了谁来顶"。哨兵就是干这个的:它是一组监控进程,监控主从状态,主库宕机时自动选一个从库提升为新主库,并通知其他从库和客户端。

哨兵最少要 3 个(奇数个,避免脑裂),通常部署在 3 台机器上。Sentinel 配置文件 /etc/redis/sentinel.conf:

port 26379
sentinel monitor mymaster 10.0.0.11 6379 2
sentinel auth-pass mymaster 主库密码
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1

关键参数解读:

  • sentinel monitor mymaster <ip> <port> 2:最后的 2 是 quorum(法定票数),表示至少 2 个哨兵认为主库不可达才触发故障转移。
  • down-after-milliseconds 5000:5 秒探测不到响应就标记主观下线(SDOWN)。
  • parallel-syncs 1:故障转移后,新主库一次只让 1 个从库同步,减少同时全量同步对新主库的冲击。

故障转移的过程是:一个哨兵发现主库主观下线 → 询问其他哨兵 → 达到 quorum 后标记客观下线 → 选举领头哨兵 → 领头哨兵从从库中挑一个(按复制偏移量、优先级)提升为主 → 其余从库改指向新主。

客户端连接要通过哨兵获取当前主库地址,推荐用支持哨兵的客户端库,或者用 redis-cli -h sentinel -p 26379 sentinel get-master-addr-by-name mymaster 动态查询。不要在主库地址上写死 IP,否则故障转移后应用还不知道主库换了人。

内存管理与淘汰策略:别让 Redis 把机器撑爆

Redis 默认没有内存上限,数据一直涨,最终触发 OOM Killer 把整个进程干掉。必须显式设置:

maxmemory 2gb
maxmemory-policy allkeys-lru

淘汰策略是一道选择题,选错了会出大问题:

  • noeviction:内存满了直接对新写入报错。缓存场景绝不能用,会导致写入失败。
  • allkeys-lru:从所有 key 里淘汰最近最少使用的。纯缓存场景首选。
  • volatile-lru:只淘汰设置了过期时间的 key。适合"一部分 key 需要长期保留(如队列、计数),一部分是缓存"的混用场景,但要注意如果没给缓存 key 设 TTL,它就永远不会被淘汰。
  • allkeys-lfu / volatile-lfu:按访问频率淘汰,适合热点高度集中的场景(LRU 会误伤刚被访问一次的冷 key,LFU 更精准)。

配套还要注意过期 key 的删除机制:Redis 采用"惰性删除 + 定期抽样删除",所以内存占用和实际 key 数量之间会有延迟,看到 used_memory 降不下来先别慌,用 MEMORY DOCTOR 看建议。大 key(单个几 MB 的 Hash/List)是另一个常见祸根,删除或过期一个几 MB 的大 key 会阻塞主线程,强烈建议拆分成小 key,并用 redis-cli --bigkeys 定期扫描发现。

排查实战:三个高频命令

线上 Redis 出问题,先跑这三条:

# 1. 看整体状态:内存、连接数、命中率、是否正在重写/同步
redis-cli INFO all | grep -E "used_memory_human|keyspace_hits|keyspace_misses|connected_clients|rdb_bgsave_in_progress|aof_rewrite_in_progress|blocked_clients"

# 2. 实时看最耗时的命令(排查慢查询,默认超过 10ms 记录)
redis-cli SLOWLOG GET 10

# 3. 找出大 key
redis-cli --bigkeys

命中率用 keyspace_hits / (keyspace_hits + keyspace_misses) 估算,低于 80% 说明缓存设计或 TTL 有问题。慢查询日志里如果频繁出现 KEYS *、SMEMBERS 大集合、HGETALL 大 hash,那就是典型的用法错误,要用 SCAN 替代 KEYS,把大集合拆桶。

小结

自建 Redis 的运维闭环是:持久化(RDB + AOF 双开,把丢失窗口压到 1 秒)→ 复制(加从库做读和备份,调大 repl-backlog)→ 哨兵(3 个节点自动故障转移,客户端动态发现主库)→ 内存(设 maxmemory 和淘汰策略,警惕大 key 和 fork 内存)。这四步走完,Redis 才算从"一个能跑的缓存"变成"一个能扛事的组件"。至于更进一步的分片集群(Cluster),等单机主从的 QPS 或内存真的顶不住了再上,不必一开始就过度设计。

补充:持久化文件的备份与恢复演练

配好持久化只是第一步,"备份能不能恢复"才是关键。RDB 文件是 dump.rdb(路径由 dir 和 dbfilename 决定),AOF 是 appendonly.aof 及其 appendonlydir 目录。备份策略建议:

  • 每天定时把 RDB 文件复制到异地(对象存储或另一台机器),并保留最近 7 天的多份。别只留一份,也别和 Redis 放同一块盘。
  • 真正做一次恢复演练:把备份文件拷到一台干净的 Redis,启动后 DBSIZE 对比 key 数量、抽样 GET 几个关键 key,确认数据可用。没演练过的备份等于没有备份。
  • AOF 出问题时不要慌,Redis 提供了 redis-check-aof --fix appendonly.aof 修复工具,能砍掉文件尾部不完整的命令;RDB 对应的是 redis-check-rdb。

恢复的优先级是:如果同时有 RDB 和 AOF,Redis 默认优先加载 AOF(因为它更完整)。如果 AOF 损坏想改用 RDB 恢复,临时把 appendonly no 再启动即可。理解这个加载顺序,能在灾难恢复时少走弯路。

最后提醒一个容易被忽略的点:maxmemory 要留出足够余量。因为 fork 重写、复制全量同步、命令缓冲区都会额外吃内存,经验是把 maxmemory 设为物理内存的 60%~70%,剩下的留给系统和其他进程,否则连着触发重写就可能被 OOM Killer 盯上。

Last modification:October 9th, 2026 at 12:24 pm

Leave a Comment