文章

Redis 排错调优速查

Redis 排错调优速查

[!abstract] 定位 面试高频考点,覆盖延迟排查、大 Key 治理、持久化调优、紧急止血。


一、延迟排查四步法

graph LR
    A[延迟飙升] --> B{CPU 高?}
    B -->|否| C[慢查询?]
    B -->|是| D[大 Key / 热 Key?]
    C --> E[slowlog get 20]
    D --> F[--bigkeys / --hotkeys]
    E --> G{危险命令?}
    G -->|KEYS *| H[ban 命令 / 重命名]
    G -->|大 Key DEL| I[UNLINK 异步删除]
    G -->|O(N) 聚合| J[业务逻辑下推]

第一步:确认慢查询

redis-cli slowlog get 20
# 输出示例:
# 1) 1) (integer) 17           # 慢日志 ID
#    2) (integer) 1691078400   # 时间戳
#    3) (integer) 450000       # 执行时间(微秒)→ 450ms!
#    4) 1) "HGETALL"
#       2) "user:session:big_hash"

[!warning] 常见杀手命令

  • KEYS * → 扫描全量 Key,直接阻塞,生产环境必须禁用
  • HGETALL 大 Hash → 单次返回几百 MB 数据
  • ZRANGE 大 ZSet → O(N) 遍历
  • SUNIONSORT → O(N) 聚合

第二步:检查内存与持久化

redis-cli info memory
# 关键字段:
# used_memory_rss_human    # 实际物理内存
# maxmemory_human          # 配置的内存上限
# evicted_keys             # 被驱逐的 Key 数量(非零说明内存满了)
# mem_fragmentation_ratio  # 碎片率,>1.5 需要重启整理

redis-cli info persistence
# 关键字段:
# rdb_bgsave_in_progress   # 0=空闲,1=正在 BGSAVE
# aof_rewrite_in_progress  # 0=空闲,1=正在 AOF rewrite
# latest_fork_usec         # 最近一次 fork 耗时(微秒),>100ms 需要警惕

[!tip] fork 延迟原理 Redis 的 BGSAVEAOF rewrite 都是 fork 子进程来做的。fork 使用 Copy-on-Write,如果父进程内存很大(比如 20GB),fork 本身能阻塞几百毫秒。这就是内存大 + 持久化开 = 周期性延迟抖动的原因。

第三步:定位大 Key 和热 Key

# 扫描大 Key(生产环境慎用,会阻塞)
redis-cli --bigkeys

# Redis 6+ 支持热 Key 分析(基于 LFU)
redis-cli --hotkeys

# 查看连接数异常的客户端
redis-cli client list | awk '{print $2,$3}' | sort | uniq -c | sort -rn

[!warning] 大 Key 怎么处理?

  1. 拆分:大 Hash 按字段拆分、大 List 按时间分片
  2. 异步删除UNLINK key 替代 DEL key
  3. 冷热分离:热数据留 Redis,冷数据下沉到 MySQL/ES
  4. 渐进式删除SCAN + DEL 分批处理

第四步:紧急止血

# 踢掉普通客户端
redis-cli CLIENT KILL TYPE normal

# 临时关闭持久化(确认数据可丢的前提下)
redis-cli CONFIG SET save ""

# 修改淘汰策略(避免 eviction 消耗 CPU)
redis-cli CONFIG SET maxmemory-policy noeviction

# 对某类慢命令临时禁用(别名成空命令)
redis-cli CONFIG SET slave-read-only yes      # 副本只读
redis-cli ACL SETUSER appuser -@dangerous      # Redis 6+ ACL 禁用危险命令

二、缓存击穿 / 穿透 / 雪崩

类型现象根因解决方案
缓存击穿热 Key 过期瞬间,海量请求打穿 DBKey 过期 + 高并发互斥锁(SETNX)+ 永不过期 + 逻辑过期
缓存穿透查询不存在的数据,每次穿到 DB攻击或业务 Bug布隆过滤器 + 空值缓存(短 TTL)
缓存雪崩大量 Key 同时过期,DB 瞬间被打死同一时刻设置了相同 TTLTTL 加随机值 + 多级缓存 + 限流熔断

[!important] 面试金句 「缓存击穿和雪崩的区别:击穿是一个 Key,雪崩是一片 Key;击穿加锁,雪崩加随机。」


三、面试常见追问

Q: Redis 为什么快?

[!tip] 标准答案

  1. 纯内存操作:数据在内存中,读写纳秒级
  2. 单线程 + IO 多路复用:没有锁竞争和上下文切换开销(Redis 6+ 多线程仅用于网络 IO)
  3. 高效数据结构:SDS、ZipList、SkipList 等定制结构

Q: 单线程有什么缺点?

[!tip] 标准答案 一个慢操作阻塞整个实例。所以必须警惕 O(N) 命令(KEYSHGETALL 大 Hash)、大 Key 删除、持久化 fork。

Q: RDB 和 AOF 怎么选?

[!tip] 标准答案

  • RDB:全量快照,恢复快,但可能丢几分钟数据
  • AOF:增量日志,数据更安全,但文件更大、恢复更慢
  • 线上标配:RDB + AOF(appendonly yes),AOF 用 everysec 策略

四、一键排查脚本

#!/bin/bash
# redis-health.sh - 快速健康检查

echo "=== 内存 ==="
redis-cli info memory | grep -E "used_memory_human|maxmemory_human|evicted_keys|mem_fragmentation_ratio"

echo "=== 持久化 ==="
redis-cli info persistence | grep -E "rdb_bgsave|aof_rewrite|latest_fork_usec"

echo "=== 客户端 ==="
redis-cli info clients | grep -E "connected_clients|blocked_clients"

echo "=== 慢日志 (最近 5 条) ==="
redis-cli slowlog get 5

echo "=== Key 数量 ==="
redis-cli dbsize

#sre #redis #面试