← 返回题目列表

Big Key 有哪些危害?如何治理?

高频 中等 第 12 / 26 题 更新于 2026/10/03
RedisBig Key延迟内存治理
学习 AI 实战项目

简化版

Big Key 是单个键对应的 value 过大或集合成员过多,不是固定字节阈值。它会造成单线程命令阻塞、网络突发、内存倾斜、复制与迁移变慢,还可能在删除时引发释放阻塞;治理要从发现、停止增长、拆分、渐进迁移和安全删除五步推进。

详细版

Big Key 的判断必须结合类型与访问方式:5MB 字符串、百万成员 Hash,或一次命令返回数十万成员都可能成为问题。危害不只在占内存,而在一次操作把大量 CPU、内存和网络工作集中到一个 Redis 主线程或一个分片上;主从全量/增量复制、AOF 重写、Cluster 槽迁移和故障恢复也会被拖慢。

发现手段包括低峰期运行 redis-cli --bigkeys 做全库渐进扫描、对候选键执行 MEMORY USAGE、STRLEN、HLEN/SCARD/ZCARD/LLEN,以及监控客户端响应大小、慢日志和分片内存倾斜。生产环境不要用 KEYS * 全量扫描,也不要对未知大集合直接执行会返回全部元素的命令。

治理时先限制写入继续膨胀,再按业务维度拆成多个键或将大对象移到对象存储/数据库;迁移采用双写、分批回填、校验和灰度切读。删除大键优先 UNLINK 异步回收,集合也可按批次 HSCAN/SSCAN/ZSCAN 后渐进删除,但仍需监控 lazyfree backlog 与内存峰值。

完整版教学

一、Big Key 的“大”没有统一阈值

Redis 不规定超过多少字节就叫 Big Key,因为风险取决于数据类型、命令复杂度、实例容量、网络和延迟目标。一个 2MB 字符串在离线缓存中可能可接受,在 P99 要求 5ms 的在线链路上就可能过大;一个 Hash 只有 20MB,但若含 100 万字段,遍历和删除仍很危险。

判断时至少看 value 字节数、集合元素数、单次命令处理/返回量和访问频率。Big Key 与 Hot Key 也不同:前者强调单键体积或成员规模,后者强调访问流量;同一个键可以又大又热,风险会叠加。

记忆钩子:Big Key 真正危险的是“把大量工作集中在一次操作和一个分片”,不只是占了多少内存。

二、为什么会阻塞请求和网络

Redis 大部分命令执行仍由主线程串行完成。读取 10MB 字符串需要查找、复制并写入客户端输出缓冲;对百万成员集合执行全量命令还要遍历和序列化。该命令执行期间,后续轻量请求也会排队。

假设接口 QPS 为 5000,某次大键命令阻塞 200ms,期间理论上约有 5000 × 0.2 = 1000 个请求等待。10MB 响应经 100Mbps 链路传输,忽略协议开销也约需 10×8/100 = 0.8 秒,客户端与代理的缓冲同样承压。

大键命令执行 -> Redis 主线程占用 -> 后续命令排队
             -> 大响应进入输出缓冲 -> 网络发送变慢
             -> 客户端超时重试 -> 压力进一步增加

三、它还会破坏集群均衡与恢复

Cluster 按 slot 分配键,一个键不能被自动拆到多个 slot。即使全局内存平均,某个 4GB 键也只落在一个主节点,形成内存和带宽倾斜。扩容迁移该 slot 时,这个键的数据搬迁会延长迁移窗口,甚至因超时反复失败。

复制也要传递大对象。主从断线重连、AOF rewrite 或 RDB fork 时,Big Key 会增加复制流量、写时复制内存和恢复时间。故障切换后,冷节点首次读取大对象还可能造成突发延迟。

环节Big Key 的影响应观察的指标
在线命令主线程与输出缓冲被占用command latency、slowlog
分片内存/QPS 倾斜max shard / avg shard
复制复制流量和 lag 增长replication lag、repl buffer
持久化fork/COW 与重写压力fork time、RSS、rewrite time
迁移恢复单键搬迁时间过长slot migration time、失败数

四、怎样在生产中安全发现

redis-cli --bigkeys 基于 SCAN 渐进遍历当前键空间,并按数据类型比较长度或成员数;它报告的是扫描期间看到的候选大键,不是按真实内存占用做全局排序。扫描期间键仍可能增删,结果也不是某一时刻的完整快照。对候选键还要使用 MEMORY USAGE key SAMPLES n 估算内存,并用类型对应的长度命令确认成员数。

