<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>BigKey on Zampo Blog</title><link>https://blog.cpdd.fyi/tags/bigkey/</link><description>Recent content in BigKey on Zampo Blog</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Wed, 15 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.cpdd.fyi/tags/bigkey/index.xml" rel="self" type="application/rss+xml"/><item><title>我追着 Redis 超时查了一圈，最后是 1200 万 field 的 Hash 卡住了实例</title><link>https://blog.cpdd.fyi/posts/redis-bigkey-timeout-postmortem/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0000</pubDate><guid>https://blog.cpdd.fyi/posts/redis-bigkey-timeout-postmortem/</guid><description>&lt;h1 id="我追着-redis-超时查了一圈最后是-1200-万-field-的-hash-卡住了实例"&gt;我追着 Redis 超时查了一圈，最后是 1200 万 field 的 Hash 卡住了实例&lt;/h1&gt;
&lt;p&gt;接口开始一批批变慢，Redis 超时告警也跟着冒出来。&lt;/p&gt;
&lt;p&gt;这种时候，人很容易把锅先扣给连接池：连接是不是借不出来了，应用是不是堆了一堆等待 Redis 的 goroutine。接着再去翻慢查询，怀疑是不是哪条业务请求突然把数据库拖住了。&lt;/p&gt;
&lt;p&gt;这两步没有错。但它们都在应用侧找“谁等得太久”。这次真正该问的问题是：Redis 在那段时间里，究竟被哪一条命令占住了？&lt;/p&gt;
&lt;p&gt;最后钉住问题的，是一个 Hash。它有 12,102,218 个 field。&lt;/p&gt;
&lt;p&gt;不是连接池先坏了。是这个 Hash 先把 Redis 实例堵住，连接池和慢接口只是后面跟着出现的受害者。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Redis 的 Key 设计，不能只问“能不能放进去”；还要问“任何一次读、删、迁移它时，实例要被占住多久”。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://blog.cpdd.fyi/images/redis-bigkey-timeout-postmortem/cover.png" alt="告警扩散路径示意图"&gt;&lt;/p&gt;
&lt;h2 id="先别急着扩容先看谁把实例占住了"&gt;先别急着扩容，先看谁把实例占住了&lt;/h2&gt;
&lt;p&gt;排查一开始还是按常规路径走：连接池、调用链、慢查询。&lt;/p&gt;
&lt;p&gt;连接池图能告诉你“有很多请求正在等”；慢查询能告诉你“业务链路某一段变长了”。但两张图都不等于根因。尤其当超时集中指向同一组 Redis 实例时，继续在应用侧猜，会把排查带偏。&lt;/p&gt;
&lt;p&gt;应该把视线收回 Redis 自己的延迟。&lt;/p&gt;
&lt;p&gt;公开案例里，业务侧先观察到访问 Redis 超时，&lt;code&gt;HKEYS&lt;/code&gt; 平均响应约 200 ms，最大超过 500 ms。实例监控则显示，平时响应约 0.1 ms，异常时出现约 70 ms 的突刺，最高约 100 ms。[1]&lt;/p&gt;
&lt;p&gt;这两组数字放在一起，事情就清楚了：不是所有请求都慢，而是某些时刻有命令占住了实例。后面的命令排队，客户端看到的时间就被拉长。&lt;/p&gt;
&lt;p&gt;这也是 Redis 超时最容易被误判的地方。你看到的是调用方等了 500 ms；真正消耗实例的，可能只是一段 70 ms 到 100 ms 的执行窗口。对一个本来以亚毫秒响应为常态的实例来说，这已经够把后面一串请求堵住。&lt;/p&gt;
&lt;p&gt;别把“平均延迟还可以”当成没事。对在线服务更危险的，往往是那根偶尔冒出来、却把队列压长的尖刺。&lt;/p&gt;
&lt;h2 id="慢的不是-redis是你让它一次做得太多"&gt;慢的不是 Redis，是你让它一次做得太多&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;HKEYS&lt;/code&gt; 的语义很直接：取出一个 Hash 的全部 field。问题不在命令名字，而在“全部”两个字。&lt;/p&gt;</description></item></channel></rss>