---
tags: [redis, memory, troubleshooting, monitoring]
create time: 2026-06-01 10:30
---
# Redis 内存持续增长排查
## 概述
生产环境中 Redis 内存持续增长是一个常见的告警场景。它不一定意味着 Bug——但如果不及时处理,最终会导致 OOM 或频繁 Swap。本节提供一套**系统性的排查思路**,对应你的薄弱点 F10(TTL 命令检查 Key 过期状态)。
## 一、排查流程图
```mermaid
flowchart TD
Start["⚠️ 内存持续增长告警"] --> Step1["Step 1: MEMORY USED 确认增长幅度"]
Step1 --> Q1{"增长是持续的还是突发的?"}
Q1 -- "持续增长" --> SLOW["缓慢增长路径"]
Q1 -- "突然飙升" --> SPIKE["突发峰值路径"]
subgraph SLOW["慢增长排查"]
S1["检查 Key 总数变化
DBSIZE / SCAN"] --> S2["找出增长最快的 Key 前缀
SCAN + TTL 检查"] --> S3["确认 EXPIRE 是否生效
TTL key → -1 表示未过期"] --> S4["僵尸 Key 清理"]
end
subgraph SPIKE["爆发增长排查"]
P1["监控 BigKey 列表
MEMORY USAGE + SORTED BY size"] --> P2["检查是否有大量写入并发
Slowlog / Monitoring"] --> P3["排查是否为临时批处理任务
定时任务/数据同步"]
end
S4 --> Action{"找到原因了?"}
P3 --> Action
Action -- "是" --> Fix["修复"]
Action -- "否" --> Dump["MEMORY ANALYZER
深入分析内存分布"]
style Start fill:#fff3e0
style Fix fill:#c8e6c9
style Dump fill:#e3f2fd
```
## 二、逐步排查方法
### Step 1:定位增长来源
```bash
# 查看所有数据库的大小
INFO keyspace
→ db0:keys=12345,expires=12000,avg_ttl=3600000
```
输出解析:
| 字段 | 含义 |
|------|------|
| `keys` | 该数据库中 Key 的总数 |
| `expires` | 设置了过期时间的 Key 数量 |
| `avg_ttl` | 这些 Key 的平均剩余生存时间(毫秒) |
如果 `keys` 在持续增长而 `expires` 不变 → 有大量未设置过期的 Key
### Step 2:查找大 Key
```bash
# 方式1: redis-cli --bigkeys(推荐快速使用)
redis-cli --bigkeys
# 扫描中...
# --- STRINGS ---
# biggest rate:sliding:user123 (6 MB)
# count 15 (total 15 keys)
# ...
# 方式2: 手动用 SCAN + MEMORY USAGE
SCAN 0 MATCH rate:* TYPE string | xargs -I{} redis-cli MEMORY USAGE {}
```
> [!tip]- bigkeys 工具的坑
>
> `redis-cli --bigkeys` 会遍历所有 Key,对大库来说仍然可能阻塞 Redis。**生产环境慎用**,建议在从库或低峰期运行。
### Step 3:检查过期状态(F10 考点 🔑)
这是排查内存问题的核心技能——**判断 Key 是否已经过期**。
```bash
# 检查单个 Key 的 TTL
TTL rate:limit:user123
→ 86400 ← 剩余 86400 秒,正常
TTL rate:limit:user456
→ -1 ← ❌ 没有过期时间!这就是僵尸 Key
TTL rate:limit:user789
→ -2 ← ❌ Key 已不存在,已被删除
```
| TTL 返回值 | 含义 | 处理方式 |
|-----------|------|---------|
| `> 0` | Key 存在,还有 N 秒过期 | 正常,等待自动清理 |
| `-1` | **Key 存在但没有设置过期时间** | ❌ 检查代码中是否漏了 EXPIRE,手动 DEL |
| `-2` | Key 已不存在 | 正常,无需操作 |
### Step 4:批量检查僵尸 Key
```go
func findZombieKeys(ctx context.Context, rdb *redis.Client, pattern string) []string {
var zombies []string
cursor := uint64(0)
for {
keys, nextCursor, _ := rdb.Scan(ctx, cursor, pattern, 200).Result()
for _, key := range keys {
ttl, err := rdb.TTL(ctx, key).Result()
if err == nil && ttl == -1 {
zombies = append(zombies, key)
}
}
if nextCursor == 0 {
break
}
cursor = nextCursor
}
return zombies
}
```
## 三、常见内存增长原因及对策
| 原因 | 表现 | 解决方式 |
|------|------|---------|
| **EXPIRE 未生效** | TTL 返回 -1 | 检查 Lua 脚本或 Go 代码中的 EXPIRE 调用 |
| **Sorted Set 规模过大** | 个别 Key 占几 MB~几十 MB | 切换到令牌桶方案(O(1) 空间) |
| **批量写入无上限** | DBSIZE 快速增长 | 添加写入限流和 Key 数量上限 |
| **大 Hash/List 结构** | 单 Key 占用巨大 | 拆分或使用更合适的数据结构 |
| **副本同步延迟** | Master 已过期,Slave 还没删 | 配置 `replica-ignore-maxmemory` |
| **Redis 缓存淘汰策略不当** | maxmemory-policy 设为 noeviction | 改为 `allkeys-lru` 或 `volatile-ttl` |
## 四、预防性措施
```mermaid
flowchart TD
Monitor["📊 持续监控"] --> A["MEMORY USAGE per Key 告警"]
Monitor --> B["DBSIZE 增长速率告警"]
Monitor --> C["Evicted Keys 计数告警"]
Policy["⚙️ 合理的淘汰策略"] --> D["maxmemory-policy: allkeys-lru"]
Policy --> E["maxmemory: 设为可用内存的 70%"]
Design["🔧 设计时考虑"] --> F["所有 Key 必须有 EXPIRE"]
Design --> G["高频场景用 O(1) 算法(令牌桶)"]
Design --> H["定期 SCAN 清理残留数据"]
style Monitor fill:#e3f2fd
style Policy fill:#fff3e0
style Design fill:#e8f5e9
```
### 推荐配置
```conf
# redis.conf
# 最大内存限制(建议设置为服务器总内存的 70%,留余量给 Swap)
maxmemory 2gb
# 淘汰策略:LRU(最近最少使用)
maxmemory-policy allkeys-lru
# 或者:优先删除有过期时间的 Key 中最早过期的
# maxmemory-policy volatile-ttl
# RDB/AOF 混合持久化
rdb-save-incremental-mem YES
aof-use-rdb-preamble YES
```
## 五、紧急止血步骤
```
当内存已经接近上限时的应急操作:
1. 停止所有非必要的写入(暂停定时任务、数据同步)
2. 找到并删除最大的几个 Key:
redis-cli --bigkeys
DEL ...
3. 如果无法确定哪些 Key 该删,先批量减少 Key 总量:
SCAN 0 MATCH rate:* COUNT 1000 → DEL 随机抽取的部分 Key(谨慎评估影响)
4. 临时提升 maxmemory(如果服务器还有物理内存可用)
5. 事后复盘:为什么没及时发现?监控阈值要不要调低?
```
> [!warning]- DEL vs UNLINK
>
> 删除大 Key 时用 `UNLINK`(异步删除)替代 `DEL`(同步删除),避免阻塞主线程:
> ```bash
> # 同步删除(阻塞)
> DEL rate:sliding:large_key
>
> # 异步删除(不阻塞)
> UNLINK rate:sliding:large_key
> ```
## 关联笔记
- [[分布式限流]] — 排错指南中的内存问题章节
- [[僵尸Key防护]] — 针对僵尸 Key 的具体防护措施
- [[SCAN命令]] — SCAN 是排查内存问题的必备工具