扫描要限速并避开业务高峰。SCAN 单次调用虽是增量的,完整遍历仍会消耗 CPU 和网络;在大集群里应按节点执行、设置较小 COUNT,并记录游标断点。更成熟的做法是在写入侧记录 value 大小分布,提前拒绝异常增长。

string -> STRLEN + MEMORY USAGE
hash   -> HLEN   + MEMORY USAGE
set    -> SCARD  + MEMORY USAGE
zset   -> ZCARD  + MEMORY USAGE
list   -> LLEN   + MEMORY USAGE

五、拆分方案必须匹配访问模式

大 Hash 可按业务 ID 哈希分桶,例如 user:profile:{bucket};时间序列可按天/月分键;排行榜可按地区或业务段拆分;大 JSON 应把高频字段独立缓存,低频大对象放对象存储。拆分目标是让常用读写只触达必要分片,而不是机械切成 100 份后每次仍全量读取。

假设百万成员集合拆成 100 桶,每桶约 1 万成员,单桶查询和删除成本显著下降。但若业务每次都要全量聚合 100 桶,读扩散和聚合成本会增加。因此要先统计查询维度,再选择分桶键。

迁移可采用新旧结构双写:先上线兼容代码,批量回填并比较数量/校验和,再按用户或租户灰度切读,最后停止旧写并延迟删除。双写失败要有重试和对账,不能靠一次脚本赌成功。

六、删除大键为什么也会出事故

DEL 不仅删除字典入口,还可能同步释放复杂对象及大量成员,回收工作会阻塞主线程。UNLINK 将实际内存回收交给后台 lazyfree 线程,可显著减少主线程停顿,但内存不会立即下降,后台队列过大时仍有压力。

对于巨大集合,可以先按 SCAN 分批删除成员,控制每批数量和间隔,再删除空键。执行前要确认业务已停止写入,否则边删边长会导致遗漏。删除期间监控内存、CPU、lazyfree pending objects、复制滞后和延迟。

冻结/切断旧键写入 -> 分批迁移与校验 -> 灰度切读
                  -> UNLINK 或分批删成员 -> 观察后台回收 -> 清理

七、如何建立长期治理

在客户端或缓存 SDK 设置单 value 大小和集合成员增长保护,例如字符串超过 512KB 告警、超过 2MB 拒绝(阈值按业务压测确定)。对批量读取限制元素数量和响应字节,禁止无界 HGETALL、SMEMBERS 进入在线接口。

监控应同时包含 Top Key 内存、Top Key 流量、分片倾斜、客户端输出缓冲、慢命令和迁移耗时。告警发现后先限制增长和大范围读取,再安排迁移,避免在高峰直接删除造成二次故障。

治理完成后用同等数据量压测拆分后的访问模式,并验证扩容、故障切换和清理路径。只验证普通 GET 延迟不足以证明风险消失。

八、常见误区与追问

  • 误区:占用超过 10MB 才算 Big Key。 阈值取决于类型、命令、频率和 SLO,小于 10MB 也可能造成阻塞。
  • 误区:Big Key 只浪费内存。 它还影响主线程、网络缓冲、复制、持久化、槽迁移和故障恢复。
  • 误区:SCAN 完全没有性能影响。 单次增量不阻塞全库,但完整扫描仍消耗 CPU 和网络,必须限速。
  • 误区:使用 UNLINK 后内存会立刻释放。 它只是把回收移到后台,释放进度和后台队列仍需监控。
  • 追问:为什么虚拟节点不能拆散一个 Big Key? 哈希路由以键为最小单位,同一个键始终落在一个 slot/节点。
  • 追问:大 Hash 应按什么维度拆? 选择能匹配高频查询且分布均匀的业务字段或哈希桶,避免每次跨所有桶。
  • 追问:怎样防止再次产生? 在写入 SDK 加大小与成员数阈值、增长告警和无界命令检查,并纳入代码评审。

九、加强记忆

用“量、堵、斜、迁、删”记 Big Key:单键数据量大,命令会堵主线程,分片出现倾斜,复制迁移变慢,删除回收也可能阻塞。治理顺序是先发现并停止增长,再按访问模式拆分、双写迁移和校验,最后使用 UNLINK 或分批删除安全清理,而不是在线高峰一把删掉。