大 key 与热 key 是怎么产生的
Redis 单线程执行命令,任何一条慢命令都会堵住后面的所有请求。大 key 和热 key 正是慢命令的两大来源:大 key 指单个 key 的 value 过大,比如一个 list 塞了几百万条、一个 hash 存了几十万字段;热 key 指某个 key 被高频访问,比如秒杀商品、明星八卦,流量全打在一个节点上。
它们的产生往往来自设计欠账:图省事把整张表序列化进一个 key;排行榜、消息列表只增不减;缓存 key 不带随机后缀导致所有请求命中同一个 key。多数问题不是突发事故,而是长期积累,等到线上卡顿、节点漂移才被注意到。
- 大 key单 key 数据量过大,阻塞命令。
- 热 key单 key 访问过高,打爆节点。
- 拆分按维度把大结构拆成小单元。
- 本地缓存热 key 的兜底方案。
大 key 的危害与排查
大 key 的危害是全面的:读取和删除都要遍历全部数据,单条命令耗时动辄几十毫秒,直接阻塞其他请求;大 key 占满节点内存后,集群会把整个 key 迁移到另一个节点,迁移期间服务不可用;持久化时写放大严重,主从复制也受影响。排查工具主要有三条路:slowlog 看慢命令、redis-cli --bigkeys 扫大 key、以及客户端统计 key 长度。
# 查看慢日志,找出耗时命令
redis-cli slowlog get 20
# 扫描大 key
redis-cli --bigkeys -i 0.1
更精细的做法是在客户端侧做抽样统计:记录每次读写命令涉及的 key 与数据量,定期汇总上报,这样能拿到真实业务视角下的大 key 名单,而不是靠一把梭的扫描。判定阈值按业务定,一般字符串超过 10KB、集合超过 5000 个元素就值得关注。
大 key 拆分实战
拆分的原则是按访问维度切,而不是机械地均分。同一个 hash 里字段按业务域拆成多个 key:用户信息、订单统计、行为标签各放各的;一个大 list 改成按时间分片,每个片一个 key;大字符串如果是整表快照,改为逐条小 key 加本地缓存。
// 原:一个 hash 存全部用户
HSET user:all 1001 "张三" 1002 "李四"
// 改:按区间拆分
HSET user:1001 name "张三" age 30
HSET user:1002 name "李四" age 25
- 按业务域把混合数据按类型分 key。
- 按时间流水数据按时分片,便于淘汰。
- 按主键一对一大 key 改一对一细 key。
拆分要同步改读写代码:写端按新规则分散写入,读端按需聚合多个小 key。历史数据可以用后台任务逐步迁移,先写新 key 再删旧 key,避免一次性全量迁移把集群压垮。
大 key 删除的正确姿势
直接 DEL 一个大 key 是最危险的姿势:删除同样要遍历全量数据,阻塞时间和大 key 读写一样长。Redis 提供了两个非阻塞命令:UNLINK 把 key 从键空间摘除,真正的内存回收交给后台线程慢慢做;集合类数据还可以用 SSCAN 分批取出元素、逐步删除,适合在旧版本上做兼容处理。
# 非阻塞删除
redis-cli unlink big:key
# 分批清理集合(伪代码)
cursor = 0
loop {
cursor, members = sscan big:set cursor
srem big:set members
if cursor == 0: break
}
还有一类特殊的大 key:过期时间很短、集中到期,瞬间同时过期也会造成阻塞抖动。处理方式是给过期时间加随机偏差,让淘汰分散到时间轴上。
热 key 发现与应对
热 key 的杀伤力不亚于大 key:单节点每秒几十万次访问,CPU 打满、网络打满,其他 key 全部陪跑。发现途径包括:客户端统计 key 访问频次、Redis 的 hotkeys 参数与 monitor 采样、以及代理层的 QPS 排行。应对策略有四层,从近到远:本地缓存扛掉大头,多级缓存分流,热点 key 加随机后缀分散到多节点,最后是容量上把热点数据放在更高性能的节点。
// 本地缓存:先查本地再查 Redis
Object v = localCache.get(key);
if (v == null) {
v = redis.get(key);
localCache.put(key, v, shortTtl);
}
- 本地缓存最短路径,扛高频读。
- 随机后缀让 key 分散到多个分片。
- 读写分离读压力分流到从节点。
- 限流兜底极端流量下保护节点。
注意本地缓存的一致性:写操作要失效或短 TTL 兜底,接受秒级延迟。随机后缀方案要区分读写,写仍写原 key,读随机挑副本 key,才能保证一致性。
治理体系与监控告警
大 key 与热 key 治理不能靠救火,要建体系:发布前在测试环境做 key 规格检查,超阈值直接拦截;线上用客户端埋点持续统计,定期出大 key 与热 key 榜单;接入监控告警,key 长度、节点 CPU、慢命令数量超线即通知值班。数据过期策略也要纳入治理,无 TTL 的 key 要排查是设计如此还是忘了设置。
# 检查 key 的过期时间
redis-cli ttl user:1001
指标沉淀成周报后,可以看到治理趋势:大 key 数量是不是在下降、热 key 的本地缓存命中率够不够。治理是持续动作,每次大促前做一次全量巡检,把风险前置到发布之前。
落地清单
- 上线前做 key 规格检查,字符串与集合设阈值。
- 客户端埋点统计大 key 与热 key,定期出榜。
- 大 key 按业务域或时间拆分,同步改造读写代码。
- 删除用 UNLINK 或分批清理,禁止直接 DEL。
- 过期时间加随机偏差,避免集中淘汰抖动。
- 热 key 用本地缓存与随机后缀分散,异常流量限流兜底。