news 2026/9/6 11:36:37

Redis 应用实战(3):热 key 与大 key 治理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis 应用实战(3):热 key 与大 key 治理

上一篇通过 TTL 抖动与请求合并压住集中回源,但缓存内部仍可能严重倾斜。本篇把两个常被混称的问题拆开:热 key 是访问频率异常,大 key 是单个 value 或集合规模异常。前者消耗执行与网络吞吐,后者放大传输、复制、持久化和释放成本;只有分别测量,治理动作才不会南辕北辙。

一、痛点:平均值会隐藏最危险的 key

热 key 指访问频率显著高于其他 key,不一定占很多内存;大 key 指 value 或集合元素过大,不一定经常访问。一个 20 字节的秒杀开关可能每秒读取十万次,是热 key;一个含百万成员但每天读取一次的 Set 是大 key。若只看实例平均 CPU、平均延迟和总内存,就会错过局部倾斜。

热 key 会集中在 Redis 单线程命令执行路径,并在 Cluster 中集中到一个分片。副本分担读能缓解只读流量,但带来复制延迟和一致性取舍。大 key 的GET虽是 O(1),返回几十 MB 仍会阻塞网络与客户端解析;HGETALLSMEMBERSLRANGE 0 -1的成本与元素数相关。同步DEL巨型对象还可能在释放内存时卡住事件循环。

二、发现:从实例信号下钻到对象

先看INFO commandstatsINFO stats、延迟和网络吞吐,判断是命令计算、输出缓冲还是内存压力。SLOWLOG只记录服务器执行阶段,不包含网络传输,因此客户端很慢而慢日志为空并不矛盾。redis-cli --hotkeys依赖 LFU 相关计数,适合辅助采样,不应当作精确全量榜单;应用侧按规范化 key 前缀统计请求频率通常更可靠。

大 key 可用redis-cli --bigkeys做采样扫描,它按类型报告“最大元素数”等信息,并非所有类型都直接按字节比较。对候选 key 再用MEMORY USAGE key SAMPLES nSTRLENHLENLLENSCARDZCARD核实。扫描也会消耗资源,应在副本或低峰进行,设置节奏,避免把诊断变成事故。

下面程序按访问计数的中位数检测热点,并按估算字节识别大对象。生产阈值应按实例容量和 SLO 校准;示例强调两张榜单不能混为一谈。

fromstatisticsimportmedian samples={"config:flash-sale":{"qps":12000,"bytes":32},"user:42":{"qps":80,"bytes":2048},"catalog:all":{"qps":20,"bytes":8_500_000},"user:43":{"qps":70,"bytes":1900},"product:9":{"qps":90,"bytes":1200},}baseline=median(item["qps"]foriteminsamples.values())hot_threshold=baseline*20large_threshold=1_000_000hot=sorted((keyforkey,valueinsamples.items()ifvalue["qps"]>=hot_threshold),key=lambdakey:samples[key]["qps"],reverse=True,)large=sorted((keyforkey,valueinsamples.items()ifvalue["bytes"]>=large_threshold),key=lambdakey:samples[key]["bytes"],reverse=True,)print(f"baseline_qps={baseline}")print(f"hot_keys={hot}")print(f"large_keys={large}")print(f"overlap={sorted(set(hot)&set(large))}")

运行输出:

baseline_qps=80 hot_keys=['config:flash-sale'] large_keys=['catalog:all'] overlap=[]

三、治理:拆分、复制、限界和渐进删除

热读数据可放应用进程本地缓存,使用很短 TTL 或版本通知收敛;这样请求不再全部穿过网络。允许轻微陈旧时,可将同一值复制到多个带后缀的 key,由客户端随机读取以分散 Cluster 槽位,但更新必须写全副本并容忍短暂不一致。热写计数器可按时间或随机桶分片,读取时汇总;这把写压力换成读放大,只适合可合并数据。

大 String 应按业务边界拆分,不要机械切字节导致每次读取仍需全量拼接。大 Hash/Set/Sorted Set 可按用户、月份或哈希桶拆 key,并提供分页 API。集合遍历使用HSCANSSCANZSCAN,接受游标期间元素变化和可能重复;SCAN 不是快照。列表或日志必须设置上限、归档周期和生产者背压。

下面脚本创建一个受控测试 Hash,用游标分批扫描,然后以UNLINK异步释放。它不在共享命名空间运行,也不会扫描整个数据库。

#!/usr/bin/env bashset-euopipefailredis_url="${REDIS_URL:-redis://127.0.0.1:6379/0}"key='demo:bigkey:users'redis-cli-u"$redis_url"DEL"$key">/dev/nullforstartin0100200300;doargs=()for((i=start;i<start+100;i++));doargs+=("user:$i""score:$((i%17))")doneredis-cli-u"$redis_url"HSET"$key""${args[@]}">/dev/nulldonecount="$(redis-cli-u"$redis_url"--rawHLEN"$key")"cursor=0seen=0while:;domapfile-treply<<(redis-cli-u"$redis_url"--rawHSCAN"$key""$cursor"COUNT80)cursor="${reply[0]}"seen=$((seen+(${#reply[@]}-1)/2))[["$cursor"==0]]&&breakdoneprintf'hash_fields=%s\n'"$count"printf'scanned_fields=%s\n'"$seen"printf'unlinked=%s\n'"$(redis-cli-u"$redis_url"--rawUNLINK"$key")"

