Redis 线上问题排查
问题
Redis 出现响应变慢、内存暴涨、连接数飙升等线上问题,如何排查?
答案
一、常见问题全景
二、大 Key 问题
什么是大 Key
| 类型 | 大 Key 标准 | 风险 |
|---|---|---|
| String | > 1MB | 网络阻塞 |
| Hash | > 5000 field | 操作耗时 |
| List | > 10000 元素 | DEL 阻塞 |
| Set | > 10000 成员 | SMEMBERS 卡顿 |
| ZSet | > 10000 成员 | ZRANGEBYSCORE 慢 |
发现大 Key
# 方法 1:redis-cli 扫描(安全,不阻塞)
redis-cli --bigkeys
# 方法 2:MEMORY USAGE 查看单个 Key
redis-cli MEMORY USAGE my_big_key
# 方法 3:SCAN + 检测(生产环境推荐)
redis-cli --scan --pattern '*' | while read key; do
size=$(redis-cli MEMORY USAGE "$key" 2>/dev/null)
if [ "$size" -gt 1048576 ]; then # > 1MB
echo "$key: $size bytes"
fi
done
处理大 Key
// ❌ 直接删除大 Key 可能阻塞 Redis
await redis.del('big_hash_key'); // Hash 有 10 万 field,DEL 耗时数秒
// ✅ 方案 1:UNLINK 异步删除(Redis 4.0+)
await redis.unlink('big_hash_key');
// ✅ 方案 2:分步渐进删除
async function deleteHashProgressively(key: string) {
let cursor = '0';
do {
const [nextCursor, fields] = await redis.hscan(key, cursor, 'COUNT', 100);
cursor = nextCursor;
if (fields.length > 0) {
const fieldNames = fields.filter((_, i) => i % 2 === 0);
await redis.hdel(key, ...fieldNames);
}
} while (cursor !== '0');
await redis.del(key); // 最后删除空 Key
}
三、热 Key 问题
热 Key = 大量请求集中在少数 Key 上,导致单节点压力过大。
发现方式:
1. redis-cli --hotkeys(需开启 LFU 策略)
2. 业务监控:统计 Redis 客户端各 Key 的访问频次
3. 代理层统计:Redis Proxy 统计 QPS 分布
解决方案:
| 方案 | 做法 | 适用 |
|---|---|---|
| 本地缓存 | L1 缓存热 Key,短 TTL | 读多写少 |
| Key 分片 | key:shard:0~9,随机读 | 读多写少 |
| 读写分离 | 从库分担读流量 | 集群模式 |
四、慢命令排查
# 查看慢日志
redis-cli SLOWLOG GET 10
# 设置慢查询阈值(微秒)
redis-cli CONFIG SET slowlog-log-slower-than 10000 # 10ms
redis-cli CONFIG SET slowlog-max-len 128
常见慢命令:
| 命令 | 风险 | 替代方案 |
|---|---|---|
KEYS * | 全量扫描 O(N) | SCAN 渐进式遍历 |
SMEMBERS | 大 Set 全量返回 | SSCAN 分批 |
HGETALL | 大 Hash 全量返回 | HSCAN 或 HMGET 指定字段 |
DEL 大 Key | 阻塞主线程 | UNLINK 异步删除 |
FLUSHDB | 清空整个库 | FLUSHDB ASYNC |
五、内存问题排查
# 查看内存使用
redis-cli INFO memory
# 关键指标
# used_memory : 已用内存
# used_memory_rss : 操作系统分配内存(含碎片)
# mem_fragmentation_ratio: 碎片率(> 1.5 需关注)
# maxmemory : 最大内存限制
| 问题 | 表现 | 解决 |
|---|---|---|
| 未设过期 | 内存持续增长 | 设置 TTL |
| 碎片率高 | RSS > used × 1.5 | MEMORY PURGE 或重启 |
| 淘汰频繁 | evicted_keys 增长 | 扩容或优化数据结构 |
六、持久化导致的卡顿
问题:RDB fork 子进程时,主进程短暂阻塞
原因:
- fork 需要复制页表,内存越大耗时越长
- 开启 AOF always 模式时,每次写入同步磁盘
解决:
1. 控制单实例内存 < 10GB
2. AOF 使用 everysec 策略(非 always)
3. 关闭 THP(Transparent Huge Pages)
4. 使用 SSD 磁盘
常见面试问题
Q1: Redis 为什么变慢了?排查思路是什么?
答案:
排查顺序:
1. SLOWLOG GET → 有没有慢命令?
2. INFO memory → 内存是否达到上限?在淘汰?
3. INFO clients → 连接数是否异常?
4. INFO persistence → 是否在做 RDB/AOF rewrite?
5. latency 命令 → 是否有延迟事件?
6. 网络排查 → 客户端到 Redis 的网络延迟
7. 大 Key 排查 → --bigkeys 扫描
Q2: Redis 内存满了会怎样?
答案:
取决于 maxmemory-policy(淘汰策略):
| 策略 | 行为 |
|---|---|
noeviction | 写入报错(默认) |
allkeys-lru | 淘汰最久未使用的 Key |
allkeys-lfu | 淘汰最少使用频次的 Key |
volatile-lru | 仅淘汰有过期时间的 Key(LRU) |
volatile-ttl | 淘汰 TTL 最短的 Key |
推荐 allkeys-lfu:根据访问频率淘汰,更智能。
Q3: Redis Cluster 节点数据倾斜怎么办?
答案:
- 发现:
redis-cli --cluster info查看各节点 Key 数量和内存 - 原因:
- 大 Key 集中在某节点
- Hash Tag 导致固定路由
- 迁移不均匀
- 解决:
- 拆分大 Key
- 合理使用 Hash Tag
CLUSTER REBALANCE重新平衡- 手动迁移 Slot