关于ShardingSphere内置分片算法及其数据倾斜问题总结

ShardingSphere是一款不错的分库分表中间件,并且其内置提供了多种分片算法。但是使用内置的分片算法会造成数据倾斜问题。下面以5.2.0版本的ShardingSphere详细介绍下几种内置分片算法并且分析下数据倾斜问题。

一、ShardingSphere内置分片算法介绍

根据官网描述,ShardingSphere内置提供了多种分片算法,能够满足用户绝大多数业务场景的需要。

按照类型可以划分为自动分片算法、标准分片算法、复合分片算法和 Hint 分片算法。其中,自动分片算法包括:取模分片算法,哈希取模分片算法,基于分片容量的范围分片算法,基于分片边界的范围分片算法,自动时间段分片算法。标准分片算法包括:行表达式分片算法,时间范围分片算法。复合分片算法只有复合行表达式分片算法。Hint 分片算法只有Hint 行表达式分片算法。

下面详细介绍下ShardingSphere内置的几种分片算法。

1、自动分片算法

1.1取模分片算法

 其中,分片数量是分库数量或分表数量。

该算法的实现类是org.apache.shardingsphere.sharding.algorithm.sharding.mod.ModShardingAlgorithm.java。

该算法的实质是根据分片键的值分别对数据库或表得分片数量取模来分片,所以分片键必须是数字或纯数字的字符串。

优点:

       1. 简单方便

        2.算法容易理解

        3.可以方便的算出数据落到哪个库哪个表

缺点:

        1.分片键的值的类型受限,必须是纯数字,不能包括字母等

        2.会造成数据倾斜

1.2哈希取模分片算法

 其中,分片数量是数据库数量或表的数量。

该算法的实现类是org.apache.shardingsphere.sharding.algorithm.sharding.mod.HashModShardingAlgorithm.java。

该算法的本质也是取模,不过是先计算分片键的哈希值,即计算分片值的hashCode的绝对值,然后再根据分片数量来取模。所以该算法允许分片键的值包括数字或字母。

优点:

       1. 简单方便

        2.算法容易理解

        3.分片键的值的类型不受限,支持分片值非纯数字类型

缺点:

        1.不方便的快速算出数据落到哪个库哪个表

        2.会造成数据倾斜

1.3基于分片容量的范围分片算法

其中范围上下界必填,该范围根据分片容量将实数区间分为几个部分,根据分片值在具体哪个区间来确定数据落在具体哪个库表。该算法也要求分片值必须为纯数字类型。

该算法的实现类是org.apache.shardingsphere.sharding.algorithm.sharding.range.VolumeBasedRangeShardingAlgorithm.java。

我们来看下该算法具体是如何进行分片的。我们以四库(db_0、db_1、db_2、db_3)四表(order_0、order_1、order_2、order_3),分片键为order_id,range-lower=10,range-upper=25,sharding-volume=10为例。该算法根据范围下界,范围上届及分片容量将整个(-∞,+∞)区间分成5部分,分别为(-∞,10)、[10,20)、[20,25)、(25,+∞)。其内部通过calculatePartitionRange方法缓存维护一个分区及其范围的Map<Integer, Range<Comparable<?>>>,其中0分区对应(-∞,10),1分区对应[10,20),2分区对应[20,25)、3分区对应(25,+∞)。若此时order_id=22,则库表分别在2分区的范围内,所以该条记录在db_2.order_2。

需要注意的是分区数最好和库数或表数相同。比如将上例改为只有两个分库(db_0、db_1),根据该算法算出该条记录应该在db_2.order_2中,但是没有db_2,此时就会报数据库路由错误。

优点:

       1. 简单方便

        2.算法容易理解

        3.可以方便的算出数据落到哪个库哪个表

缺点:

        1.分片键的值的类型受限,必须是纯数字,不能包括字母等

1.4基于分片边界的范围分片算法

该算法与基于分片容量的范围分片算法有些类似,该算法根据设置的分片的范围边界将实数区间分为几个部分,根据分片值在具体哪个区间来确定数据落在具体哪个库表。该算法也要求分片值必须为纯数字类型。

该算法的实现类是 org.apache.shardingsphere.sharding.algorithm.sharding.range.BoundaryBasedRangeShardingAlgorithm.java。