四、迁移:治理动作本身也要限流

拆 key 不能一次切换。先让写路径双写旧、新结构;后台按游标回填历史数据;读取路径优先新结构,缺失时回退旧结构并补写;核对数量、校验和与业务抽样后停止旧写;最后等待回滚窗口再UNLINK。双写不是事务,必须幂等并记录失败。Cluster 跨槽双写还不能依赖普通事务保证原子。

随机过期旧 key,或一次性删除大量 key,都可能制造内存与回源尖峰。设置每秒迁移条数和字节预算,观察used_memory、事件循环延迟、复制积压与副本 lag。若副本追不上,继续迁移只会扩大故障域。生产者写入无界集合时,治理重点不是定期清理,而是把容量上限写进数据模型。

热 key 的本地缓存也有代价:进程越多,总内存越大,失效广播越复杂。极高价值且很小的配置适合这种方式;用户权限等敏感数据必须用短 TTL、版本校验,不能让撤权长期不生效。复制热点 key 时避免使用哈希标签把副本又固定到同一 Cluster 槽。

五、验证:建立按前缀的容量与热度预算

每类 key 应有 owner、预计数量、单项大小、最大基数、TTL、读写 QPS 和删除方式。监控按业务前缀聚合,而不是把完整用户 ID 作为指标标签造成高基数。报警后保留候选 key、命令、客户端和时间窗口,才能复盘流量来源。

压测要同时模拟请求分布和 value 大小。均匀随机流量无法复现热点;只有小 value 的基准也看不到网络阻塞。观察 p99/p999、Redis CPU、网卡、客户端连接池等待和复制延迟。治理后若实例平均 QPS 不变但最热分片下降、尾延迟收敛,才说明目标达成。

当热点操作需要“只有一个客户端执行”时,人们常顺手写SETNX,却忽略租约、误删和故障恢复。下一篇将把这些风险收束成一把有明确安全边界的分布式锁。

治理验收不要只比较一次MEMORY USAGE。至少跨一个业务高峰记录最热节点 CPU、每秒出站字节、命令 p99、复制 lag 和候选 key 的访问分布;拆分后还要确认总 key 元数据没有反向吞掉节省的内存。若采用本地缓存,额外测量配置变更传播的最长时间,并在通知系统中断时验证 TTL 能自动收敛。对大集合分页接口设置最大页大小和游标有效期,防止调用者绕过保护重新发起全量导出。

参考来源

  • Redis 官方文档:诊断延迟问题
  • Redis 官方文档:MEMORY USAGE
  • Redis 官方文档:UNLINK

👍 觉得有用就点个赞 + 收藏,方便回头查阅;有疑问直接在评论区留言,我看到都会回。

🚀 本文属于《Redis 应用实战》系列,持续更新,关注不迷路。

📌 文章里的代码都能直接跑。想要可直接 clone 的完整工程 + 配套部署脚本 / 踩坑清单?评论一声或发邮件到cj2664@qq.com,我免费发你。
如果你正好在做类似系统、或有工程化难题想找人做,也欢迎邮件聊一句——我按实际情况评估,能落地的就接单或出方案。评论和邮件都能直接找到我,不用跳别的平台。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 11:36:35

ARM Mali GPU链接问题全解析:从驱动栈到交叉编译调试

1. 从一块板子报错说起&#xff1a;为什么Mali GPU的“链接”这么重要前阵子帮朋友调一块RK3588的开发板&#xff0c;系统是Debian系的ARM64发行版&#xff0c;跑一个OpenGL ES的渲染demo。编译都过了&#xff0c;一执行直接甩了个运行时报错&#xff1a;error while loading s…

作者头像 李华
网站建设 2026/9/6 11:34:10

Redis 应用实战(4):分布式锁实现

上一篇处理了流量和容量集中&#xff0c;本篇转向并发执行集中&#xff1a;多个进程都认为自己应该修改同一资源。Redis 锁只是一份带期限的协调记录&#xff0c;不是数据库事务。可靠边界由原子获取、随机所有者令牌、比较后释放&#xff0c;以及由最终资源验证的 fencing tok…

作者头像 李华
网站建设 2026/9/6 11:33:27

上位机开发实战:从通信协议选型到项目落地全解析

1. 上位机不是"一台电脑"那么简单&#xff1a;先把行业底层逻辑捋清楚1.1 上位机和下位机怎么分工先回答一个很多新人问过我的问题&#xff1a;上位机到底是啥&#xff1f;简单说&#xff0c;上位机就是发出指令、做数据展示和分析的那一端&#xff0c;通常跑在PC、工…

作者头像 李华
网站建设 2026/9/6 11:32:11

腾讯云AI Skills实战:Agent技能开发与编排避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 11:27:14

基于Stable Diffusion的角色定向图像生成:萍琪派鬃毛打理场景实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 11:21:56

JMeter性能测试实战:从安装到压测报告全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华