主题
Redis 生产踩坑
一句话:这些故障的共同点是把「单线程 + 有限内存」当成无限资源;现象可能都是「Redis 卡住/不可用」,但根因和处置完全不同。
适用版本:Redis 7.x / 8.x。涉及旧版本的命令会单独标注。
为什么需要
Redis 把复杂度藏在了「快」后面:命令默认在主线程执行,内存没有硬上限时由操作系统兜底,主从复制默认异步。这三件事在低峰期看不出来,一到写入高峰、大 Key、主从切换就集中爆发。下面按「现象 → 证据 → 根因 → 处置」整理五类高频问题,目的是排障时知道先看哪个指标。
核心机制与处置
一、RDB fork 导致内存翻倍 OOM
现象:Redis 进程突然消失,监控显示内存短时间冲到平时的 1.5–2 倍,系统日志有 OOM Killer 记录。
证据:INFO memory 的 used_memory 与 used_memory_rss 差距拉大;INFO stats 的 latest_fork_usec 很高;INFO persistence 显示 rdb_bgsave_in_progress:1。
根因:BGSAVE fork 后父子共享内存页,主线程继续写入会触发 COW;写入越频繁,被复制的页越多,物理内存可能接近翻倍。fork 本身还要复制页表,大实例会有几十到几百毫秒停顿。
处置:maxmemory 不超过物理内存 50%;自动 RDB 用 save "" 关闭,低峰手动 BGSAVE;vm.overcommit_memory=1;关闭透明大页;容器按 cgroup limit 算内存,不按宿主机算。
二、KEYS * 阻塞整个实例
现象:执行 KEYS "user:*" 后 Redis 卡住数秒到十几秒,所有客户端超时。
证据:SLOWLOG GET 10 里出现 KEYS;INFO commandstats 里 cmdstat_keys 的 usec_per_call 异常高。
根因:KEYS 是 O(N) 全库遍历,单线程执行期间不处理其他命令。百万级 key 就可能阻塞数秒(经验值,取决于 key 数量和模式匹配成本)。
处置:生产禁用 KEYS,用 SCAN 游标分批遍历;配置里 rename-command KEYS "";已经执行且卡住时只能等,单线程命令无法中断。
三、大 Key 删除卡主线程
现象:DEL 一个几百万元素的集合后,P99 延迟瞬间跳到数秒。
证据:SLOWLOG 里有 DEL;LATENCY LATEST 或客户端监控出现与删除时间吻合的尖峰。
根因:DEL 同步释放每个元素,元素越多,free() 次数越多,主线程被独占。耗时不可控,取决于分配器和内存布局。
处置:用 UNLINK(4.0+)把释放交给后台线程;4.0 以下用 SCAN + 分批 DEL;设计上拆分大 Key;开 lazyfree-lazy-expire/lazyfree-lazy-eviction/lazyfree-lazy-server-del,但客户端显式 DEL 仍是同步的。
四、主从切换丢数据
现象:Sentinel/Cluster 切换后,新主比旧主少了最近几秒的写入。
证据:INFO replication 对比切换前后的 master_repl_offset 与 slave_repl_offset;业务侧对账发现缺口。
根因:主从复制是异步的,主节点写完内存就返回,不等待副本确认。旧主宕机时,尚未同步的写入随旧主一起丢失。
处置:WAIT numreplicas timeout 自 3.0 起提供,可让写入等待指定数量的副本确认;min-replicas-to-write(默认 0)和 min-replicas-max-lag(默认 10 秒)可在副本跟不上时拒绝写入。两者都只能降低概率:WAIT 的确认发生在副本内存层,不保证已落盘。强一致场景换 CP 系统,或接受缓存语义的短窗口。
五、热点 Key 打满单节点
现象:Cluster 中某个分片 CPU 100%,其他分片很低;客户端超时后重试,情况继续恶化。
证据:CLUSTER NODES 定位节点;对比各主节点 CPU/QPS;redis-cli --hotkeys(需 LFU)或业务埋点找到具体 key。
根因:一个 key 只属于一个 slot/节点,访问量集中时单节点先到顶;同节点其他 key 被连带拖慢。
处置:本地缓存(TTL 1–5 秒);副本打散(2–4 份,随机读,写入更新全部副本);可累加数据分片;极端情况限流降级。加节点不会自动分走单个 key 的流量。
最小上手
bash
redis-cli
# 内存与 fork
INFO memory
INFO stats | grep -E "latest_fork_usec|evicted_keys"
INFO persistence
# 慢命令与复制状态
SLOWLOG GET 10
INFO replication
# 热点与大 Key
CONFIG SET maxmemory-policy allkeys-lfu
redis-cli --hotkeys
redis-cli --bigkeys
# 安全删除
UNLINK big:key生产动作
- 内存:
maxmemory显式设置为物理内存的 50% 以下;纯缓存allkeys-lru,混合存储volatile-lru且缓存 key 都带 TTL。 - 持久化:
save ""关闭自动 RDB,低峰手动BGSAVE;vm.overcommit_memory=1,关闭 THP。 - 命令:
rename-command KEYS "";遍历用SCAN;删除大 Key 用UNLINK。 - 复制:关键写评估
WAIT或min-replicas-*的可用性代价;监控 offset 差和lag。 - 热点:客户端埋点优先于
--hotkeys;本地缓存 + 副本 + 限流组合使用。
边界
WAIT不是持久化:只等副本在内存中确认,副本随后崩溃仍可能丢。min-replicas-*会牺牲可用性:副本故障时主节点可能拒绝所有写入,要确认业务能接受。UNLINK不是瞬间完成:后台释放任务堆积时,内存不会立刻下降。--hotkeys/--bigkeys都有开销:全库扫描,低峰执行,或从副本上跑。- 经验值不是承诺:fork 停顿、
KEYS阻塞时长、DEL耗时都随数据量和硬件变化,排障以现场指标为准。
讲清楚
Redis 生产故障大多不是「Redis 本身有 bug」,而是把单线程和内存当成了无限资源。fork 时 COW 会让内存接近翻倍;
KEYS和DEL大 Key 会把 O(N) 操作压到主线程;异步复制让主从切换必然有丢数据窗口;热点 Key 则让一个节点先到顶。排障时先看指标再动手:内存看
INFO memory和latest_fork_usec,卡顿看SLOWLOG,丢数据看 offset 差,热点看分片 CPU 和--hotkeys。处置手段各有代价,尤其是WAIT和min-replicas-*,是拿可用性换一致性,不是免费的。