当前位置:首页 > 问答 > 正文

Redis热点Key那些事儿,怎么破才不让它卡住系统性能

说到Redis热点Key问题,这确实是很多系统在运行过程中会突然遇到的“头疼事”,你可能遇到过这种情况:平时运行得好好的系统,突然在某个时间点响应变得极慢,监控报警响个不停,一查发现Redis的某个实例CPU使用率飙升到100%,而其他实例却非常空闲,这十有八九就是热点Key在“作祟”。

到底什么是热点Key呢?说白了,就是某一个或某几个Key在短时间内被非常频繁地访问,这个访问量远远超过了其他普通的Key,可以想象一下,Redis处理请求就像一个银行的柜台,大部分时间每个柜台都处理着不同的业务,有条不紊,突然,来了一个超级VIP客户(热点Key),所有人都必须排着队只为服务他一个人,其他柜台却闲着没事干,结果就是,等待这个VIP客户的队伍排得老长,整个银行的业务处理都被拖慢了,这个VIP客户就是热点Key,而那个被排长队的柜台就是承载这个Key的Redis实例。

Redis热点Key那些事儿,怎么破才不让它卡住系统性能

为什么热点Key会卡住系统性能呢?这要从Redis的两个特点说起,第一,Redis是单线程处理命令的,这意味着无论你的服务器CPU有多少个核心,一个Redis实例在同一时刻只能处理一个命令,第二,数据是分片存储的,一个Key通过哈希计算会落到集群中的某一个实例上,当某个Key成为热点,所有对这个Key的请求(比如每秒几万甚至几十万次读取)都会涌向同一个Redis实例,这个实例的单线程就会忙得不可开交,CPU直接打满,导致其他原本应该由这个实例处理的请求也被阻塞住,排队等待,响应时间急剧上升,最终拖垮整个依赖Redis的应用程序。

知道了问题的严重性,那怎么才能发现和解决它呢?

Redis热点Key那些事儿,怎么破才不让它卡住系统性能

如何发现热点Key? 你不能等到系统卡死了才后知后觉,可以参考《Redis开发与运维》这本书里提到的一些方法,一个是靠监控报警,通过Redis自带的INFO命令或者监控工具,持续观察每个实例的QPS(每秒查询次数),如果发现某个实例的QPS异常地高于其他实例,那很可能就是有热点Key落在了它上面,另一个更直接的方法是使用Redis 4.0之后版本提供的redis-cli --hotkeys命令,它可以帮你直接找出可能的热点Key,对于一些云服务商提供的Redis,它们的管理控制台通常也会内置热点Key的发现功能。

接下来是关键,怎么破解这个难题? 这里提供几个常见的思路,参考了像“阿里云开发者社区”这类技术社区中常见的解决方案。

Redis热点Key那些事儿,怎么破才不让它卡住系统性能

  1. 本地缓存是首选利器:这是最常用也是最有效的方法,既然这个Key被读的次数远远大于写的次数(比如商品信息、配置信息等),我们可以在应用程序层面加一个本地缓存(比如Google Guava Cache或Caffeine),当第一次从Redis读到数据后,就把数据缓存在应用服务器的本地内存中,并设置一个较短的过期时间(比如几秒钟),这样,后续的绝大部分读请求就不用再去访问Redis了,直接本地返回,压力瞬间解除,这要注意本地缓存与Redis数据的一致性,一般对少量延迟不敏感的数据非常适合。

  2. 打散热点Key:如果这个热点Key必须频繁写,不适合做本地缓存(比如某个热门帖子的实时点赞数),那么可以考虑把这一个Key拆分成多个Key,原来的Key叫 post:123:likes,现在我们可以把它拆成 post:123:likes:1post:123:likes:2 ... post:123:likes:10 一共10个Key,在应用程序读写时,通过一个简单的规则(比如对用户ID取模)来决定操作哪一个子Key,最后要获取总点赞数时,只需要把这10个Key的值加起来就行了,这样就把对单个Key的巨额访问压力,均匀地分摊到了10个Key上,而这10个Key有很大概率会分布在不同的Redis实例上。

  3. 使用Redis集群的读写分离:如果使用的是Redis集群模式,可以考虑为热点数据所在的实例配置从节点(副本),并让一部分读请求可以去从节点读取,这样就把读压力分担了一部分出去,不过要注意,从节点的数据会有微小的延迟,适用于对数据一致性要求不是极度严格的场景。

  4. 业务逻辑上做文章:热点Key的产生和业务逻辑有关,比如在秒杀场景下,所有人都在抢同一个商品库存,这种情况下,除了上述技术手段,还可以在业务上做优化,淘宝技术这十年》等资料里就提到过,可以把库存提前分段扣减,或者在系统内存中完成大部分抢购请求的排队和过滤,最后只有成功的请求才去访问数据库或Redis执行真正的扣减,极大减少对热点Key的并发冲击。

解决Redis热点Key问题,核心思路就八个字:“发现它,然后分散它”,关键在于要有完善的监控体系能及时发现问题,然后根据业务场景选择最合适的方案,要么把访问量“挡”在Redis之外(本地缓存),要么把压力“分”到多个节点上(Key打散),只要处理得当,热点Key这个性能杀手是完全可以被制服的。