--- 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 是排查内存问题的必备工具