什么是热Key
在某段时间内某个key收到的访问次数,显著高于其他key时,我们可以将其称之为热key。
例如,某redis的每秒访问总量为10000,而其中某个key的每秒访问量达到了7000,这种情况下,我们称该key为热key。
热key带来的问题
1.热Key占用大量的Redis CPU时间使其性能变差并影响其它请求;
2.Redis Cluster中各node流量不均衡造成Redis Cluster的分布式优势无法被Client利用,一个分片负载很高而其它分片十分空闲从而产生读/写热点问题;
3.热Key的请求压力数量超出Redis的承受能力造成缓存击穿,此时大量强求将直接指向后端存储将其打挂并影响到其它业务;
热key出现的场景
预期外的访问量抖增,如突然出现的爆款商品,访问量暴涨的热点新闻,直播间某大主播搞活动大量的刷屏点赞。
热点数据的发现
redis-cli的--hotkeys命令
redis自4.0起提供了hotkeys参数以支持用户进行实例级别热key分析。该参数能够访问所有key的返回次数。另外,使用该命令,需要将redis-server的maxmemory-policy参数设置为LFU。
问题:需要扫描整个keyspace,耗时可能会非常大,同时实时性差。
Redis内存淘汰策略?
noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
allkeys-lru:在主键空间中,优先移除最近未使用的key。volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。
allkeys-random:在主键空间中,随机移除某个key。
volatile-random:在设置了过期时间的键空间中,随机移除某个key。
volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。
volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键。
allkeys-lfu:从所有键中驱逐使用频率最少的键。
LRU和LFU的区别?
LRU -> Recently Used,根据最近一次访问的时间比较。
LFU -> Frequently Used,根据key的访问频率比较。
LRU的缺点,它无法正确的表示一个Key的热度,如果一个key从未被访问过,仅仅发生内存淘汰的前一会儿被用户访问了一下,在LRU算法中这会被认为是一个热key。
在客户端进行收集
执行redis的每一次访问都来自业务层,因此我们可以通过在业务层增加相应的代码对redis的访问进行记录并异步汇总分析。比如我们可以对客户端工具进行封装,在发送请求前进行收集,同时定时把收集到的数据上报到统一的服务进行聚合计算。
问题:增加的业务代码的复杂度。
在代理层进行收集
设置了代理层的架构,因为所有的redis请求都会经过代理层,可以在代理层进行热key的收集和分析。在代理层进行,不会入侵业务层代码,对使用方透明。
问题:并不会所有的redis集群架构中都有代理层。
Redis节点抓包分析
在redis节点使用一些抓包工具,抓取一段时间内的流量进行解析。这种方式对SDK或proxy代理层没有入侵。
问题:热节点的网络流量和系统负载已经过高了,抓包可能会导致情况进一步恶化。
热key的常见处理方案
Redis 热Key问题首先是请求流量过大造成的,但是更深层次原因还是出现了流量倾斜,单个Redis实例承担的流量过大造成的。为了解决流量倾斜的问题,我们就是要想办法将单个实例承担的流量打散,让每个机器均衡承担热Key的流量,不要出现流量倾斜,保证系统的稳定性。
一些方案:
1.在Redis Cluster结构中对热key进行复制。
此时可以将对应热Key进行复制并迁移至其他node,例如为热Key foo复制出3个内容完全一样的Key并名为foo2,foo3,foo4,然后将这三个Key迁移到其他node来解决单一node的热Key压力。
问题:代码需要联动修改,同时存在多key的数据一致性问题。
2.如果热key的产生来自于读请求,可以使用读写分离的架构。
使用读写分离的架构,可以通过增加从节点来降低读请求的压力。
问题:
a)读写分离架构增加了业务代码层面的复杂度,也增加了架构层的复杂度。我们需要为多个从节点提供转发层(proxy, LVS)来实现负载均衡。
b)在读请求大的场景下,读写分离架构会产生延迟,对数据一致性要求高的场景,并不合适。
3.热点key使用本地缓存替代。
参考:https://dongzl.github.io/2021/01/14/03-Redis-Hot-Key/index.html https://juejin.cn/post/7001030996384546847#heading-4
点个“赞 or 在看” 你最好看!
👇👇👇谢谢各位老板啦!!!