我们来看下该算法具体是如何进行分片的。我们以两库(db_0、db_1)四表(order_0、order_1、order_2、order_3),分片键为order_id,分库算法的sharding-ranges=8,分表算法的sharding-ranges=1,5,10为例。该算法根据分库的分片的范围边界将整个(-∞,+∞)区间分成两部分,其中0分区对应(-∞,8),1分区对应[8,+∞)。根据分表的分片的范围边界将整个(-∞,+∞)区间分成四部分,其中0分区对应(-∞,1),1分区对应[1,5),2分区对应[5,10),3分区对应[10,+∞)。若此时order_id=22,则在库的一分区,表的三分区范围内,所以该条记录在db_1.order_3。

需要注意的是分区数最好和库数或表数相同。比如将上例改为只有三个分表(order_0、order_1、order_2),根据该算法算出该条记录应该在db_1.order_3中,但是没有order_3,此时就会报路由错误。

优点:

       1. 简单方便

        2.算法容易理解

        3.可以方便的算出数据落到哪个库哪个表

缺点:

        1.分片键的值的类型受限,必须是纯数字,不能包括字母等

1.5自动时间段分片算法

该算法针对时间字段类型作为分片键,与前面算法有些类似,都是划分区间,不过不同的是,该算法是将时间进行划分分片。该算法根据设置的分片的起始时间范围、分片的结束时间范围、单一分片所能承载的最大时间,将整个时间区间分为几个部分,根据分片值在具体哪个时间区间来确定数据落在具体哪个库表。

其中,datetime-lower和datetime-upper可以相等,sharding-seconds不能为0。

该算法的实现类是 org.apache.shardingsphere.sharding.algorithm.sharding.datetime.AutoIntervalShardingAlgorithm.java。

我们来看下该算法具体是如何进行分片的。我们以两库(db_0、db_1)四表(order_0、order_1、order_2、order_3),分片键为order_time,分库算法的datetime-lower=2022-12-21 00 :00:00,datetime-upper=2022-12-21 00 :00:00,sharding-seconds=1。分表算法的datetime-lower=2022-12-21 00:00:00,datetime-upper=2022-12-21 00:00:10,sharding-seconds=5为例。该算法根据分库的配置参数将整个时间区间分成两部分,其中0分区对应(-∞,2022-12-21 00 :00:00],1分区对应(2022-12-21 00 :00:00,+∞)。根据分表的配置参数将整个时间区间分成四部分,其中0分区对应(-∞,2022-12-21 00 :00:00],1分区对应(2022-12-21 00 :00:00,2022-12-21 00 :00:05],2分区对应(2022-12-21 00 :00:05,2022-12-21 00 :00:10],3分区对应(2022-12-21 00 :00:10,+∞)。若此时order_time=2022-12-21 00 :00:06,则在库的一分区,表的二分区范围内,所以该条记录在db_1.order_2。

需要注意的是分区数最好和库数或表数相同。比如将上例改为只有两个分表(order_0、order_1),根据该算法算出该条记录应该在db_1.order_2中,但是没有order_2,此时就会报路由错误。

优点:

       1. 简单方便

        2.算法容易理解

        3.可以方便的算出数据落到哪个库哪个表

缺点:

        1.分片键类型受限,必须时间字段类型

        2.会造成数据倾斜

2、标准分片算法

2.1行表达式分片算法

使用 Groovy 的表达式,提供对 SQL 语句中的 = 和 IN 的分片操作支持,只支持单分片键。 对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的 Java 代码开发,如: t_user_$->{u_id % 8} 表示 t_user 表根据 u_id 模 8,而分成 8 张表,表名称为 t_user_0 到 t_user_7

该算法的实现类是 org.apache.shardingsphere.sharding.algorithm.sharding.inline.InlineShardingAlgorithm.java。

其核心也是根据分片键的值对分片数量进行取模。

优点:

       1. 简单方便

        2.算法容易理解

        3.可以方便的算出数据落到哪个库哪个表

缺点:

        1.分片键的值的类型受限,必须是纯数字,不能包括字母等

        2.会造成数据倾斜

2.2时间范围分片算法

此算法主动忽视了 datetime-pattern 的时区信息。 这意味着当 datetime-lowerdatetime-upper 和传入的分片键含有时区信息时,不会因为时区不一致而发生时区转换。 当传入的分片键为 java.time.Instant 时存在特例处理,其会携带上系统的时区信息后转化为 datetime-pattern 的字符串格式,再进行下一步分片。

 

 该算法的实现类是 org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm.java

