Redis 数据倾斜?别慌!从成因到解决方案,一文帮你搞定

本文已收录在Github关注我,紧跟本系列专栏文章,咱们下篇再续!

  • 🚀 魔都架构师 | 全网30W技术追随者
  • 🔧 大厂分布式系统/数据中台实战专家
  • 🏆 主导交易系统百万级流量调优 & 车联网平台架构
  • 🧠 AIGC应用开发先行者 | 区块链落地实践者
  • 🌍 以技术驱动创新,我们的征途是改变世界!
  • 👉 实战干货:编程严选网

分片集群中,数据会按一定分布规则分散到不同实例保存。 如使用Redis Cluster,数据先CRC计算值对Slot(逻辑槽)取模,同时,所有Slot又会由运维管理员分配到不同实例。这样,数据就被保存到相应实例。这种方法实现简单,但易导致数据倾斜。

1 数据倾斜

  • 数据量倾斜 实例数据分布不均衡,某实例上的数据太多
  • 数据访问倾斜 虽然每个集群实例上的数据量差别不大,但某实例上的数据是热点数据,被访问得很频繁

若数据倾斜了,则保存大量数据或保存热点数据的实例的处理压力就会增大,速度变慢,甚至可能引起该实例的内存耗尽。

2 数据倾斜成因

数据倾斜时,数据在分片集群的多个实例上分布不均衡,大量数据集中到1或多个实例。

主要原因:某实例保存了bigkey、Slot分配不均衡及Hash Tag。

2.1 bigkey导致倾斜

2.1.1 某实例正好保存bigkey

大K的V值很大(String类型)或保存大量集合元素(集合类型),导致该实例的数据量增加,内存资源消耗严重。

大K操作一般都会造成实例I/O线程阻塞,若大K访问量较大,就会影响到这个实例上的其它请求的处理速度。

2.1.2 应对方法

在业务层生成数据时,尽量避免将过多数据保存在同一KV对。 若大K是集合类型,还能把大K拆分成很多个小的集合类型数据,分散保存在不同实例。

假设Hash类型集合user:info保存100万个用户信息,是个大K。 则可按用户ID范围,将该集合拆成10个小集合,每个集合只保存10万个用户信息(如小集1保存ID1~10w的用户信息,小集2保存ID10w零 ~ 20w用户)。 这就把一个大K化整为零、分散保存,避免大K给单切片实例带来访问压力。

大K访问量较大时,也会造成数据访问倾斜。

2.2 Slot分配不均衡导致倾斜

若集群运维人员没有均衡地分配Slot,就会有大量数据被分配到同一Slot,而同一Slot只在一个实例上分布,导致大量数据被集中到一个实例。

假设Redis Cluster共16384个Slot,假设集群共5个实例,而实例1硬件配置较高,运维人员在给实例分配Slot时,就可能给实例1多分配Slot。

但我们不知道数据和Slot的对应关系,可能导致大量数据正好被映射到实例1的Slot,造成数据倾斜。

2.2.1 应对

可通过运维规范,在分配前,就避免将过多Slot分配到同一实例。

已分配好Slot的集群,可先查看Slot和实例的具体分配关系,从而判断是否有过多Slot集中到同一实例:

  • 若有,就将部分Slot迁移到其它实例,避免数据倾斜。

查看Slot分配情况:

  • Redis Cluster,CLUSTER SLOTS命令
  • Codis,codis dashboard查看

如执行CLUSTER SLOTS查看Slot分配情况:

  • Slot 0 到Slot 4095被分配到了实例192.168.10.3
  • Slot 12288到Slot 16383被分配到了实例192.168.10.5
shell 复制代码
127.0.0.1:6379> cluster slots
1) 1) (integer) 0
   2) (integer) 4095
   3) 1) "192.168.10.3"
      2) (integer) 6379
2) 1) (integer) 12288
   2) (integer) 16383
   3) 1) "192.168.10.5"
      2) (integer) 6379

