前两天,咱们应用的某云厂商服务挂了,而且一挂就是挂大半天,咱们的服务强依赖于他们,所以咱们也跟着一起挂。然而咱们却无能为力,只能等他们复原。事变起因中听他们提到Redis有个热key,正好我在上家公司负责过部门Redis集群,也解决过很多起Redis数据热点的问题,接下来就一起聊聊什么是Redis热点?Redis热点问题为什么会极大地影响整个集群的性能?如何防止Redis数据热点?热点问题如何排查?热点问题如何解决?
什么是Redis热点?
我曾在我过往的博文中屡次提到了局部性一词(对于局部性能够看下我之前的博文 局部性原理),数据热点就是数据拜访局部性的体现,具体表现就是Redis中某个Key的拜访频次远大于其余残余的Key,咱们也有一句俗语来形容这种景象旱的旱死,涝的涝死。
为什么Redis会有热点问题?这就得从Redis的原理说起了。家喻户晓,Redis中存储的是K-V数据,在集群模式下,Redis会将所有数据按Key的CRC64值调配到16384个数据槽(slot)中,并将这16384个数据槽调配到集群中各个机器上,尽可能实现数据在各个机器上的平均存储。但平均存储并不意味着平均拜访,有时候某个Key的申请会占到总申请的很大一部分,这就会导致申请集中在某个Redis实例上,将该Redis实例的承载能力耗尽,所有存储在这个实例上的其余数据也就无奈失常拜访了,这就意味着所有依赖于这些数据的服务都会出问题。
这里的出问题并不是说Redis会间接宕机,家喻户晓Redis的外围流程是单线程模式,这就意味着Redis是串行解决所有申请的,当申请过多时,申请就会拥挤起来,从应用层的视角来看,就是申请Redis的耗时会特地特地高。因为应用层应用Redis都是作为缓存,都是同步申请,所以会间接导致应用层的申请解决也会特地耗时,从而导致应用层申请也逐步拥挤,最终整体不可用。
咱们来举个简略的例子,置信大家都在微博上吃过瓜,当有大瓜呈现时,微博上会迅速涌入一批用户检索相干信息,疯狂拜访同一份微博(数据),这种状况下这份数据就是热点数据,如果数据太“热”最终会导致微博挂掉,实际上微博挂掉的状况曾经呈现很屡次了,不是因为微博技术不行,而是因为热点问题太可怕。
Redis热点是如何拖垮其余服务的?
Redis的热点并不仅仅会导致单个服务异样,而是会导致所有依赖于此Redis集群的所有服务异样。上图中Server2、Server、Server3频繁拜访XXX_KEY,导致RedisServer2实例不可用,因为Server4依赖于RedisServer2上的Key7,即使是RedisServer1 3 4 5 都失常服务,Server4也无奈对外提供失常的服务。
是不是有人会问RedisServer2挂了,不能把它下掉,换一个新的机器下来吗? 其实在Redis集群模式下,某个实例宕机Redis集群会主动将其替换。但当初的状况是,即使是替换了新实例,大量的申请也会一下子涌进来将其压挂。所以面对Redis热点问题,重启之类的伎俩是有效的,只能从申请端解决问题。
其实这就是一个很显著的 木桶模型, 一个木桶所能装水的多少取决于木桶上最短的那块木板,当利用应用Redis集群时,Redis集群的性能下限并不齐全等于单实例下限乘以实例个数。 但当Redis集群中任一实例有问题,下层感知到的就是整个Redis集群有问题。
Redis热点如何防止?
上文也说到,热点问题其实是局部性问题,而局部性问题的防止其实十分难,任何分布式系统简直都会受局部性的影响。面对这种问题,说实话没有相对能够防止的形式,只能提前通过剖析数据的个性,做好相应的措施。说白了就是靠教训,不晓得大家有啥其余好的思路,能够在评论区探讨下。
热点问题如何排查?
Redis的热点的问题其实算是很好查的,就是靠监控数据,监控Redis各个实例的CPU使用率、QPS 数据,如果你看到redis集群中某些实例负载和QPS特地高,但其余实例负载很低,不必问必定是呈现热点问题了,接下来你须要做的就是找出具体的热点key,并且找出数据拜访的起源。
找出热点Key其实也很简略,抓局部拜访日志,而后统计下很容易就看进去了。但比拟难的是找出数据拜访的起源,像我之前所在的公司,同一个Redis集群是被很多业务所共享的,但Redis的拜访并为被纳入到全链路监控的数据中,所以找出拜访起源最间接的形式就是在群外面问,听起挺原始的,但也没有其余办法。
热点问题如何解决?
尽管解决问题最好的形式是防止问题的产生,但我方才也说了,Redis热点其实很难防止,任何业务中热点肯定有,但不肯定会造成劫难而已。 热点的发现不肯定非得是事变引出的,咱们也能够在日常工作中定期巡检,一有发现热点的苗头,间接将其扼杀。
对于发现热点后如何解决,我这里提供两个我的解决思路,大家能够探讨下:
应用层Cache
罕用的实现形式就是在利用里实现本地缓存(LocalCache),就相当于是对Redis数据又加了一层Cache,对于那些十分热的热点数据,应用层有极大的概率能在本地缓存中找到数据,只有极小局部LocalCache数据过期时的申请会漏到上面,这样热点数据的申请在应用层外部就能消化,从而极大缩小对Redis的压力。
这种实现办法的话,仅须要数据读取端做革新,数据写入端齐全不须要革新。然而毛病的话也很显著:
- 须要各端自行实现,会减少应用层开发和保护老本。
- 会额定节约各端的存储空间。
- 须要针对性开发,不适宜大范畴推广。
减少数据正本
既然热点问题是因为某个Key被大量拜访导致的,那咱们将这个Key的申请做下拆分不就行了。例如,原始的热点Key叫做XXX_KEY,咱们在数据写入的时候,能够用不同的Key反复写10份,比方 XXX_KEY_01, XXX_KEY_02……XXX_KEY_10, 拜访的时候在原始Key上随机凭接一个1-10之间的后缀即可,这样就能实现数据申请的扩散,如果想让申请更扩散,能够存储更多的正本。
这种计划的长处就是数据读取端实现老本较低(也不是齐全没有),但对数据写入端的要求就高多了。不仅要写入多份,而且还得思考数据写入后一致性的问题。这种办法要求两端都得改,貌似更麻烦了,你是不是感觉还不如第一种计划?实则不然,一般来说读取端会很多且很扩散,革新的老本会十分高,频繁变动更是不太可能,所以有些工作不得不搁置到比拟集中的端上。
以上两种计划其实都是通过存储来换性能,次要差别点就在于由谁来做而已。前者是客户端来做,后者是服务端来做,各有优缺点。 有没有可能对外提供纯正的Redis协定,但能够解决数据热点的问题?。鲁迅……David Wheeler已经说过,计算机科学的如何问题都能够通过减少一层来解决,热点的问题也不例外。咱们能够在利用和Redis之间加一层中间层,这层中间层能够是实在的服务,比方数据对立拜访层。也能够是特制的Redis客户端。
中间层能够对特定的Key加本地Cache,就能够保障热点不会呈现在Redis上。至于对哪些Key加本地Cache,中间层能够实时去剖析近期申请热点数据,自行决定。其实最简略的形式就是开个LRU或者LFU的Cache。
另外,像第二种减少数据正本的计划,也齐全能够由中间层去实现。当咱们发现有数据热点时,让中间层被动将热点数据复制,拦挡并改写所有对热点数据的申请,将其分散开来。 当然,如果中间层更智能的化,这些齐全都能够实现自动化,从热点的发现到解决,齐全不须要人参加。
明天的文章就到这了,大家感觉有用请点赞,喜爱请关注。对于Redis热点的问题,大家有啥认识或者教训能够在评论区留言探讨。