如何快速定位 Redis 热 key?

在 redis 中 , 热 key 指的是那些在一段时间内访问频次比较高的键值 , 具体到业务上 , 商品的限时抢购、瞬时的新闻热点或某个全局性的资源 , 都极有可能产生热点 key 。
 
热点 key 的出现可能会对系统的稳定性和可用性造成影响 , 比如对应节点的网卡带宽被打满 , 出现丢包重传 , 请求波动耗时大幅上升 , 甚至影响到业务的正常使用 , 引发用户的不满 。因此 , 在日常的工作中 , 我们需要着重避免这种情况的出现 , 比如在设计和编码阶段避免引入全局性热 key , 或者在设计时考虑热 key 出现时的应对方案 。
 
可能的方案 
热点 key 即使我们在设计和开发时已经极力避免 , 然而在真实的生产环境中还是可能依旧存在的 , 导致其继续出现的原因有以下几种:

  • 有一些边界 case 没有考虑到
  • 异常或非预期的流量
 
既然不可能完全避免 , 我们就需要有一种方法能够在出问题的时候快速定位有没有热 key 以及热 key 具体是啥 , 来帮助业务快速排障 , 定位问题的根源 。如果要设计定位方案的话 , 我们可以从 Redis 请求路径上的节点来着手 , 比如在客户端、中间层和服务端 , 具体来说如下:
  1. 客户端收集上报改动 Redis SDK , 记录每个请求 , 定时把收集到的数据上报 , 然后由一个统一的服务进行聚合计算 。方案直观简单 , 但没法适应多语言架构 , 一方面多语言 SDK 对齐是个问题 , 另外一方面后期 SDK 的维护升级会面临比较大的困难 , 成本很高 。
  2. 代理层收集上报如果所有的 Redis 请求都经过代理的话 , 可以考虑改动 Proxy 代码进行收集 , 思路与客户端基本类似 。该方案对使用方完全透明 , 能够解决客户端 SDK 的语言异构和版本升级问题 , 不过开发成本会比客户端高些 。
  3. Redis 数据定时扫描Redis 在 4.0 版本之后添加了 hotkeys 查找特性[1] , 可以直接利用 redis-cli --hotkeys 获取当前 keyspace 的热点 key , 实现上是通过 scan + object freq 完成的 。该方案无需二次开发 , 能够直接利用现成的工具 , 但由于需要扫描整个 keyspace , 实时性上比较差 , 另外扫描耗时与 key 的数量正相关 , 如果 key 的数量比较多 , 耗时可能会非常长 。
  4. Redis 节点抓包解析在可能存在热 key 的节点上(流量倾斜判断) , 通过 tcpdump 抓取一段时间内的流量并上报 , 然后由一个外部的程序进行解析、聚合和计算 。该方案无需侵入现有的 SDK 或者 Proxy 中间件 , 开发维护成本可控 , 但也存在缺点的 , 具体是热 key 节点的网络流量和系统负载已经比较高了 , 抓包可能会情况进一步恶化 。
Redis 的 Monitor 命令不在考虑之列 , 原因是开销比较大 , 单个 monitor 的 client 会降低 50% 的系统吞吐 , 更多详情见: https://redis.io/commands/monitor
 我们的选择 
由于在饿了么内部 , 所有的 Redis 请求都是经过透明代理 Samaritan[2] 的 , 并且该代理是由我们自己开发维护的 , 在代理层改造的成本完全受控 , 因此我们选择了方案二 , 即在代理层进行收集上报 。
 
大的方向确定之后 , 需要考虑具体的细节 , 比如:
  1. 记录所有请求如何能够保证不占用过多的内存甚至 OOM ?
  2. 记录所有请求如何能够保证代理的性能, 请求耗时不会有明显的上升?
 
针对第 1 点 , 既然我们只关心热 key 而不是要统计所有 key 的 counter , 那么就可以用 LFU 只保留访问频次最高的 , 第 2 点则需要结合代理具体的实现去考虑 。
 
下图是代理内部的实现方案, 略去了一些无关的细节:
如何快速定位 Redis 热 key?

文章插图
 
注: