背景
我们知道在面试中,面试官经常会问一些关于高并发的问题,其中热点key问题就是一个热门问题,热点key顾名思义就是访问非常频繁的key,再加上高并发访问条件,热点key缓存问题如何解决。接下来我们从以下三个方面来剖析一下:
1、热点key定义
2、如何发现热点key
3、解决方案
热点key定义
突然有几十万甚至更大的请求去访问redis上的某个特定key。这样会造成流量过于集中,达到例如Redis单实例瓶颈(一般是10W OPS级别),或者物理网卡上限,从而导致这台redis的服务器Hold不住。后果就是接下来对这个key的请求,将会压垮缓存服务
如何发现热点key
1、凭借业务经验,预估出那些访问频繁的热点key
优点:可行性高,例如秒杀商品业务中,秒杀的商品都是热点key
缺点:并非所有的业务都容易预估出热点key,例如商品推荐业务,你事先是无法料之客户的喜好的
2、客户端收集
优点:实现简单方便,只需要在访问redis客户端之前加入一行代码进行数据统计,统计方式多种多样,有本地计数、发消息单独处理统计等
缺点:对客户端代码造成入侵
3、在Proxy(代理层)收集
优点:proxy层统一入口做统计,对业务代码无入侵
缺点:要求缓存架构有proxy层结构才行,例如Proxy可以是Twemproxy
4、使用redis自带命令
4.1、monitor命令
monitor命令可以实时抓取出redis服务器接收到的命令,然后写代码统计出热key是啥。当然,也有现成的分析工具可以给你使用,比如redis-faina。
缺点:该命令在高并发的条件下,不仅有内存暴增的隐患,还会降低redis的性能
4.2、hotkeys参数
优点:redis 4.0.3提供了redis-cli的热点key发现功能,执行redis-cli时加上–hotkeys选项即可,操作方便
缺点:该参数在执行的时候,如果key比较多,执行起来比较慢
5、自己抓包分析
Redis客户端使用TCP协议与服务端进行交互,通信协议采用的是RESP。自己写程序监听端口,按照RESP协议规则解析数据,进行分析。
缺点:开发成本高,维护困难,有丢包可能性
以上五种发现热点key的方案,各有优缺点,这个要根据自己的业务场景选择最适合自己的就行
解决方案
1、使用二级缓存(极力推荐)
例如使用 guava-cache,或者 ehcache,或者最常用的集合 Hash 等,发现热点key之后,将这些热点key加载到JVM中(可以是堆内,也可以是堆外)作为本地缓存。访问这些key时直接从本地缓存获取即可,不会直接访问到redis层了,有效的保护了缓存服务器
2、备份热点key
不要让同一个key都存放到同一台redis机器上,我们把这个key在多个redis上都存一份。当有热key请求进来的时候,我们就在有备份的redis上随机选取一台,进行访问取值,返回数据。
假设redis的集群数量为N,步骤如下图所示:
缺点:缓存的维护代价非常大,假设有100个备份KEY,那么在删除或者更新时,也需要更新100个KEY,所以这种方案不是很推荐
热点key拓展(业内方案)
对于上面的发现热点key和解决方案了解后,我们想有办法在项目运行过程中,自动发现热点key,然后程序自动处理么?这个在业内是怎么做的呢?其实只有两步:
1、监控热点key
2、通知系统做处理
业务有赞出了一篇《有赞透明多级缓存解决方案(TMC)》,里头也有提到热点key问题,我们刚好借此说明学习一下
1)、监控热点key
有赞用的是方式二:在客户端进行收集
在《有赞透明多级缓存解决方案(TMC)》中有一句话提到:
TMC 对原生jedis包的JedisPool和Jedis类做了改造,在JedisPool初始化过程中集成TMC“热点发现”+“本地缓存”功能Hermes-SDK包的初始化逻辑
有赞改写了jedis原生的jar包,加入了Hermes-SDK包。那Hermes-SDK包用来干嘛?答案是做热点发现和本地缓存。从监控的角度看,该包对于Jedis-Client的每次key值访问请求,Hermes-SDK 都会通过其通信模块将key访问事件异步上报给Hermes服务端集群,以便其根据上报数据进行“热点探测”。
除了这种方式之外,有的公司在监控方面用的是方式五: 自己抓包分析
具体是这么做的,就是先利用flink搭建一套流式计算系统。然后自己写一个抓包程序抓redis监听端口的数据,抓到数据后往kafka里丢。接下来,流式计算系统消费kafka里的数据,进行数据统计即可,也能达到监控热key的目的。
2)、通知系统做处理
有赞用的是上面的解决方案一:使用二级缓存
有赞在监控到热key后,Hermes服务端集群会通过各种手段通知各业务系统里的Hermes-SDK,告诉他们:"老弟,这个key是热key,记得做本地缓存。"于是Hermes-SDK就会将该key缓存在本地,对于后面的请求。Hermes-SDK发现这个是一个热key,直接从本地中拿,而不会去访问集群。
除了这种通知方式以外。我们也可以这么做,比如你的流式计算系统监控到热key了,往zookeeper里头的某个节点里写。然后你的业务系统监听该节点,发现节点数据变化了,就代表发现热key。最后往本地缓存里写,也是可以的。
总结
对于热点key问题,我们只需要两步处理:
1、监控热点key
2、通知系统做处理