在切片集群中,数据会按照一定的分布规则分散到不同的实例上保存。比如,在使用Redis Cluster数据都会先按照 CRC 算法的计算值对 Slot(逻辑槽)取模,同时,所有的 Slot 又会由运维管理员分配到不同的实例上。这样,数据就被保存到相应的实例上了。
虽然这种方法实现起来比较简单,但是很容易导致一个问题:数据倾斜。
数据倾斜有两类:
- 数据量倾斜:某个节点的数据量明显大于其他节点
- 数据访问倾斜:虽然每个集群实例上的数据量相差不大,但是某个实例上的数据是热点 数据,被访问得非常频繁。
那么,数据量倾斜是怎么产生的呢?这主要有三个原因,分别是某个实例上保存了 bigkey、Slot 分配不均衡以及 Hash Tag。
bigKey导致的数据倾斜
第一个原因是,某个实例上正好保存了 bigkey。bigkey 的 value 值很大(String 类 型),或者是 bigkey 保存了大量集合元素(集合类型),会导致这个实例的数据量增加, 内存资源消耗也相应增加。
而且,bigkey 的操作一般都会造成实例 IO 线程阻塞,如果 bigkey 的访问量比较大,就 会影响到这个实例上的其它请求被处理的速度。
为了避免 bigkey 造成的数据倾斜,一个根本的应对方法是,我们在业务层生成数据时,要尽量避免把过多的数据保存在同一个键值对中。此外,如果 bigkey 正好是集合类型,我们还有一个方法,就是把 bigkey 拆分成很多个小的集合类型数据,分散保存在不同的实例上。
Slot分片不均衡
如果集群运维人员没有均衡地分配 Slot,就会有大量的数据被分配到同一个 Slot 中,而同一个 Slot 只会在一个实例上分布,这就会导致,大量数据被集中到一个实例上,造成数据倾斜。
查看 Slot 分配情况的方式不同:如果是 Redis Cluster,就用 CLUSTER SLOTS 命令。 也可以使用 KEYS 选项,一次迁移多个 key(key1、2、3),这样可以提升迁移效率。
hash Tag
Hash Tag 是指加在键值对 key 中的一对花括号{}。这对括号会把 key 的一部分括起来,客户端在计算 key 的 CRC16 值时,只对 Hash Tag 花括号中的 key 内容进行计算。如果没用 Hash Tag 的话,客户端计算整个 key 的 CRC16 的值。
使用 Hash Tag 的好处是,如果不同 key 的 Hash Tag 内容都是一样的,那么,这些 key 对应的数据会被映射到同一个 Slot 中,同时会被分配到同一个实例上。
和数据量倾斜不同,热点数据通常是一个或几个数据,所以,直接重新分配 Slot 并不能解决热点数据的问题。
常见的方案:
- 将热点 Key 的关联数据拆分到多个 Key;
- 把热点数据复制多份,在每一个数据副本的 key 中增加一个随机前缀,让它和其它副本数据不会被映射到同一个 Slot 中。这样一来,热点数据既有多个副本可以同时服务请求,同时,这些副本数据的 key 又不一样,会被映射到不同的 Slot 中。(生成一个随机的hashtag),使得原本相同的KEY由于增加了随机的前缀,计算的Hashtag不同,从而落在不同的实例上。
比如:某个热点的key=user:profile 使用随机前缀的hashtag,增加多个副本,{UUID}:user:profile。
高并发的场景处理方案: 数据分片、访问分散、数据复制、本地缓存、异步处理