何为热key问题?
从名字上可以理解,Redis中的热key就是在Redis中频繁被访问的数据,例如热点新闻,热点评论,双十一商品等等。当某一热key的请求到Server主机时,这样会造成流量过于集中,达到物理网卡上限,从而导致这台redis的主机资源不足,甚至宕机。那接下来这个key的请求,就会直接怼到你的数据库上,导致你的服务不可用。
热点数据对服务器来说,是一个巨大的隐患,以redis-cluster 来说,它可能会造成整体流量不均衡,个别ops过大的情况,极端的情况下,可能直接超过redis本事的承受能力。
热key问题产生的原因
- 用户消费的数据远远大于生产的数据,比如热卖商品、热点新闻、热点评论等,这些典型的读多写少的场景会产生热点问题。
2.在请求分片集中,超过单Server的性能极限,比如固定名称的key,hash落入一台Server,在访问量极大的情况下,超过Server极限时,就会导致热点问题的产生。
热key问题的危害
- 流量过于集中,达到物理网卡上限,从而导致这台redis的主机资源不足,甚至宕机
- 请求过多,缓存分片服务被打垮,不能通过扩容解决,且不能发挥集群多分片的优势
- 可能会造成缓存雪崩和缓存穿透
如何发现热key?
-
客户端代码统计
即在操作redis之前,加入一行代码进行数据统计。这个数据统计的方式有多种,也可以是给外部的通讯系统发送一个通知信息。但是缺点是有代码入侵,且维护成本较高,大规模落地肯定不合适,万一热key多了,还有可能oom。 -
凭借业务经验,进行预估
例如提前知道了某个活动的开启,那么就将此Key作为热点Key。但不是每个人都有这个经验的。 -
如果服务端有代理层,可以在代理层进行收集上报
在代理层做收集上报的话,首先,得用了代理,例如codis之类的。因此缺点也很明显,并非所有的redis集群架构都有proxy,而且需要做二次开发,肯定得投入人力改proxy源码,不论难不难,都要考虑一个稳定性和维护成本。 -
用redis自带命令
- hotkeys参数,redis于4.0.3版本开始正式支持基于LFU的热点key发现机制,执行redis-cli时加上–hotkeys选项即可。但是该参数在执行的时候,如果key比较多,执行起来比较慢,且只适用于缓存淘汰策略是lfu的时候。
(可以参考https://yq.aliyun.com/articles/278922) - monitor命令,该命令可以实时抓取出redis服务器接收到的命令,然后写代码统计出热key是啥。当然,也有现成的分析工具可以给你使用,比如redis-faina。但是monitor命令在高并发的场景下,会存在内存暴增,影响redis的性能,只适合短时间的统计,不适合一直使用,只能统计单节点的热点key,对于集群需要进行汇总统计。
- hotkeys参数,redis于4.0.3版本开始正式支持基于LFU的热点key发现机制,执行redis-cli时加上–hotkeys选项即可。但是该参数在执行的时候,如果key比较多,执行起来比较慢,且只适用于缓存淘汰策略是lfu的时候。
-
TCP消息抓包
Redis客户端使用TCP协议与服务端进行交互,通信协议采用的是RESP。如果站在机器的角度,可以通过对机器上所有Redis端口的TCP数据包进行抓取完成热点key的统计。但是依然存在3个问题:- 需要一定的开发成本,但是一些开源方案实现了该功能,例如ELK(ElasticSearch Logstash Kibana)体系下的packetbeat[2] 插件,可以实现对Redis、MySQL等众多主流服务的数据包抓取、分析、报表展示;
- 对于高流量的机器抓包,对机器网络可能会有干扰,同时抓包时候会有丢包的可能性;
- 维护成本过高。
如何解决热key问题?
- 拆分复杂数据结构: 如果当前key的类型是一个二级数据结构,例如哈希类型。如果该哈希元素个数较多,可以考虑将当前hash进行拆分,这样该热点key可以拆分为若干个新的key分布到不同Redis节点上,从而减轻压力。
- 备份热点key:不要让key走到同一台redis上就行了,把热点key在多个redis上都存一份,有热key请求进来的时候,我们就在有备份的redis上随机选取一台,进行访问取值,返回数据。
假设redis的集群数量为N,步骤如下图所示:
不一定是2N,你想取3N,4N都可以,看要求。 - 本地缓存加通知机制:可以将热点key放在业务端的本地缓存中,针对这种热key请求,会直接从JVM中去,而不通过redis层。因为是在业务端的本地内存中,处理能力要高出Redis数十倍,但当数据更新时,此种模式会造成各个业务端和Redis数据不一致,通常会使用发布订阅机制来解决类似问题。
(亦可参考https://www.iteye.com/blog/carlosfu-2269687)