从原理、危害到解决方案,一文彻底搞懂 Redis 大 Key 的来龙去脉,助你打造高可用、高性能的 Redis 集群
先搞清楚定义,才能对症下药。
Redis 中的 大 Key(Big Key / Hot Key) 指的是某个 Key 对应的 Value 所占内存空间非常大,
或者某个 Key 的读写操作频率远超其他 Key(热点 Key)。通常我们说的"大 Key"主要关注前者——Value 过大的问题。
经验阈值参考:
• String 类型:Value 超过 10 KB 即需警惕,超过 100KB 算典型大 Key
• Hash / List / Set / ZSet:元素数量超过 5000 个 或 总大小超过 10 MB
⚠️ 注意:具体阈值取决于你的 Redis 实例内存大小和业务场景
| 数据类型 | 常见大 Key 形态 | 风险等级 |
|---|---|---|
| String | 存储大型 JSON、HTML 页面、图片 Base64、长文本日志等 | ⛔ 高危 |
| List | 消息队列堆积、未限制长度的列表(如用户行为记录) | ⛔ 高危 |
| Hash | 用户画像全量数据缓存、对象属性全部存入一个 Hash | ⚠️ 中危 |
| Set | 海量标签集合、去重集合(如亿级 UV 去重 Set) | ⚠️ 中危 |
| ZSet | 排行榜全量数据、延时队列(大量元素) | ⚠️ 中危 |
大 Key 不只是"占空间",它会引发一系列连锁反应,从性能抖动到集群崩溃都有可能。
Redis 是单线程执行命令的。当对一个几 MB 甚至几十 MB 的大 Key 执行 GET、DEL、
HGETALL 等 O(N) 复杂度操作时,该命令会长时间占用线程,导致后续所有请求排队等待。
在高并发下,这会导致整个 Redis 实例响应超时,甚至出现"假死"现象。集群模式下还会触发 failover。
在 Redis Cluster 模式下,Key 按 slot 分配到不同节点。如果某个大 Key 占用几十 MB 甚至上百 MB 内存, 它所在的节点内存使用率会远高于其他节点,造成严重的数据倾斜。轻则资源浪费,重则触发 内存淘汰或 OOM,而其他节点还很空闲。
大 Key 会导致主从复制延迟急剧增大。主节点处理大 Key 命令时耗时很长, 从节点同步也需要同样时间。如果在此期间主节点宕机,从节点可能还未同步完最新的大 Key 操作, 发生切换后出现数据丢失。同时,全量同步期间大 Key 还会导致带宽打满。
一个大 Key 就可能吃掉实例可用内存的很大比例。当多个大 Key 同时存在时,Redis 可用内存被迅速消耗, 触发 maxmemory 策略进行 key eviction,可能误删重要业务 Key。 更极端的情况下直接触发 Linux OOM Killer 杀掉 Redis 进程。
这是最容易被忽视的问题。删除一个大 Key 和写入一样慢!
DEL 命令的时间复杂度为 O(N),删除一个包含百万元素的 Set 可能需要数秒。
在这期间 Redis 完全无法服务其他请求。Redis 4.0+ 引入了
UNLINK(异步删除)就是为了解决这个问题。
大 Key 让日常运维变得痛苦:SCAN 扫描缓慢、持久化文件(RDB/AOF)变大导致 fork 子进程和加载时间变长;恢复备份时大 Key 是瓶颈;监控指标失真(平均 Key 大小掩盖了异常值); 排查性能问题时难以快速定位根因。
知己知彼,防患于未然。以下是生产环境中最常见的"大 Key 温床"。
将一个实体的所有字段全部存到一个 Hash中(如用户完整画像),或者将 所有商品信息存到一个 Key中。随着业务增长,这些数据结构会越来越大。
用 List 做消息队列但不消费也不设长度上限(LPUSH 无限增长);
用 Set 做 UV 统计但从不清理;ZSet 排行榜保留全量数据而非 Top N。
将图片、文件、大段 HTML以字符串形式存入 Redis; 把完整的 JSON 文档序列化后存为一个 String Value。这些本应存放在 OSS/S3 上的内容错误地放进了 Redis。
缓存数据不设置 TTL,只增不减;临时数据长期积累; 批量导入数据时没有做拆分;没有定期扫描和清理机制。
发现问题是解决问题的第一步。以下是常用的检测方法。
Redis 自带的扫描工具,可快速发现各类型中的最大 Key。注意:线上慎用!它使用 SCAN 命令遍历所有 Key,对生产实例有性能影响。
精确查看单个 Key 的内存占用。可以配合 SCAN 逐个检查可疑 Key。Redis 4.0+ 可用。
MEMORY USAGE 命令本身也会在执行时短暂阻塞线程,对于特别大的 Key 不要频繁调用。最安全的做法。将线上的 RDB 文件拷贝到测试环境,用工具离线分析,不影响线上服务。可以生成每个 Key 的详细内存报告。
如果你用的是阿里云 Redis、腾讯云 Redis 等托管服务,控制台通常提供大 Key 分析功能,一键扫描即可。自建 Redis 可以通过 redis_exporter + Prometheus + Grafana 搭建监控面板,设置 Key 分布告警。
以下方案按推荐优先级排列,从预防到治理全覆盖。
将一个大 Key 拆分成多个小 Key,这是最有效、最通用的方案。不同数据类型有不同的拆分策略:
对于 String 类型的大 Key(如 JSON、HTML、文本),可以使用压缩算法大幅减小体积。常用方案:Snappy、LZ4、Gzip。压缩率通常可达 3x ~ 10x。
对于 List、Set、ZSet、Hash 等集合类型,核心思路是:控制单个集合规模 + 分片分散。
对于不适合放在 Redis 中的大数据(图片、文档、大 JSON),应该将其迁移到合适的存储系统,只在 Redis 中保存引用(URL / ID / 元信息)。
当你必须删除一个大 Key 时,绝对不要在线上使用 DEL 命令!Redis 4.0+ 提供了 UNLINK 命令,它在后台异步释放内存,不会阻塞主线程。
除了 Redis 本身,客户端代码层面的规范同样关键。好的编码习惯可以从源头避免大 Key。
从系统架构角度建立多层防御体系,让大 Key 无处遁形。
一目了然,帮你根据实际情况选择最合适的方案。
| 方案 | 适用场景 | 优点 | 缺点/代价 | 推荐度 |
|---|---|---|---|---|
| 拆分大 Key | 通用,几乎所有场景 | 根治问题,彻底消除隐患 | 需要改代码,有迁移成本 | ⭐⭐⭐⭐⭐ |
| 压缩 Value | String 类型的文本/JSON | 改动小,效果明显 | 增加 CPU 开销,读写需编解码 | ⭐⭐⭐⭐ |
| 控制集合规模 | List/Set/ZSet/Hash | 简单有效,一行代码的事 | 可能丢失部分历史数据 | ⭐⭐⭐⭐⭐ |
| 大对象外置 | 图片/文件/大文档 | 从根本上解决问题 | 引入额外依赖(OSS 等),网络开销 | ⭐⭐⭐⭐⭐ |
| UNLINK 异步删除 | 必须删除已有大 Key | 不阻塞线程,安全 | 仅解决删除问题,非根治 | ⭐⭐⭐⭐ |
| 客户端门卫检查 | 预防新大 Key 产生 | 防患于未然 | 需要改造所有写入路径 | ⭐⭐⭐⭐ |
| 定期巡检 + 告警 | 运维保障 | 早发现早治疗 | 不能自动修复,依赖人工 | ⭐⭐⭐⭐ |
总结一下在生产环境中应对大 Key 的黄金法则。