面试官:说说如何解决Redis中的大热Key问题

本文首发于公众号:托尼学长,立个写 1024 篇原创技术面试文章的flag,欢迎过来视察监督~

在技术面试中,Redis方向还是有挺多高频问题的,比如:击穿、穿透、雪崩、分布式锁、持久化方式、单线程改多线程等等。

除此之外,还有一个相对高频的面试题,那就是"Redis中如何解决大热Key问题"。而且,这也是一个比较务实的好问题。

毕竟相对而言,没有20年的脑残功力,谁也不会让Redis中的Key在同一时刻全部失效,引发缓存雪崩问题,也很少有人读取数据库后不将Key重新加载进Redis,任由它来个缓存击穿。

接下来言归正传,讲讲什么是Redis中的大热Key,以及会造成哪些影响。

Redis中的大热Key问题

先说说什么是大Key,这个不能顾名思义,其实是该Key所对应的value值比较大(如:大于1MB),存储它会占用比较大的内存空间,在进行读取的时候也会占用很大的网络带宽。

一般来讲,String类型的大Key相对少一些,集合类型(List、Set、ZSet、Hash)由于其包含的元素较多,则比较容易产生大Key。

其实大Key本身所带来的影响并不大,用户每秒钟请求它一两次的不会产生任何问题,最怕的就是大Key + 热Key双鬼拍门。

我们以常见的千兆网卡来计算,其最大传输速度为每秒钟128MB,那就意味着如果Redis的大Key为1MB、每秒钟有100多个请求打到该台服务器上,就能将这台Redis服务器的网卡打满,从而影响系统可用性。

如下图所示,一旦Redis Cluster中存储大热Key节点的网卡被打满,就会导致集群中的资源消耗倾斜,不仅会影响大热Key的请求被影响,就连该节点下的其他请求也会被影响。

如何解决大热Key问题

嗯,既然问题出现了,我们还是希望通过有效的方式去解决它。下面就来盘点一下几种常见的解决方案。

1、雨露均沾法

这种方案应该是代码改动量最小、见效最快的方式了,甚至都可以用来做Hotfix。如下图所示:

嗯,这样是不是就雨露均沾了?

有的同学可能会说,我的系统中只有一个大热Key,怎么才能让它均摊到各个服务器中呢?

其实很简单,我们想想海量数据下的分库分表是如何做的呢?举个例子,原本是一张order表,分库分表后变成了order00、order01、......、order99。

如下图所示:

Redis Cluster也是一样的,如果是三主三从的话,我们将其后缀取数范围设置为0 --- 14,这样可以将它的存储和请求流量相对均匀地分散开,单服务器所承载的请求量降低为原来的1/3。

具体落地步骤如下:

(1)当该大热Key出现set、del操作时,需要将该大热key所对应的0 --- 14后缀全部进行添加、更新和删除操作,以保证其数据的一致性。

(2)当该大热Key出现get操作时,需要将该大热key所对应的0 --- 14后缀中随机挑选一个进行读取,以保证其热点分散化。

生成随机数的代码很简单,使用Java中的Random工具类即可。

java 复制代码
import java.util.Random; public class RandomNumberGenerator {    
    public static void main(String[] args) {        
        Random random = new Random();         
        // 生成一个0到14之间的随机数        
        int randomNumber = random.nextInt(15);         
        System.out.println(randomNumber);    
    }
}

"雨露均沾法"适用于该大热Key的改动较少,且请求访问量级可控,不会让Redis Cluster网卡成为瓶颈的场景。

2、黄雀在后法

如果用了"雨露均沾"法仍然不能解决问题,此时我们可以考虑用该种方法。

因为绝大多数情况下,一个系统应用服务器的数量一定是多于Redis Cluster主节点的数量的这种情况下,我们就可以通过Local Cache来在前面顶一波大热Key了,只有在Local Cache失效的情况下,才会访问Redis Cluster。

如下图所示:

在系统QPS比较高的情况下,哪怕给Local Cache设置为一秒钟过期,其命中率依然会很高,应该可以替Redis Cluster挡住99%+的请求。

至于在有了Local Cache的情况下,是否还需要使用Redis Cluster进行二次防护,这是个仁者见仁智者见智的问题。

不用的话,可以减少系统的复杂度和多存储下数据一致性的问题,而用的话,则可以保护数据库这个最为系统链路中最为稀缺的资源。

"黄雀在后法"适用于对该大热Key的改动较少,且对数据实时性要求不是很高的业务场景。

3、先行过滤法

实话实说,我认为99%的大热Key问题,都可以通过重新梳理业务逻辑的方式解决的。

其原因在于,无论是我们的PC端还是手机端,当前能给用户看到的屏幕内容是非常有限的,一定是对大热Key进行二次数据过滤后,才返回给客户端的。

如下图所示:

基于此种情况,我们可以将数据筛选过滤这个动作前置化,下沉到往Redis Cluster中生成数据的时候进行,或是从Redis Cluster中获取数据的时候进行,这样就不会再出现网卡被打满,从而影响系统可用性的情况。

举个例子,很多同学都喜欢通过Redis中的ZSet数据类型去构建排行榜,但在某些业务场景下排行榜中的元素会比较多,比如:电商平台上的热门商品排行榜。

此时,如果循规蹈矩、一个不落地把平台上的所有商品全部吃进ZSet中,那肯定妥妥的是一个大热Key。

但换个角度思考,该排行榜在服务器端一定会进行数据过滤的,最终展示给用户会一定是下图中的样子。

在此情况下,我们其实只用ZSet存储销量Top 10的热门商品信息即可,在榜单上的商品销量增加的时候,直接进行ZINCRBY操作添加对应分数。

但为了防止榜单排名发生变化,假设排在第11名的商品的销量超过排名第10的商品上榜,我们需要通过后台的定时任务来定期重构商品榜单。

这种方案就是先行过滤数据来减少网络资源消耗的方案,并在一定程度上牺牲了数据时延性。

"先行过滤法"可以从根本上解决大热Key的问题,但会改动到核心的代码逻辑,且需要case by case地进行问题分析,并不存在一个通用的解决方案。

结语

我想文章讲到这里,无论是应对面试官的刁难还是解决项目中的实际问题,都应该已经足够了。

相关推荐
容若只如初见1 小时前
项目实战--Spring Boot + Minio文件切片上传下载
java·spring boot·后端
weixin_440401692 小时前
分布式锁——基于Redis分布式锁
java·数据库·spring boot·redis·分布式
码农爱java2 小时前
Spring Boot 中的监视器是什么?有什么作用?
java·spring boot·后端·面试·monitor·监视器
Apifox.3 小时前
什么是 HTTP POST 请求?初学者指南与示范
后端·http·学习方法·web
无名指的等待7123 小时前
SpringBoot实现图片添加水印(完整)
java·spring boot·后端
skyshandianxia4 小时前
Java面试八股之如何提高MySQL的insert性能
java·mysql·面试
三两肉5 小时前
如何使用缓存提升SpringBoot性能(EhCache和Redis方式)
spring boot·redis·缓存
甜甜圈的小饼干6 小时前
Spring Boot+Vue项目从零入手
vue.js·spring boot·后端
陪我养猪吧7 小时前
Linux 服务器环境搭建
linux·服务器·redis·mysql·nginx·jdk·maven
剑圣土豆7 小时前
大模型LLM面试常见算法题-包括Attention和Transformer常见面试题
人工智能·深度学习·算法·自然语言处理·面试·nlp·transformer