如何解决 Redis 中的热点 key 问题?

Sherwin.Wei Lv7

如何解决 Redis 中的热点 key 问题?

回答重点

Redis 中的热点 Key 问题是指某些 Key 被频繁访问,导致 Redis 的压力过大,进而影响整体性能甚至导致集群节点故障。

解决热点 Key 问题的主要方法包括:

  • 热点 key 拆分:将热点数据分散到多个 Key 中,例如通过引入随机前缀,使不同用户请求分散到多个 Key,多个 key 分布在多实例中,避免集中访问单一 Key。
  • 多级缓存:在 Redis 前增加其他缓存层(如 CDN、本地缓存),以分担 Redis 的访问压力。
  • 读写分离:通过 Redis 主从复制,将读请求分发到多个从节点,从而减轻单节点压力。
  • 限流和降级:在热点 Key 访问过高时,应用限流策略,减少对 Redis 的请求,或者在必要时返回降级的数据或空值。

扩展知识

热点 key 的定义

热点 Key 与 big key 一样,没有一个很明确的定义来约定什么样的 key 叫做热点 key。

我们可以参考阿里云 Redis 对热 key 的定义:

image.png

可以看到,如果一个 key 的访问频率占比过大,或带宽占比过大,都属于热点 key。

由于 Redis 的读写是单线程执行的,所以热点 key 可能会影响 Redis 的整体效率,消耗大量的 CPU 资源,从而降低 Redis 的整体吞吐量。集群环境下会使得流量不均衡,从而导致读写热点倾斜问题的发生。

如何发现热 key?

1)根据业务经验进行分析

这个主要就是依据业务场景进行分析,通过经验判断哪些 key 可能成为热门 key,比如某明星的花边新闻、秒杀活动,演唱会门票等。

  • 优点:这个方案实现起来简单直接,只有直接进行判断就可以了,基本没有什么成本。
  • 缺点:这个主要依据业务能力,对于业务能力有一定的要求,并且不是所有的业务都能判断出来是否是热 key 的。且有些突发事情是无法预测的。

2)redis 集群监控

这种方式主要依据与 Redis 集群,我们只需要查看集群中哪个 Redis 出现 QPS 倾斜,而出现 QPS 倾斜的实例有极大的概率存在热点 Key。

  • 优点:这种方案和上面差不多,由于企业的 Redis 大多数是集群部署,所以使用起来非常简单。
  • 缺点:每次发生状况都需要排查,因为不一定所有的 QPS 倾斜都是热 Key 导致的。

3)使用 hotkey 监控

这个是 Redis 4.0 版本之后引入的一个新的指令,只需要在命令行执行 redis-cli 的时候加上 –hotkeys 的选项就可以了。它是通过 scan + object freq 实现的。

  • 优点:因为这个命令是 Redis 自带的,使用起来简单快捷
  • 缺点:需要扫描整个 keyspace,如果 Redis 中的 key 数量比较多的话,可能导致执行时间非常长,且实时性不好。

4)使用 monitor 命令

如下图所示,这个是 Redis 自 1.0 起就支持的功能。

image.png

当通过 MONITOR 命令开启监视器之后,Redis 只需要在执行之后结合一些日志和相关的分析工具就可以进行统计。

  • 优点:这个方案的优点在于这个是 Redis 原生支持的功能,使用起来简单快捷。
  • 缺点:monitor 非常消耗性能,单个客户端执行 monitor 就会损耗 50% 的性能!不推荐这个方式!

以下为 redis 官网的 benchmark:

image.png

5)客户端收集

在操作 Redis 之前,通过加上统计 Redis 键值查询频次的逻辑,将统计数据发送给一个聚合计算平台进行计算,计算之后查看相对应的结果

  • 优点:对性能损耗较低。
  • 缺点:成本较大,企业如果没有聚合计算平台还需要引入。

6)代理层收集

在代理层进行统一的收集,因为有些服务在请求 Redis 之前都会请求一个代理服务,这种场景可以使用在代理层收集 Redis 热 Key 数据,和在客户端收集比较类似。

  • 优点:客户端使用方便,不需要考虑 SDK 多语言异构差异和升级成本高的问题。
  • 缺点:需要给 Redis 定制一个代理层,进行转发等操作,构建代理成本不低,且转发有性能损耗。

应用程序中的多级缓存

在应用程序中,一般结合使用一级缓存和二级缓存:

  • 一级缓存:一般指的是应用程序本地缓存(如 JVM 内存中的缓存)。
  • 二级缓存:则为 Redis 缓存。当数据不在一级缓存中时,才会请求二级缓存。

通过这种多级缓存架构,可以有效减少 Redis 的访问次数,从而避免单 Key 的热点问题。

热点 key 的拆分

我们可以按照不同场景做不同的“拆分“。有些场景可以全量拷贝,即将 mianshiya 这个 key 复制成 mianshiya_1、mianshiya_2、mianshiya_3 ,它们之间的数据是一致的,这样不同用户都访问到全量的数据。

有些场景直接进行 key 的拆分,mianshiya_1、mianshiya_2、mianshiya_3 各存一部分的数据,不同用户仅需访问不同数据即可,例如一些推流信息,因为一个热点往往有很多发布者,大家看一部分,后续热度稍微降低下来,可以替换数据。

不同用户可以进行 hash,将用户 id 哈希之后取余得到后缀,拼上 mianshiya_ 即可组成一个 key。

Comments