若某实例上有太多Slot,则可用迁移命令把这些Slot迁移到其它实例。 Redis Cluster可使用3个命令完成Slot迁移:

  • CLUSTER SETSLOT 使用不同的选项进行三种设置,分别是设置Slot要迁入的目标实例,Slot要迁出的源实例,以及Slot所属的实例。
  • CLUSTER GETKEYSINSLOT 获取某个Slot中一定数量的key。
  • MIGRATE 把一个key从源实例实际迁移到目标实例。

假设要把Slot 300从源实例(ID为3)迁移到目标实例(ID为5):

  • 先在目标实例5上执行命令,将Slot 300的源实例设置为实例3,表示要从实例3上迁入Slot 300。
bash 复制代码
CLUSTER SETSLOT 300 IMPORTING 3
  • 在源实例3上,我们把Slot 300的目标实例设置为5,这表示,Slot 300要迁出到实例5:
objectivec 复制代码
CLUSTER SETSLOT 300 MIGRATING 5
  • 从Slot 300中获取100 个key。因为Slot中的key数量可能很多,所以我们需要在客户端上多次执行下面的这条命令,分批次获得并迁移key。
objectivec 复制代码
CLUSTER GETKEYSINSLOT 300 100
  • 把刚才获取的100个key中的key1迁移到目标实例5上(IP为192.168.10.5),同时把要迁入的数据库设置为0号数据库,把迁移的超时时间设置为timeout。我们重复执行MIGRATE命令,把100个key都迁移完。
bash 复制代码
MIGRATE 192.168.10.5 6379 key1 0 timeout
  • 重复执行第3和第4步,直到Slot中的所有key都迁移完成。

Redis 3.0.6开始,也可使用KEYS选项,一次迁移多个key(key1、2、3),提升迁移效率。

bash 复制代码
MIGRATE 192.168.10.5 6379 "" 0 timeout KEYS key1 key2 key3

Codis可以执行下面的命令进行数据迁移。其中,我们把dashboard组件的连接地址设置为ADDR,并且把Slot 300迁移到编号为6的codis server group上。

css 复制代码
codis-admin --dashboard=ADDR -slot-action --create --sid=300 --gid=6

2.3 Hash Tag导致倾斜

加在KV对key中的一对花括号{},把K的一部分括起来,客户端在计算K的CRC16值时,只对Hash Tag花括号中的部分K内容进行计算。

不用Hash Tag,则客户端计算整个K的CRC16的值。

如K=user:info:3231,把3231作为Hash Tag,K就变成user:info:{3231}。客户端计算该K的CRC16,就只会计算3231的。否则会计算整个user:info:3231的CRC16值。

2.3.1 好处

若不同K的Hash Tag内容相同,则这些K所对应数据会被映射到同一Slot,同时会被分配到同一实例。使用Hash Tag后,数据被映射到相同Slot的情况:

数据key 哈希计算 对应的Slot
user:profile:{3231} CRC16('3231') mod 16384 1024
user:profile:{5328} CRC16('5328') mod 16384 3210
user:order:{3231} CRC16('3231') mod 16384 1024
user:order:{5328} CRC16('5328') mod 16384 3210

user:profile:{3231}user:order:{3231}的Hash Tag一样,CRC16计算值对16384取模后的值也一样,所以映射到相同的1024Slot。

2.3.2 应用场景

主要用在Redis Cluster,支持事务操作和范围查询。因为Redis Cluster本身不支持跨实例的事务操作和范围查询,当业务应用有这些需求时,就只能:

  • 先把这些数据读取到业务层进行事务处理
  • 或逐个查询每个实例,得到范围查询的结果

这很麻烦,所以,可使用Hash Tag把要执行事务操作或范围查询的数据映射到同一实例,就能轻松实现事务或范围查询。

2.3.3 Hash Tag潜在问题

大量数据可能被集中到一个实例上,导致数据倾斜,集群负载不均衡。

应对

需在范围查询、事务执行的需求和数据倾斜带来的访问压力之间,取舍!

2.3.4 最佳实践

若使用Hash Tag进行切片的数据会带来较大访问压力,优先考虑避免数据倾斜,最好不要使用Hash Tag进行数据切片。 因为事务和范围查询都还可以放在客户端来执行,而数据倾斜会导致实例不稳定,造成服务不可用。

