解决 Redis 大 Key 问题
解决 Redis 大 Key 问题通常分为 发现 → 拆解 → 优化 → 清理 四个步骤,你可以按这个顺序来操作:
一、发现大 Key(先定位问题)
| 方法 | 命令/工具 | 说明 |
|---|---|---|
--bigkeys | redis-cli --bigkeys | 扫描并返回每种数据类型中最大的 Key(注意:会扫描全库,生产环境建议在低峰期做)。 |
MEMORY USAGE | MEMORY USAGE key | 精确查看某个 Key 占用的内存(字节)。 |
SCAN + 推断 | 自定义脚本 | 遍历所有 Key,对疑似大 Key 用 STRLEN/LLEN/HLEN/ZCARD 等判断长度。 |
| 监控工具 | Prometheus + Redis exporter / CacheCloud | 长期监控慢查询、内存热 Key、大 Key。 |
二、解决大 Key(核心方法)
根据数据类型不同,采用不同拆分策略:
1. String 类型大 Value(最常见)
- 压缩:用 snappy / lz4 / gzip 压缩后再存(CPU 换内存+网络)。
- 拆分为多个小 Key:例如 1MB 的字符串拆成 100 个 10KB 的 key,用
MGET批量读取。 - 冷热分离:大对象不全部放 Redis,Redis 只存索引/元数据,实际内容放对象存储(如 S3/OSS)。
2. Hash 类型大 Key(field 过多)
- 拆分:按
hash(key) % N将一个大 Hash 拆成 N 个小 Hash(例如hash_0,hash_1…)。 - 转换结构:如果每个 field 对应一个独立对象,直接改用 String 分别存储(
user:1001:name)。 - 部分读取:使用
HSCAN分批获取,避免HGETALL。
3. List / Set / ZSet 类型大 Key(元素过多)
- 分页 / 分段存储:例如
list:0-999,list:1000-1999。 - 冷数据归档:旧数据移出 Redis,必要时用 Lua 或定时任务转移。
- 限制长度:使用
LTRIM定期截断,避免无限增长(如只保留最近 1000 条)。
三、删除大 Key(避免阻塞)
| 方法 | 适用场景 | 说明 |
|---|---|---|
| UNLINK(推荐) | 所有类型 | 异步删除,主线程立即返回,后台线程逐步回收内存,不阻塞服务。 |
DEL(不推荐) | 小 Key | 对大数据量会阻塞 Redis。 |
| 分批删除 | List / Set / Hash / ZSet | 用 LRANGE + LTRIM / SSCAN + SREM / HSCAN + HDEL / ZRANGE + ZREMRANGEBYRANK 每次删一小批。 |
四、预防与常态化治理
- 设置 Key 长度阈值:代码中拦截超过 1MB 的写入(可配置)。
- 使用 Hash 代替多个 String:对于字段较多的对象,Hash 比大量独立 String 更省内存(但也要避免单个 Hash 过大)。
- 读写分离 + 只读副本:将大 Key 的读请求分散到从节点,减少主节点压力。
- 监控告警:对
maxmemory使用率、慢日志、大 Key 数量设置告警。
五、集群模式下的特殊处理
- 数据倾斜:将大 Key 的拆分后分散到不同 slot(如添加随机后缀
{user:123}_part1,但注意{}内相同会定向到同一分片)。 - 使用 Redis 6.0+ 的
TLDR或KeyDB:多线程处理删除/释放内存更高效。
一句话总结:发现用 --bigkeys,String 压缩或拆分,Hash/List 分段存储,删除用 UNLINK,写入加阈值防御。