先聊一下什么是数据倾斜,数据倾斜分两类。
- 数据访问倾斜:当某个实例上出现热点 key,会导致其访问非常频繁。
- 数据量倾斜:在默写情况下,当实例上的数据不是均匀分布的,这就会导致某些实例上会有很多数据。
当我们现网发现了数据倾斜,会导致我们整个集群访问的速度会变慢,整个性能会下降的很明显,甚至会出现内存资源耗尽,服务不可用等问题。
本篇文章我们来聊聊,出现了数据倾斜我们第一时间应该怎么去快速解决。
1. 数据量倾斜
当我们实例的数据出现数据量级别的倾斜时,会造成切片集群多个实例之间数据分布不均匀,如下图:
为什么会造成数据量倾斜呢?其实主要有三个原因
- BIg Key
- Slot 分配不均匀
- Hash Tag
1.1 BigKey
什么是BigKey?
- value 值很大(string类型)
- 存储了大量的数据(集合类型)
当我们某一个实例上出现了一个 BigKey,导致这个实例的数据量会一下子增大,消耗大量的内存资源(此时会发现内存的使用容量一下被占用很多)。BigKey 会造成实例 IO 线程阻塞,当这个 BigKey 被频繁访问,会影响到这个实例的响应速度,可能也会影响到整个集群的处理速度。
我们在业务开发中是不允许出现 Bigkey 的,我们应该避免将很多的数据保存在同一个键值对中。
当 Bigkey是一个集合类型,我们会将其拆分成很多个小的集合类型数据,分散在其他不同的实例,
eg:我们有一个 key 存储了 1000w 个用户信息,此时我们很多同学会选择用 Hash 类型存储。 那么有人考虑过这会是一个 Bigkey 吗?
解决方案:我们将其拆分成 100 个小集合,每个集合只保存 10 w 个用户信息(可以选择 ID 从 1 - 10w,10w-20w 一组),分布到不同的实例上,避免单个实例压力过大。
1.2 Slot 分配不均匀
什么是 slot?
首先,Redis Cluster 一共有 16384 个槽(slot),Redis Cluster 中存储每个节点负责哪些 Slot。Redis Cluster 中存储每个 slot 对应哪一个节点。
当我们的 DBA 没有采用均衡的方法分配 Slot,就会导致大量的数据被我分配到同一个 Slot 中。就会出现大量的数据全部分配到一个实例上,造成数据倾斜。
DBA 给每个实例分配数据时会选择一个配置高一点的实例权重高一点,这样会给某一个实例多分配一个 Slot。这样就会给某一个实例造成数据倾斜。
解决方案:规范 DBA 操作流程,分配数据前,Slot 数量平均分配,禁止把过多的 slot 分配给实例配置高的机器。
1.3 Hash Tag
Hash Tag 是值在键值对中的一对花括号,仅仅花括号中间的部分参与 hash CRC16,hash CRC16 结果为 slot 编号。
key := "a{b}c"
hash tag 的用途是强制多个key写入同一个slot,也就是同一个节点(假设没有正在进行分片)。
举个 eg:
key | hash | slot |
user:info:{2313} | CRC16(2313) mod16384 | 1024 |
user:info:{5231} | CRC16(5231) mod16384 | 3210 |
user:pay:{2313} | CRC16(2313) mod16384 | 1024 |
user:pay:{5231} | CRC16(5231) mod16384 | 3210 |
上方表出现了 key user:info:{2313},当 key 为 user:profile:{2313},其中花括号中 2313 就是 Hash Tag,对 Hash Tag 也就是 2313 做 crc16 运算得到值 1024。我们可以看到 key user:pay:{2313} 的值也是1024,那么他们就会被映射到同一个 slot 中,同一个实例上。
所以,使用 HashTag 的好处就是,我们不需要关注 key 的值,只要我们的 HashTag 的值是一样的,那么这些 key 都会被映射到同一个 slot 中,也会被分配到同一个实例上。
那么有人会问,Hash Tag 一般都用在什么场景?
他其实主要用在 Codis & Redis Cluster 中,为了支持跨实例进行事务操作和范围查询的使用场景,我们在日常开发中是需要对数据进行事务操作或需要挨个查询每个实例得到结果。
这样操作会很麻烦,所以我们就会用 Hash Tag 将需要执行的操作(事务、范围查询)数据映射到同一个实例中,从而达到事务或范围查询操作。
但是,这会有一个问题,当大量的数据都被映射到一个实例上,在分片集群中就会出现数据负载不均衡,就会出现数据倾斜。
那么就只能对这 2 者间做权衡取舍了。
我个人建议,若我们使用 Hash Tag 切片的时候会对实例造成访问上的压力,那么我们此时可以考虑不使用 Hash Tag 进行切片。因为数据倾斜会导致实例响应时间长,可能会影响到整个集群,导致服务不可用。可以采用客户端来执行事务或者范围查询来实现。
2. 数据访问倾斜
在电商类公司大促场景,我们会有大量的热销商品信息需要做热点数据,我们会将热点数据放到 Redis 中,当热点数据被存放到某个实例中,会导致这个实例的访问量要高出其他实例。用一个图解释一下:
当我们热点数据出现在实例 2 中,此时就会出现数据倾斜,那我们会怎么应对呢?
我们知道 Redis 的热点数据读操作会偏多,当出现了热点数据这种情况,我们一般会采用热点数据多副本的方式来应对。
2.1 热点数据多副本
当我们出现热点数据时,我们通常的做法是将这个数据复制出多份,此时复制出来的我们会称自副本,然后给每一个副本数据的 key 增加一个前缀,这个前缀可以自己定义其规则,只要保证不要重复就 OK,这样我们通过 CRC 就不会映射到同一个 Solt 中。这样,这个热点数据就以副本数据的方式分布到了其他实例中。
注意:这里用坑,用副本这种方法只适用于只读的热点数据,如果热点数据是有读有写的不建议使用这种方法,因为还要保证每个实例中 key 对应的热点数据一致性,这样就会造成我们负担。
对于有读有写的热点数据,我们建议增加实例的硬件资源吧,升级机器来硬抗 QPS。
3. 总结
本文我们介绍了什么是数据倾斜?以及 2 种情况:数据量倾斜、数据访问倾斜。也介绍了造成数据倾斜的原因、影响、以及对应的解决方案。希望在未来的使用中和面试中能够知道和回答出,加油。
最后,经过排查后得到的原因是因为 bigkey 造成的,因为写入端没有对异常值进行过滤处理,导致一个 bigkey 存到了 Codis 中,C 端 QPS 峰值很多请求同时访问了这个 bigkey 导致实例响应慢了很多,整个接口时延拉长了很多。
我们临时修复,首先过滤异常值的读和写,然后低峰时删除 bigkey 的方式进行处理。
作者:程序员祝融
链接:https://juejin.cn/post/7171814667235360776
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。