热 key 指的是那些在一段时间内访问频次比较高的键值,具体到业务上,商品的限时抢购、瞬时的新闻热点或某个全局性的资源,都极有可能产生热点 key。
设计定位方案的话,我们可以从 Redis 请求路径上的节点来着手,比如在客户端、中间层和服务端,具体来说如下:
客户端收集上报
改动 Redis SDK,记录每个请求,定时把收集到的数据上报,然后由一个统一的服务进行聚合计算。方案直观简单,但没法适应多语言架构,一方面多语言 SDK 对齐是个问题,另外一方面后期 SDK 的维护升级会面临比较大的困难,成本很高。
代理层收集上报
如果所有的 Redis 请求都经过代理的话,可以考虑改动 Proxy 代码进行收集,思路与客户端基本类似。该方案对使用方完全透明,能够解决客户端 SDK 的语言异构和版本升级问题,不过开发成本会比客户端高些。
Redis 数据定时扫描
Redis 在 4.0 版本之后添加了 hotkeys 查找特性[1],可以直接利用 redis-cli –hotkeys 获取当前 keyspace 的热点 key,实现上是通过 scan + object freq 完成的。该方案无需二次开发,能够直接利用现成的工具,但由于需要扫描整个 keyspace,实时性上比较差,另外扫描耗时与 key 的数量正相关,如果 key 的数量比较多,耗时可能会非常长。
Redis 节点抓包解析
在可能存在热 key 的节点上(流量倾斜判断),通过 tcpdump 抓取一段时间内的流量并上报,然后由一个外部的程序进行解析、聚合和计算。该方案无需侵入现有的 SDK 或者 Proxy 中间件,开发维护成本可控,但也存在缺点的,具体是热 key 节点的网络流量和系统负载已经比较高了,抓包可能会情况进一步恶化。
Redis 的 Monitor 命令不在考虑之列,原因是开销比较大,单个 monitor 的 client 会降低 50% 的系统吞吐
https://redis.io/commands/monitor
饿了么内部,所有的 Redis 请求都是经过透明代理 Samaritan[2] 的
每个 client 内部都有自己的 Hotkey Collector,不同 Collector 间相互独立
包含 LFU Counter、Syncer 和 Etrace Client 三部分
基本的工作流程是,LFU Counter 负责记录 key 的访问频次,Syncer 会定期将统计数据通过 Etrace Client 发送给远端的服务器。另外,为了避免向服务端发送过多无效的数据,内部会预先设置一个阈值,超过阈值的才发送到服务端。
https://zhuanlan.zhihu.com/p/127128142
怎么发现热key
方法一:凭借业务经验,进行预估哪些是热key
其实这个方法还是挺有可行性的。比如某商品在做秒杀,那这个商品的key就可以判断出是热key。缺点很明显,并非所有业务都能预估出哪些key是热key。
方法二:在客户端进行收集
这个方式就是在操作redis之前,加入一行代码进行数据统计。那么这个数据统计的方式有很多种,也可以是给外部的通讯系统发送一个通知信息。缺点就是对客户端代码造成入侵。
方法三:在Proxy层做收集
有些集群架构是下面这样的,Proxy可以是Twemproxy,是统一的入口。可以在Proxy层做收集上报,但是缺点很明显,并非所有的redis集群架构都有proxy。
方法四:用redis自带命令
(1)monitor命令,该命令可以实时抓取出redis服务器接收到的命令,然后写代码统计出热key是啥。当然,也有现成的分析工具可以给你使用,比如redis-faina。但是该命令在高并发的条件下,有内存增暴增的隐患,还会降低redis的性能。
(2)hotkeys参数,redis 4.0.3提供了redis-cli的热点key发现功能,执行redis-cli时加上–hotkeys选项即可。但是该参数在执行的时候,如果key比较多,执行起来比较慢。
方法五:自己抓包评估
Redis客户端使用TCP协议与服务端进行交互,通信协议采用的是RESP。自己写程序监听端口,按照RESP协议规则解析数据,进行分析。缺点就是开发成本高,维护困难,有丢包可能性。
以上五种方案,各有优缺点。根据自己业务场景进行抉择即可。那么发现热key后,如何解决呢?
如何解决
目前业内的方案有两种
(1)利用二级缓存
比如利用ehcache,或者一个HashMap都可以。在你发现热key以后,把热key加载到系统的JVM中。
针对这种热key请求,会直接从jvm中取,而不会走到redis层。
假设此时有十万个针对同一个key的请求过来,如果没有本地缓存,这十万个请求就直接怼到同一台redis上了。
现在假设,你的应用层有50台机器,OK,你也有jvm缓存了。这十万个请求平均分散开来,每个机器有2000个请求,会从JVM中取到value值,然后返回数据。避免了十万个请求怼到同一台redis上的情形。
(2)备份热key
这个方案也很简单。不要让key走到同一台redis上不就行了。我们把这个key,在多个redis上都存一份不就好了。接下来,有热key请求进来的时候,我们就在有备份的redis上随机选取一台,进行访问取值,返回数据。
https://www.cnblogs.com/leeego-123/p/11588429.html
https://zhuanlan.zhihu.com/p/133796900
https://blog.csdn.net/weixin_45583158/article/details/105236620
https://zhuanlan.zhihu.com/p/127128142
https://github.com/facebookarchive/redis-faina
$ ./redis-faina.py ./m.log
Overall Stats
========================================
Lines Processed 44 – 总命令数
Commands/Sec 0.63 – qps
n/a
userToken 21 (47.73%)
userSession 21 (47.73%)
HGET 40 (90.91%)
HSET 2 (4.55%)
COMMAND 1 (2.27%)
Median 394010.0
75% 447155.0
90% 8218888.25
99% 16510561.75
HGET 57269310.75
HSET 8219184.75
COMMAND 4601067.25
16510561.75 “HGET” “userSession” “7b8a4f64e03c7e24586f2fb2d705b232”
14359969.25 “HGET” “userSession” “1dcb54f57c3d9775c713ea8f97ae0ebc”
9015870.0 “HGET” “userSession” “7b8a4f64e03c7e24586f2fb2d705b232”
8881043.0 “HGET” “userSession” “1dcb54f57c3d9775c713ea8f97ae0ebc”
8218888.25 “HSET” “userSession” “b52de307a793b35d79e914a1d7b26028” “1502348079”
4601067.25 “COMMAND”
1396710.25 “HGET” “userSession” “123”
1305825.25 “HGET” “userSession” “123”
https://blog.csdn.net/u010522235/article/details/89238799
2.redis-live
RedisLive是一款用Python编写的Redis图形监控工具。RedisLive的原理很简单,就是通过监控脚本来利用Redis提供的MONITOR命令从被监控Redis实例中获取数据并存储到Redis的监控实例中来做数据分析。RedisLive以可视化的方式展示了Redis实例中的数据,分析查询模式和峰值
https://www.cnblogs.com/yxlblogs/p/11365024.html
https://github.com/nkrode/RedisLive
https://www.oschina.net/p/redis-faina?hmsr=aladdin1e1
https://blog.csdn.net/cjfeii/article/details/77069778