我们来看下该算法具体是如何进行分片的。以四表(order_20221201、order_20221202、order_20221203、order_20221204),分片键为order_time,分表算法的datetime-pattern=yyyy-MM-dd HH:mm:ss,datetime-lower=2022-12-01 00 :00:00,datetime-upper=2022-12-04 00 :00:00,sharding-suffix-pattern=yyyyMMdd,datetime-interval-amount=1为例。若此时order_time=2022-12-03 15:00:00,根据算法计算,后缀为20221203,则该数据落在order_20221203表中。

需要注意的是分区数最好和库数或表数相同。比如将上例改为只有两个分表(order_20221201、order_20221202),根据该算法算出该条记录应该在order_20221203中,但是没有order_20221203表,此时就会报路由错误。

优点:

       1. 简单方便

        2.算法容易理解

        3.适合将某一时间段的数据存储一张表的情况,比如每天数据存储一张表, 或每月或每个季度或每年等数据存储一张表。

缺点:

        1.算法配置较麻烦

        2.分片键类型受限,必须时间字段类型

        3.会造成数据倾斜

3、复合分片算法

3.1复合行表达式分片算法

该算法支持分片键多字段的情况,其中sharding-columns如果只配置一个字段,那么该算法将退化为行表达式分片算法。

该算法的实现类是 org.apache.shardingsphere.sharding.algorithm.sharding.complex.ComplexInlineShardingAlgorithm.java

我们来看下该算法具体是如何进行分片的。以四表(order_00, order_01, order_10, order_11),分片键为type和order_id,分表算法的sharding-columns=type,order_id,algorithm-expression=order_${type % 2}${order_id % 2}为例。若此时type=1, order_id=4,根据算法计算,type % 2=1,order_id % 2 = 0,则该数据落在order_10表中。

优点:

       1. 简单方便

        2.算法容易理解

        3.支持多个分片键

        4.可以方便的算出数据落到哪个库哪个表

缺点:

        1.分片键的值的类型受限,必须是纯数字,不能包括字母等

        2.会造成数据倾斜

4、Hint 分片算法

4.1Hint 行表达式分片算法

该算法和行表达式分片算法很类似,不同的是可以不配置algorithm-expression属性,不配置的话默认是找表名为分片键的值的表。

 该算法的实现类是 org.apache.shardingsphere.sharding.algorithm.sharding.hint.HintInlineShardingAlgorithm.java

优点:

       1. 简单方便

        2.算法容易理解

        3.可以方便的算出数据落到哪个库哪个表

缺点:

        1.分片键的值的类型受限,必须是纯数字,不能包括字母等

        2.会造成数据倾斜

5、自定义类分片算法

通过配置分片策略类型和算法类名,实现自定义扩展。 CLASS_BASED 允许向算法类内传入额外的自定义属性,传入的属性可以通过属性名为 props 的 java.util.Properties 类实例取出。

如果strategy=STANDAR,则自定义算法类需要实现org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm.java接口;strategy=COMPLEX,则自定义算法类需要实现org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingAlgorithm.java接口;strategy=HINT,则自定义算法类需要实现org.apache.shardingsphere.sharding.api.sharding.hint.HintShardingAlgorithm.java接口。

优点:

       1. 可以根据业务情况实现自己的分库分表算法,算法较灵活

       2.分片键的值可以是任意形式

       3.一定程度上可以避免数据倾斜问题

缺点:

        1.需要通过Java类的形式实现分库分表策略,增加编码量

二、ShardingSphere内置分片算法的数据倾斜问题

此处所谓的数据倾斜是指,在通过分片算法后大量数据落在某些库表中,导致另某些库表中只有少量或没有数据,导致数据分布不够均匀。

通过上面关于ShardingSphere内置分片算法的介绍,我们可以根据实际的业务情况选择适合的分片算法。如果希望按时间段分片可以选择自动时间段分片算法或者时间范围分片算法。但是该算法也存在因为某些时间段数据激增,某些时间段数据量很少导致数据倾斜。而以按数据库数或表数取模的算法则会存在永远不会有数据落在某些表中的情况。取模分片算法、哈希取模分片算法的实质就是取模。行表达式分片算法、复合行表达式分片算法、Hint 行表达式分片算法的取模形式的行表达式算法也会存在这个问题。比方说,我们按5库20表进行发分库分表,只有分片键是0或是5的倍数才会落到0库,但是又因为是20表,所以落到0库的数据只会分布在0或5或10或15这4个表中,导致路由到0库的数据永远不会落到除上述4个表的其他16个表中,这严重违背了我们的初衷。