3 数据访问倾斜的成因

根因:实例上存在热点数据(如热点新闻内容、热销商品信息)。

一旦热点数据被存在某实例,则该实例的请求访问量就会远高于其它实例,访问压力很大:

3.1 应对

不同于数据量倾斜,热点数据通常是一或多个数据,所以,直接重新分配Slot也无法解决。

一般热点数据主要服务读操作,可使用热点数据多副本:把热点数据复制多份,在每个数据副本的key中增加一个随机前缀,使其和其它副本数据不会被映射到同一Slot。 如此,热点数据既有多个副本可同时服务请求,这些副本数据的key又不同,会被映射到不同Slot。 在给这些Slot分配实例时,注意把它们分到不同实例,则热点数据的访问压力就被分散到不同实例。

热点数据的多副本方法只针对只读热点数据。

若热点数据有读有写,就不适合了,因为要保证多副本间数据一致性。要给实例本身增加资源,如使用配置更高机器,应对大量访问压力。

总结

数据倾斜的两种情况:

  • 数据量倾斜
  • 数据访问倾斜

导致数据量倾斜的主要原因:

  • 数据中有bigkey,导致某个实例的数据量增加
  • Slot手工分配不均,导致某个或某些实例上有大量数据
  • 使用了Hash Tag,导致数据集中到某些实例上。

数据访问倾斜主要原因:有热点数据存在,导致大量访问请求集中到了热点数据所在的实例上。

应对数据倾斜问题:

倾斜类型 倾斜成因 应对方法
数据量倾斜 存在bigkey 业务层避免创建bigkey 把集合类型的bigkey拆分成多个小集合,分散保存
同上 Slot手工分配不均 制定运维规范,避免把过多Slot分配到一个实例上
同上 使用Hash Tag,导致大量数据集中到一个Slot 如果Hash Tag会造成数据倾斜,优先避免数据倾斜,不使用Hash Tag
数据访问倾斜 存在热点数据 采用带有不同key前缀的多副本方法

Q:有数据访问倾斜时,若热点数据突然过期,而 Redis 中数据是缓存,数据的最终值保存在后端DB,会发生啥问题?

A:缓存击穿。Redis 很多性能问题,如导致 Redis 阻塞的场景:bigkey、集中过期、大实例 RDB等都与数据倾斜类似,都因数据集中、处理逻辑集中导致的耗时变长。解决思路类似,把集中变分散,如 bigkey 拆分为小 key、单个大实例拆分为分片集群等。

软件架构演进过程,从单机到分布式,再到MQ、负载均衡,都为将请求压力分散开,避免数据集中、请求集中,既能让系统承载更大的请求量,还保证系统稳定性。

本文由博客一文多发平台 OpenWrite 发布!

相关推荐
烛阴5 小时前
简单入门Python装饰器
前端·python
好开心啊没烦恼5 小时前
Python 数据分析:numpy,说人话,说说数组维度。听故事学知识点怎么这么容易?
开发语言·人工智能·python·数据挖掘·数据分析·numpy
面朝大海,春不暖,花不开5 小时前
使用 Python 实现 ETL 流程:从文本文件提取到数据处理的全面指南
python·etl·原型模式
2301_805054567 小时前
Python训练营打卡Day59(2025.7.3)
开发语言·python
万千思绪7 小时前
【PyCharm 2025.1.2配置debug】
ide·python·pycharm
微风粼粼8 小时前
程序员在线接单
java·jvm·后端·python·eclipse·tomcat·dubbo
云天徽上9 小时前
【PaddleOCR】OCR表格识别数据集介绍,包含PubTabNet、好未来表格识别、WTW中文场景表格等数据,持续更新中......
python·ocr·文字识别·表格识别·paddleocr·pp-ocrv5
你怎么知道我是队长9 小时前
python-input内置函数
开发语言·python
叹一曲当时只道是寻常9 小时前
Python实现优雅的目录结构打印工具
python
hbwhmama10 小时前
python高级变量XIII
python