Redis热点Key解决方案


一、背景

Squirrel是基于redis cluster开发的K-V存储系统,对某个key进行读写时,会根据该key的hash计算出对应的slot,根据这个slot就能找到与之对应的分片(一个master和多个slave组成的一组redis集群)来存取该K-V,如果集群不发生服务器数量变化,那么这一映射关系就不会变化。但是在实际应用过程中,对于某些特定业务或者一些特定的时段,可能会发生大量的请求访问同一个key。所有的请求(且这类请求读写比例非常高)都会落到同一个redis server上,该redis的负载就会严重加剧,此时整个系统增加新redis实例也没有任何用处,因为根据hash算法,同一个key的请求还是会落到同一台新机器上,该机器依然会成为系统瓶颈。若此热点 key 的 value 也比较大,也会造成网卡达到瓶颈。这个问题称为 “热点key” 问题。

  • 对于读命令,如果客户端开启了master-slave配置,所有的请求会平均落到这组分片里面满足路由策略的所有节点,此时我们还可以通过水平增加从节点来解决。
  • 对于写命令,所有的请求都会落所属分片master节点。此时通过水平增加从节点已经解决不了问题了。

二、现状

随着公司业务规模越来越大,基本上所有的业务都接入了Squirrel,目前我们线上redis集群支撑了每天服务端万亿+、端到端3000亿+的调用量。用户因为一些业务的特性触发热点问题是业务需要,是不可避免,理所当然的,难免会触发“热点key问题”。在发现有热点的情况下,如果继续放任该热点无限激增,就会带来整个系统雪崩似宕机。之前的做法会根据QPS或带宽,如果发现了热点key,我们就会对用户进行流控,限流会对热点key所属的category整体进行限流。这样确实能暂时解决热点key问题,但是由于少数几个热点key就造成整Category不可用,显然对于用户来说是不可接受的。用户触发热点问题是业务需要,是理所当然;Squirrel对热点key进行流控是保障系统稳定性,也在情理之中。但有没有一种既能提供用户热点key访问的需求,又能保护redis server服务器的方法,正是本文所要阐述的。

三、思考

对于redis热点key问题,目前我们主要面临两个问题:

  • 如何高效的发现热点key
  • 发现热点key后如何处理

目前如何准确实时发现热点key其实对我们来说相对更难一点,如果能准确发现热点key,后续我们可以有很好的方案来解决热点问题。

3.1、热点key的发现

目前业要有以下五种方案来寻找热点key,第五种是我们squirrel团队正在开发的,分别简要介绍。

  1. 客户端:客户端是距离key”最近”的地方,redis的命令每次都是从客户端触发的,基于此我们可以在squirrel-client的一些代码处进行统计计数。但是这种方案也存在一些问题。无法预知key的个数,存在内存泄漏的风险;只能解决当前客户端的热点key,无法实现规模化的运维统计。
  2. 代理端:像Twemproxy,codis这些基于代理的redis分布式架构,所有的客户端请求都是通过代理端完成的。此架构就很适合做热点key的统计,因为代理是所有redis客户端和服务端的桥梁。但是我们squirrel是基于redis cluster直连无中间层的设计,显然这种方案无法满足。
  3. 服务端:redis monitor命令可以统计出一段时间内的某redis节点上的所有命令。因为monitor命令会监控该节点的所有的命令,为了减少网络开销以及加快输出缓冲区的速度,monitor尽可能在本机执行。此种方法也有以下问题:在高并发条件下,会存在内存暴涨和redis性能的隐患,所以此种方法适合在短时间内使用;同样只能统计一个redis节点的热点key,对于集群需要汇总统计。
  4. 机器层面:Redis客户端使用TCP协议与服务端进行交互,通信协议采用的是RESP。如果站在机器的角度,可以通过对机器上所有Redis端口的TCP数据包进行抓取完成热点key的统计。此种方法对于redis客户端和服务
    端来说毫无侵入,但是依然存在问题:需要一定开发成本,由于是以机器为单位进行统计,想要了解集群维度热点key,后期还是需要汇总统计。
  5. 修改 redis server 源码:redis4.0为我们带来了许多新特性,其中便包括基于LFU的热点key发现机制,可以在此基础上实现热点key的发现。这个是我们Squirrel团队最终的解决方案,目前正在开发

目前我们 Squirrel-web 管理平台发现热点Key的方案是分析集群各节点QPS倾斜情况,找出QPS异常高的节点,然后采用上述第三种方案针对该节点调用 redis monitor 命令1-2s 获取节点操作日志,进而通过正则表达式抓取热点Key,分析其中每个Key的操作占比,选取大于设定阈值的Key作为热点Key返回。最终优化方案会采用方案五,目前正在开发。
Squirrel-schedule 作为自动化运维服务,考虑到 1-2s 的时间还是存在阻塞风险,因此换用 Jedis Client 提供的 JedisMonitor API 获取指定条数的日志来代替直接使用时间。