下面是分别以2库3表和5库20表,分片值从0-200对库表数进行取模分片的分库分表演算。

分片值2库3表5库20表
2库3表5库20表
00000
11111
20222
31033
40144
51205
60016
71127
80238
91049
1001010
1112111
1200212
1311313
1402414
1510015
1601116
1712217
1800318
1911419
200200
211011
220122
231233
240044
251105
260216
271027
280138
291249
3000010
3111111
3202212
3310313
3401414
3512015
3600116
3711217
3802318
3910419
400100
411211
420022
431133
440244
451005
460116
471227
480038
491149
5002010
5110111
5201212
5312313
5400414
5511015
5602116
5710217
5801318
5912419
600000
611111
620222
631033
640144
651205
660016
671127
680238
691049
7001010
7112111
7200212
7311313
7402414
7510015
7601116
7712217
7800318
7911419
800200
811011
820122
831233
840044
851105
860216
871027
880138
891249
9000010
9111111
9202212
9310313
9401414
9512015
9600116
9711217
9802318
9910419
1000100
1011211
1020022
1031133
1040244
1051005
1060116
1071227
1080038
1091149
11002010
11110111
11201212
11312313
11400414
11511015
11602116
11710217
11801318
11912419
1200000
1211111
1220222
1231033
1240144
1251205
1260016
1271127
1280238
1291049
13001010
13112111
13200212
13311313
13402414
13510015
13601116
13712217
13800318
13911419
1400200
1411011
1420122
1431233
1440044
1451105
1460216
1471027
1480138
1491249
15000010
15111111
15202212
15310313
15401414
15512015
15600116
15711217
15802318
15910419
1600100
1611211
1620022
1631133
1640244
1651005
1660116
1671227
1680038
1691149
17002010
17110111
17201212
17312313
17400414
17511015
17602116
17710217
17801318
17912419
1800000
1811111
1820222
1831033
1840144
1851205
1860016
1871127
1880238
1891049
19001010
19112111
19200212
19311313
19402414
19510015
19601116
19712217
19800318
19911419
2000200

 个人是不太推荐单纯的根据分片键取模分片算法的,在实际的项目中一直使用的是自定义类分片算法,实质是根据分片键的最后一位数字对数据库数取模,分片键的倒数二三位数字对表数取模。这种方式可以尽量避免数据倾斜问题,不会存在某些库表永远不会有数据的情况。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ShardingSphere5.2是一个开源的分布式数据库中间件,可以用于在分布式系统中进行数据分片处理。在ShardingSphere5.2中,我们可以配置自定义的分片算法来实现自定义的数据分片策略。 首先,我们需要创建一个自定义的分片算法类。这个类需要实现ShardingAlgorithm接口,并重写其中的方法。ShardingAlgorithm接口包含了分片算法的两个核心方法:doSharding和init。 在doSharding方法中,我们可以编写自己的分片逻辑。我们可以根据业务需求来判断数据应该分配到哪个分片上,然后返回对应的分片信息。在这个方法中,我们可以使用一些参数来获取到分片规则、分片键值和分片配置等信息。 在init方法中,我们可以进行一些初始化操作。比如,可以读取和解析配置文件,获取到分片的规则和配置信息,并进行一些初始化设置。 接下来,我们需要在ShardingSphere的配置文件中指定使用我们的自定义分片算法。我们可以在配置文件中通过配置项指定自定义分片算法的类名。同时,我们还需要配置其他相关的分片规则、分布式数据源等信息。 最后,我们将整个配置文件加载到ShardingSphere中,并启动分片功能。当系统需要进行数据分片时,ShardingSphere会自动调用我们的自定义分片算法来进行数据分片处理。 总结来说,ShardingSphere5.2配置自定义分片算法的步骤包括:创建自定义的分片算法类并实现ShardingAlgorithm接口、编写自己的分片逻辑、在配置文件中指定自定义分片算法、加载配置文件到ShardingSphere并启动分片功能。通过这些步骤,我们就可以实现自定义的数据分片策略。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luffylv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值