1 数据倾斜

1.1 定义

对于集群系统,一般缓存是分布式的,即不同节点负责一定范围的缓存数据。我们把缓存数据分散度不够,导致大量的缓存数据集中到了一台或者几台服务节点上,称为数据倾斜。一般来说数据倾斜是由于负载均衡实施的效果不好引起的。

1.2 危害

如果发生了数据倾斜,那么保存了大量数据,或者是保存了热点数据的实例的处理压力就会增大,速度变慢,甚至还可能会引起这个实例的内存资源耗尽,从而崩溃。这是我们在应用切片集群时要避免的。

1.3 分类

  • 数据量倾斜(bigkey导致倾斜)
    某个实例上正好保存了 bigkey。bigkey 的 value 值很大(String 类型),或者是 bigkey 保存了大量集合元素(集合类型),会导致这个实例的数据量增加,内存资源消耗也相应增加。
  • 数据访问倾斜(读取倾斜-热key问题)
    虽然每个集群实例上的数据量相差不大,但是某个实例上的数据是热点数据,被访问得非常频繁

2 BigKey

2.1 定义

通常以Key的大小和Key中成员的数量来综合判定,例如:

  • Key本身的数据量过大:一个String类型的Key,它的值为5 MB。
  • Key中的成员数过多:一个ZSET类型的Key,它的成员数量为10,000个。
  • Key中成员的数据量过大:一个Hash类型的Key,它的成员数量虽然只有1,000个但这些成员的Value(值)总大小为100 MB。

2.2 产生原因

  • 在不适用的场景下使用Redis,易造成Key的value过大,如使用String类型的Key存放大体积二进制文件型数据;
  • 业务上线前规划设计不足,没有对Key中的成员进行合理的拆分,造成个别Key中的成员数量过多;
  • 未定期清理无效数据,造成如HASH类型Key中的成员持续不断地增加;
  • 使用LIST类型Key的业务消费侧发生代码故障,造成对应Key的成员只增不减。

bigkey的产生主要是由于程序的设计不当所造成的,如以下几种常见的业务场景

  • 社交类:粉丝列表,如果某些明星或者大v不精心设计下,必是bigkey。
  • 统计类:例如按天存储某项功能或者网站的用户集合,除非没几个人用,否则必是bigkey。
  • 缓存类:将数据从数据库load出来序列化放到redis里,这个方式经常常用,但有两个地方需要注意:第一,是不是有必要把所有字段都缓存;第二,有没有相关关联的数据。

由此可见,在程序设计中,我们要对数据量的增长和边界有一个基本性的评估,做好技术选型和技术架构。

2.3 危害

  • 1.内存空间不均匀
    在集群模式中,由于bigkey的存在,会造成主机节点的内存不均匀,这样会不利于集群对内存的统一管理,存在丢失数据的隐患。
  • 2.超时阻塞
    由于redis单线程的特性,操作bigkey通常比较耗时,也就意味着阻塞redis可能性越大,这样会造成客户端阻塞或者引起故障切换。慢查询通常就会有它们的身影。
  • 3.网络拥塞
    bigkey也就意味着每次获取要产生的网络流量较大。假设一个bigkey为1MB,客户端每秒访问量为1000,那么每秒产生1000MB的流量,对于普通的千兆网卡(按照字节算是128MB/s)的服务器来说简直是灭顶之灾。
  • 4.阻塞删除
    有个bigkey,对它设置了过期时间,当它过期后会被删除,如果使用Redis 4.0之前的版本,过期key是同步删除,就会存在阻塞redis的可能性,而且这个过期删除不会从慢查询发现(因为这个删除不是客户端产生的,是内部循环事件)。4.0之后的版本会采用异步删除。

2.4 优化方案

2.4.1 拆分Bigkey

优化Bigkey的原则就是string减少字符串长度,list、hash、set、zset等减少元素数量。当我们知道哪些key是Bigkey时,可以把单个key拆分成多个key,比如以下拆分方式可以参考。

  • big list:list1、list2、...listN
  • big hash:可以做二次的hash,例如hash%100
  • 按照日期拆分多个:key20220310、key20220311、key202203212

如果bigkey不可避免,也要思考一下要不要每次把所有元素都取出来(例如有时候仅仅需要hmget,而不是hgetall),删除也是一样,尽量使用优雅的方式来处理。

2.4.2 选择适合的数据类型。

例如:实体类型(要合理控制和使用数据结构,但也要注意节省内存和性能之间的平衡)

2.5 监控并预警

Bigkey首先需要重源头治理,防止Bigkey的产生;其次是需要能够及时的发现,发现后及时处理。分析Bigkey的方法不少,这里介绍两种比较常用的方法,也是Daas平台分析Bigkey使用的两种方式,分别是Bigkeys命令分析法、RDB文件分析法。

2.5.1 scan命令分析

Redis4.0及以上版本提供了--Bigkeys命令,可以分析出实例中每种数据结构的top 1的Bigkey,同时给出了每种数据类型的键值个数以及平均大小。执行--Bigkeys命令时候需要注意以下几点:

  • bigkeys 是以 scan 延迟计算的方式扫描所有 key,因此执行过程中不会阻塞 redis,但实例存在大量的 keys 时,命令执行的时间会很长,这种情况建议在 slave 上扫描
  • 建议在节点本机执行,这样可以减少网络开销。
  • 如果没有从节点,可以使用--i参数,例如(--i 0.1 代表100毫秒执行一次)。
  • --Bigkeys只能计算每种数据结构的top1,如果有些数据结构有比较多的Bigkey,是查找不出来的。
  • string 类型统计的是 value 的字节数;另外 4 种复杂结构的类型统计的是元素个数,不能直观的看出 value 占用字节数(元素个数少,不一定 value 不大;元素个数多,也不一定 value 就大),所以 --bigkeys 对分析 string 类型的大 key 是有用的,而复杂结构的类型还需要一些第三方工具。