【Redis经典面试题十】热key与大key的问题如何解决?
目录
一、什么是热Key问题?如何解决热Key问题?
1.1 热key的标准
1.2 如何识别热key?
根据经验,提前预测
实时收集
1.3 如何解决?
多级缓存
热key备份
热key拆分
二、什么是大Key问题?如何解决?
2.1 多大算大?
2.2 识别big key
2.3 处理Big Key
一、什么是热Key问题?如何解决热Key问题?
当我们使用Redis作为存储时,如果发生一些特殊情况,比如明星官宣的突发事件、世界杯等重大活动、双十一的活动秒杀等等,就会出现特别大的流量,并且会导致某些热词、商品等被频繁的查询和访问。
如果在同一个时间点上,Redis中的同一个key被大量访问,就会导致流量过于集中,使得很多物理资源无法支撑,如网络带宽、物理存储空间、数据库连接等。
这也是为什么某某明星官宣之后,微博上面就会出现宕机的情况。有时候这种宕机发生后,其他功能都是可以使用的,只是和这个热点有关的内容会无法访问,这其实就和热点数据有关系了。
对于热key的处理,主要在于事前预测和事中解决。
对于事前预测就是根据一些根据经验,提前的识别出可能成为热key的Key,比如大促秒杀活动等。
在事中解决方面,主要可以考虑,热点key拆分、多级缓存、热key备份、限流等方案来解决。
1.1 热key的标准
到底“多热算热”,这个其实需要根据实际的业务情况以及你自己的缓存服务器的整体存储情况而定的。
JD有一个框架叫做hotkey,他就是专门做热key检测的,他的热key定义是在单位时间内访问超过设定的阈值频次就是热key,这个阈值需要业务自己设定,并不断的调整和优化。
热key的定义,通常以其接收到的Key被请求频率来判定,例如:
- QPS集中在特定的Key:Redis实例的总QPS为10,000,而其中一个Key的每秒访问量达到了7,000。那么这个key就算热key了。
- 带宽使用率集中在特定的Key:对一个拥有1000个成员且总大小为1 MB的HASH Key每秒发送大量的HGETALL操作请求。
- CPU使用时间占比集中在特定的Key:对一个拥有10000个成员的Key(ZSET类型)每秒发送大量的ZRANGE操作请求。
1.2 如何识别热key?
根据经验,提前预测
这种方法在大多数情况下还是比较奏效的。比较常见的就是电商系统中,会在做秒杀、抢购等业务开始前就能预测出热key。
但是,这种方式的局限性也很大,就是有些热key是完全没办法预测的,比如明星什么时候要官宣这种事情就无法预测。
实时收集
还有一种热点数据的发现机制,那就是实时的做收集,比如在客户端、服务端或者在代理层,都可以对实时数据进行采集,然后进行统计汇总。
达到一定的数量之后,就会被识别为热key。
具体的收集方式也有很多种,可以在客户端进行收集、也可以在统一代理层进行收集、还可以通过redis的自带命令进行收集。redis 4.0.3中提供了redis-cli的热点key发现功能,执行redis-cli时加上--hotkeys选项即可。
1.3 如何解决?
多级缓存
解决热key问题最主要的方式就是加缓存。通过缓存的方式尽量减少系统交互,使得用户请求可以提前返回。
这样即能提升用户体验,也能减少系统压力。
缓存的方式有很多,有些数据可以缓存在客户的客户端浏览器中,有些数据可以缓存在距离用户就近的CDN中,有些数据可以通过Redis等这类缓存框架进行缓存,还有些数据可以通过服务器本地缓存进行。
这种使用多个缓存的情况,就组成了二级缓存、三级缓存等多级缓存了。总之,通过缓存的方式尽量减少用户的访问链路的长度。
热key备份
有了缓存之后,还会带来一个问题,那就是热点数据如果都被缓存在同一个缓存服务器上,那么这个服务器也可能被打挂。
所以,很多人在加了缓存之后,还可能同时部署多个缓存服务器,如Redis同时部署多个服务器集群。并且实时的将热点数据同步分发到多个缓存服务器集群中,一旦有的集群扛不住了,立刻做切换。
热key拆分
将一个热key拆分成多个key,在每一个Key后面加一个后缀名,然后把这些key分散到多个实例中。
这样在客户端请求的时候,可以根据一定的规则计算得出一个固定的Key,这样多次请求就会被分散到不同的节点上了。
比如 <淄博烧烤>
是个热点key,
把他拆分成淄博烧烤_0001、淄博烧烤_0002、淄博烧烤_0003、淄博烧烤_0004,然后把它们分别存储在cluster中的不同节点上,这样用户在查询 <淄博烧烤>
的时候,先根据用户ID算出一个下标,然后就访问其中一个节点就行了。
有人问了,这不是意味着一个用户只能拿到部分数据了吗?确实是,但是有时候我们并不一定就需要全部的数据。
比如说,同样的两个用户在刷抖音,都想看 <淄博烧烤>
这个热点相关的视频,但是我们并不一定要给所有用户都推送同样的内容,完全可以把这个词条下面的无数个视频分散存储在不同的节点上,然后给不同的用户推送在不同的节点上的数据就行了。
然后在这个热点key没那么热了之后,再把数据做一下汇总,挑选出一些好的视频在重新推送给没推送到的用户就行了。
二、什么是大Key问题?如何解决?
Big Key是Redis中存储了大量数据的Key,不要误以为big key只是表示Key的值很大,他还包括这个Key对应的value占用空间很多的情况。通常在String、list、hash、set、zset等类型中出现的问题比较多。其中String类型就是字符串的值比较大,而其他几个类型就是其中元素过多的情况。
Redis的Big Key可能存在以下几个危害:
- 影响性能:由于big key的values占用的内存会很大,所以读取它们的速度会很慢,会影响系统的性能。
- 占用内存:大量的big key也会占满Redis的内存,让Redis无法继续存储新的数据,而且也会导致Redis卡住。
- 内存空间不均匀:比如在Redis集群中,可能会因为某个节点上存储了Big Key,导致多个节点之间内存使用不均匀。
- 影响Redis备份和恢复:如果从RDB文件中恢复全量数据时,可能需要大量的时间,甚至无法正常恢复。
- 搜索困难:由于大key可能非常大,因此搜索key内容时非常困难,并且可能需要花费较长的时间完成搜索任务。
- 迁移困难:大对象的迁移和复制压力较大,极易破坏缓存的一致性。
- 过期执行耗时:如果Bigkey设置了过期时间,当过期后,这个key会被删除,而大key的删除过程也比较耗时。
对于Big Key问题的处理,重点要在识别和解决上面。
2.1 多大算大?
Redis中多大的key算作大key并没有一个固定的标准,因为这主要取决于具体的场景和应用需求。一般来说,如果一个key的value比较大,占用的内存比较多,或者某个key包含的元素数量比较多,这些都可以被认为是大key。
通常情况下,建议不要超过以下设定,超过这些数量就可能会影响Redis的性能:
- 对于String类型的Value值,值超过5MB(腾讯云定义是10M,阿里云定义是5M,我认为5M合适一点)。
- 对于Set类型的Value值,含有的成员数量为10000个(成员数量多)。
- 对于List类型的Value值,含有的成员数量为10000个(成员数量多)。
- 对于Hash格式的Value值,含有的成员数量1000个,但所有成员变量的总Value值大小为100MB(成员总的体积过大)。
但是,这些并不是绝对的限制,而是一个经验值,具体的情况还需要根据应用场景和实际情况进行调整。
2.2 识别big key
在识别方面,Redis中的big key可以识别的程序是“redis-cli”。用户可以通过在终端中输入“redis-cli --bigkeys”来获取Redis中的big key。当redis-cli被调用时,它将搜索所有Redis数据库中包含大量内存数据的key,并且会将其保存在本地标准输出文件中。
2.3 处理Big Key
想要解决Big Key的问题,根据具体的业务情况有很多不同的方案,下面简单列几个:
-
有选择地删除Big Key:针对Big Key,我们可以针对一些访问频率低的进行有选择性的删除,删除Big Key来优化内存占用。
-
除了手动删除以外,还可以通过合理的设置缓存TTL,避免过期缓存不及时删除而增大key大小。
-
Big Key的主要问题就是Big,所以我们可以想办法解决big的问题,那就是拆分呗,把big的key拆分开:
- a、在业务代码中,将一个big key有意的进行拆分,比如根据日期或者用户尾号之类的进行拆分。使用小键替代大键可以有效减小存储空间,从而避免影响系统性能。
- b、使用Cluster集群模式,以将大key分散到不同服务器上,以加快响应速度。
-
部分迁移:将大键存放在单独的数据库中,从而实现对大键的部分迁移。