热key
热key问题的本质
热Key问题是指在分布式缓存或数据库系统中,某些特定的Key在短时间内接收到大量的访问请求,导致这些Key所在的节点负载过高,形成系统瓶颈,甚至引发雪崩效应。这类似于现实世界中的'热点'现象,少数资源承受了大部分的访问压力.
从本质上看,热Key问题是资源分配不均衡的表现。在理想情况下,请求应该均匀分布在所有节点上,但实际业务中,数据访问通常遵循二八定律,即20%的数据承担了80%的访问量。当这种不均衡达到极端时,就形成了热Key.
例如:秒杀活动中的商品库存Key、双十一期间的热门商品信息Key、实时排行榜数据Key等,这些Key在特定时间段内会接收到远超系统平均水平的访问请求。
热key的危害
热Key问题会导致多方面的系统风险:
- 首先,热Key所在节点可能因负载过高而响应变慢或崩溃;
- 其次,当热Key节点不可用时,大量请求可能会穿透到后端数据库,引发连锁反应;
- 最后,即使系统没有崩溃,热Key也会导致资源利用不均衡,降低整体系统效率
如何识别热key
要解决这种极热 key 的问题,首先要找出这些 Hot key 来。
识别热Key主要有三种方法:
- 一是通过Redis自带的监控命令如MONITOR或INFO进行实时监控;
- 二是使用第三方工具如Redis-faina或Redis-stat进行数据分析;
- 三是在应用层进行采样统计,记录高频访问的Key。
在项目中,一般用多层次的热key识别策略:
- 预防性识别:基于业务特性,提前预判可能的热Key。例如,新品发布、促销活动、热点新闻等场景下的相关Key。
- 实时监控:我们开发了一个专门的热Key监控服务,通过在Redis代理层采样请求,并结合滑动窗口计数算法,实时识别访问频率突增的Key。
- 日志分析:对Redis的慢查询日志和访问日志进行离线分析,识别历史上的热Key模式,用于优化未来的策略。
- 自适应采样:在高峰期增加采样率,低峰期降低采样率,平衡监控精度和系统开销
解决方案
客户端缓存与本地缓存
在应用服务器上引入本地缓存,可以有效减轻Redis集群的热Key压力。常用的本地缓存方案包括Caffeine、Guava Cache等,通过合理设置过期时间和更新策略,可以在保证数据相对新鲜的同时,大幅降低对分布式缓存的访问频率。
对于商品详情这类读多写少的数据,可以在应用服务器上部署了两级缓存:
- JVM本地缓存:使用Caffeine实现,针对超热Key设置较短的过期时间(如10秒)和较大的容量。
- 分布式缓存:使用Redis集群,作为本地缓存未命中时的第二层防护
同时,我们通过消息队列实现了缓存更新的准实时通知,当商品信息变更时,主动失效相关的本地缓存,保证数据的一致性。
热key分片
热Key分片是指将一个热Key的数据分散到多个子Key中,每个子Key负责一部分数据,从而将访问压力分散到多个节点。这种方法特别适用于可以进行数据分割的场景,如列表数据、计数器等.
比如,对于一个商品评论列表这类的热key,我们采用分片策略:
- 数据分片:将评论数据按照时间或ID范围分成多个子列表,存储在不同的Key中,如product:10001:comments:1、product:10001:comments:2等。
- 请求路由:客户端请求时,根据页码或时间范围计算出应访问的子Key,或者使用随机策略选择一个子Key。
- 结果聚合:对于需要聚合结果的场景,如获取评论总数,我们预先计算并存储在单独的Key中,或者使用Redis的脚本功能在服务端进行聚合。
这种分片策略使我们能够将单个热Key的访问压力分散到多个Redis节点,有效避免了单点瓶颈。
读写分离与副本漂移
利用Redis的主从复制机制,可以实现读写分离,将读请求分流到多个从节点,减轻主节点压力。对于特别热的Key,还可以实现'副本漂移',即为热Key创建更多的副本,提供更大的读取带宽。
项目中实现动态读写分离策略思路:
- 基础读写分离:使用Redis Sentinel或Redis Cluster,将读请求默认路由到从节点,写请求路由到主节点。
- 动态副本调整:监控系统实时检测热Key,当发现热Key时,自动为其创建额外的副本,并更新路由策略,将该Key的读请求优先路由到这些副本。
- 一致性保证:对于强一致性要求的场景,我们实现了'读-写-读'模式,即先尝试从节点读取,如果数据过期或不存在,则回源到主节点,并更新从节点数据。
这种动态调整的策略,使我们能够针对不同的Key提供不同级别的服务质量,既保证了热Key的访问性能,又避免了资源浪费。