突然有几十万甚至更大的请求去访问redis上的某个特定key。那么,这样会造成流量过于集中,达到Redis单实例瓶颈(一般是10W OPS级别),或者物理网卡上限,从而导致这台redis的服务器Hold不住。 那接下来这个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协议规则解析数据,进行分析。缺点就是开发成本高,维护困难,有丢包可能性。
(1)二级缓存(推荐)
比如利用ehcache
,或者guava-cache
,或者一个HashMap或者List
都可以。在你发现热key以后,把热key加载到JVM中(可以是堆内,也可以是堆外)。针对这种热key请求,会直接从JVM中取,而不会走到redis层。
假设此时有十万个针对同一个key的请求过来,如果没有本地缓存,这十万个请求就直接怼到同一台redis上了。现在假设,你的应用层有10台机器,OK,你也有jvm缓存了。这十万个请求平均分散开来,每个机器有10000个请求,会从JVM中取到value值,然后返回数据。避免了十万个请求怼到同一台redis上的情形。
(2)备份热点key 这个方案也很简单。不要让key走到同一台redis上不就行了。我们把这个key,在多个redis上都存一份不就好了。接下来,有热key请求进来的时候,我们就在有备份的redis上随机选取一台,进行访问取值,返回数据。 假设redis的集群数量为N,步骤如下图所示:
const M = N * 2
//生成随机数
random = GenRandom(0, M)
//构造备份新key
bakHotKey = hotKey + “_” + random
data = redis.GET(bakHotKey)
if data == NULL {
data = GetFromDB()
redis.SET(bakHotKey, expireTime + GenRandom(0,5))
}
说明:这种方案有一个很明显的缺点,就是缓存的维护代价非常大。假设有100个备份KEY,那么在删除或者更新时,也需要更新100个KEY,所以这种方案不是很推荐。
TMC ,即“透明多级缓存( Transparent Multilevel Cache )”,是有赞 PaaS 团队给公司内应用提供的整体缓存解决方案。
TMC 在通用“分布式缓存解决方案(如 CodisProxy + Redis ,如有赞自研分布式缓存系统 zanKV )”基础上,增加了以下功能:
以帮助应用层解决缓存使用过程中出现的热点访问问题。
改写了jedis原生的jar包,加入了Hermes-SDK包。 Hermes-SDK包用来做热点发现和本地缓存。
有赞在监控到热key后,Hermes服务端集群会通过各种手段通知各业务系统里的Hermes-SDK,告诉他们:”老弟,这个key是热key,记得做本地缓存。” 于是Hermes-SDK就会将该key缓存在本地,对于后面的请求。Hermes-SDK发现这个是一个热key,直接从本地中拿,而不会去访问集群。
除了这种通知方式以外。我们也可以这么做,比如你的流式计算系统监控到热key了,往zookeeper里头的某个节点里写。然后你的业务系统监听该节点,发现节点数据变化了,就代表发现热key。最后往本地缓存里写,也是可以的。
https://jishuin.proginn.com/p/763bfbd358db