3.2、热点key的处理

  1. 拆分复杂数据结构:如果当前key的类型是一个二级数据结构,例如hash。如果该hash元素个数比较多,可以考虑对当前hash进行拆分,这样热点key可以分拆为若干个新的key分布到不同的redis节点上,从而减轻单节点压力。
  2. 多副本:在查找到了热点key后,目前的一些处理手段把这个热点key增加多个副本来解决,这个策略主要针对读多写少的场景有效,如果都是写的热点key,多副本无效。这个方案我们之前的客户端已经实现了,后来测试下来觉得有有些问题:比如写的时候要写多副本,存在数据一致性问题。所以目前也不建议采用这种方案。
  3. 本地缓存:对于数据一致性不是那么高的业务,可以将热点key缓存到业务机器的本地缓存中,因为是业务端的本地内存中,省去了一次远程的IO调用。但是当数据更新时,可能会造成业务和redis数据不一致。对于这种方案我们目前最新的客户端版本已经实现了基于热点key本地缓存,不需要业务改任何代码,对业务端完全透明。业务可以设置key的过期时间,key过期后我们会从redis服务端获取并更新本地缓存。
  4. 迁移热点key:以 redis cluster 为例,我们可以将热点key所在的 slot 单独迁移到一个新的redis分片。这样这个热点key即使qps很高,也不会影响到整个集群的其他业务。这个方案目前我们也已经实现了,并能做到自动迁移热点key到一个独立的分片。下图是外卖的一个线上业务,红框的这个分片虽然只分配了2个slot,但是单节点qps仍然比其他分片高多了,因为这个热点key被我们迁出去了,它是一个单独的热点分片。
    由于热点Key的产生具有突发性,且不立刻干预的话危害较大,因此 Squirrel-schedule 自动化运维服务会对持续出现的热点Key进行自动化迁移,例如下图:
  5. 热点key限流:对于读命令我们可以通过迁移热点key然后添加从节点来解决,对于写命令我们可以通过单独针对这个热点key来限流,目前我们管理平台也支持这个操作。上面 3.本地缓存的配图里面可以开启单key限流

四、Squirrel解决方案

目前Squirrel已经提供了一套相对成熟的针对热点key的解决方案。覆盖了从如何快速准确的发现热点key,到发现热点key的后续处理。

4.1、整体方案

以上主要分为三个阶段:实时采样,实时分析,热点key处理。目前的架构和最终架构的不同点是如何获取指定节点上的热点key,其他过程都一样。

4.2、实时采样

squirrel-monitor-service会实时采集所有集群的拓扑结构和QPS、网卡流量等监控数据,然后发送到kafka集群。
squirrel-schedule-service会实时消费这些集群监控数据topic。(Squirrel团队模块—Squirrel的架构图)

4.3、实时分析

实时分析主要是基于redis监控数据的处理,实时的分析出QPS存在倾斜的集群,自动找出这些QPS倾斜集群中的热点server,并根据业务配置的白名单来调用redis monitor命令智能的发现热点key。上面说了redis monitor命令存在内存暴涨和性能问题,所以我们严格控制调用时间是1-2s。我们目前正在对redis源码进行修改,最终会支持redis server端自动发现热点key,并且对性能没有任何影响

4.4、热点key处理

实时分析阶段发现了集群中存在热点key。业务如果对于数据一致性要求没那么高的情况下,建议接入最新的squirrel-client并在管理平台上配置热点key本地缓存。当然如果业务要求严格的数据一致性,我们也可以自动迁移热点key到单独的分片。

五、热点Key相关参数配置

热点Key配置页位于集群运维页的热点Key标签页下:

其中开启自动扫描后,可以自动发现集群中存在的热点key


文章作者: 叶明
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 叶明 !
评论
 上一篇
Postman和Java Client访问K8s Postman和Java Client访问K8s
K8s的所有操作基本都是通过调用kube-apiserver这个组件进行的,它提供了restful api供外部系统访问,当然为了保证整个k8s集群的安全性,k8s提供了多种认证方式来保证集群的安全性:比如客户端证书、静态token、静态密
2018-11-11
下一篇 
从零开始学TIDB二(TIDB简介与整体架构) 从零开始学TIDB二(TIDB简介与整体架构)
上一篇博客,我们简单介绍了怎么在Mac OS系统上面使用docker compose搭建一个包含3个pd,3个tikv,1个tidb的TIDB集群。本文,我们会详细介绍TIDB的一些基本概念和整体架构。 TIDB 简介TIDB是pingca
2018-08-27
  目录