本文已收录在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 发布!