ShardingSphere基础

ShardingSphere包含ShardingJDBCShardingProxyShardingSidecar三个重要的产品。ShardingJDBC是用来做客户端分库分表的产品,而ShardingProxy是用来做服务端分库分表的产品,其中ShardingSidecar是针对Service Mesh定位的一个分库分表插件。

ShardingJDBC为轻量级Java框架,是客户端的一个工具包,在Java的JDBC层提供的额外服务。它使⽤客户端直连数据库,以Jar包形式提供服务无需额外部署和依赖,可理解为增强版的JDBC驱动完全兼容JDBC和各种ORM框架。所有分库分表逻辑均由业务方自己控制,功能相对灵活,支持的数据库也非常多,但对业务侵入大,需要业务方自己定制所有的分库分表逻辑。

ShardingProxy为透明化的数据库代理端,是一个独立部署的服务,提供封装了数据库二进制协议的服务端版本⽤于完成对异构语言的⽀持。对业务方无侵入,业务方可以像用一个普通的MySQL服务一样进行数据交互,基本上感觉不到后端分库分表逻辑的存在,但也意味着功能会比较固定,能够支持的数据库也比较少;目前提供MySQLPostgreSQL版本,可使用任何兼容MySQL/PostgreSQL协议的访问客户端。

ShardingSphere核心功能是数据分片读写分离,通过ShardingJDBC应用可透明的使用JDBC访问已经分库分表、读写分离的多个数据源,而不用关心数据源的数量以及数据如何分布。

  • 逻辑表水平拆分的数据库的相同逻辑数据结构表的总称
  • 真实表:在分片的数据库中真实存在的物理表
  • 数据节点:数据分片的最小单元,由数据源名称数据表组成
  • 绑定表分片规则一致的主表和子表
  • 广播表:也叫公共表所有分片数据源中都存在的表表结构表中的数据在每个数据库中都完全一致
  • 分片键用于分片的数据库字段,是将数据库或表进行水平拆分的关键字段,SQL中若没有分片字段,将会执行全路由,性能会很差。
  • 分片算法通过分片算法将数据进行分片,支持通过=BETWEENIN分片,分片算法需要由应用开发者自行实现,灵活度非常高
  • 分片策略:真正用于进行分片操作的是分片键+分片算法即分片策略,在ShardingJDBC中一般采用基于Groovy表达式inline分片策略,通过一个包含分片键的算法表达式来制定分片策略,如t_user_$->{u_id%8}

分片算法

ShardingJDBC整个分库分表的核心在于配置的分片算法,使用inline分片算法提供一个分片键和一个分片表达式来制定分片算法,该方式配置简单功能灵活,是分库分表最佳的配置方式,且可满足绝大多数分库分片场景。但若针对一些更为复杂的分片策略,如多分片键按范围分片等场景,inline分片算法就不能满足了,ShardingSphere还提供的其他几种分片策略,目前提供了五种分片策略。

NoneShardingStrategy

不分片,严格来说不算是一种分片策略了,只是ShardingSphere也提供了这么一个配置。

InlineShardingStrategy

最常用的分片方式,通过inline.sharding-column配置分片键,通过algorithm-expression配置分片表达式,最终按照分片表达式来进行分片。通过table-strategy配置表的分片策略,通过database-strategy配置库的分片策略

1
2
3
4
5
6
# inline分片策略
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding-column=cid
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm-expression=course_$->{cid%2+1}

spring.shardingsphere.sharding.tables.course.database-strategy.inline.sharding-column=cid
spring.shardingsphere.sharding.tables.course.database-strategy.inline.algorithm-expression=m$->{cid%2+1}

StandardShardingStrategy

只支持单分片键的标准分片策略,通过standard.sharding-column配置分片键,通过standard.precise-algorithm-class-name配置按照=IN逻辑的精确分片算法类名且该类必须实现io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm接口,通过standard.range-algorithm-class-name配置按照Between条件进行的范围分片算法类名且该类必须实现io.shardingsphere.api.algorithm.sharding.standard.RangeShardingAlgorithm接口,分库分表精确分片算法范围分片算法都是实现这两个接口。精确分片算法是必须提供的,范围分片算法是可选的

1
2
3
4
5
6
7
spring.shardingsphere.sharding.tables.course.table-strategy.standard.sharding-column=cid
spring.shardingsphere.sharding.tables.course.table-strategy.standard.precise-algorithm-class-name=com.eleven.icode.shardingsphere.algorithem.MyPreciseTableShardingAlgorithm
spring.shardingsphere.sharding.tables.course.table-strategy.standard.range-algorithm-class-name=com.eleven.icode.shardingsphere.algorithem.MyRangeTableShardingAlgorithm

spring.shardingsphere.sharding.tables.course.database-strategy.standard.sharding-column=cid
spring.shardingsphere.sharding.tables.course.database-strategy.standard.precise-algorithm-class-name=com.eleven.icode.shardingsphere.algorithem.MyPreciseDSShardingAlgorithm
spring.shardingsphere.sharding.tables.course.database-strategy.standard.range-algorithm-class-name=com.eleven.icode.shardingsphere.algorithem.MyRangeDSShardingAlgorithm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyPreciseTableShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
String logicTableName = shardingValue.getLogicTableName(); // 获取逻辑表
String cid = shardingValue.getColumnName(); // 获取分片键
Long cidValue = shardingValue.getValue();
// 实现 course_$->{cid%2+1)
BigInteger shardingValueB = BigInteger.valueOf(cidValue);
BigInteger resB = shardingValueB.mod(new BigInteger("2")).add(new BigInteger("1"));
String key = logicTableName + "_" + resB;
if (availableTargetNames.contains(key)) { // couse_1, course_2
return key;
}
throw new UnsupportedOperationException("route " + key + " is not supported ,please check your config");
}
}
1
2
3
4
5
6
7
8
9
10
11
public class MyRangeTableShardingAlgorithm implements RangeShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
// select * from course where cid between 1 and 100;
Long upperVal = shardingValue.getValueRange().upperEndpoint(); //100
Long lowerVal = shardingValue.getValueRange().lowerEndpoint(); //1
// TODO 完成具体的分表策略
String logicTableName = shardingValue.getLogicTableName();
return Arrays.asList(logicTableName + "_1", logicTableName + "_2");
}
}

ComplexShardingStrategy

支持多分片键的复杂分片策略,通过complex.sharding-columns配置分片键,多个分片键用逗号分隔,通过complex.algorithm-class-name配置按多个分片列进行综合分片分片算法实现类,且该实现类必须实现org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm接口。

1
2
3
4
5
spring.shardingsphere.sharding.tables.course.table-strategy.complex.sharding-columns= cid, user_id
spring.shardingsphere.sharding.tables.course.table-strategy.complex.algorithm-class-name=com.eleven.icode.shardingsphere.algorithem.MyComplexTableShardingAlgorithm

spring.shardingsphere.sharding.tables.course.database-strategy.complex.sharding-columns=cid, user_id
spring.shardingsphere.sharding.tables.course.database-strategy.complex.algorithm-class-name=com.eleven.icode.shardingsphere.algorithem.MyComplexDSShardingAlgorithm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyComplexTableShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<Long> shardingValue) {
Range<Long> cidRange = shardingValue.getColumnNameAndRangeValuesMap().get("cid");
Collection<Long> userIdCol = shardingValue.getColumnNameAndShardingValuesMap().get("user_id");
Long upperVal = cidRange.upperEndpoint();
Long lowerVal = cidRange.lowerEndpoint();
List<String> res = new ArrayList<>();
for (Long userId : userIdCol) {// course_{userID%2+1}
BigInteger userIdB = BigInteger.valueOf(userId);
BigInteger target = userIdB.mod(new BigInteger("2")).add(new BigInteger("1"));
res.add(shardingValue.getLogicTableName() + "_" + target);
}
return res;
}
}

HintShardingStrategy

不需要分片键的强制分片策略,其分片键不跟SQL语句关联而是程序指定。对于一些复杂的情况如select count(*) from (select userid from t_user where userid in (1,3,5,7,9))这样的SQL语句,无法通过SQL语句指定一个分片键,这时就可通过程序给他指定一个分片键,如按userid奇偶分片的策略下,可指定1作为分片键,然后自行指定他的分片策略。

通过hint.algorithm-class-name配置分片算法实现类,且该类必须实现org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm接口,该算法类同样需要分片键,通过HintManager.addDatabaseShardingValue方法指定分库分片键和通过HintManager.addTableShardingValue方法指定分表分片键。使用时该分片键是线程隔离的,只在当前线程有效,建议使用之后立即关闭,或者用try资源方式打开。

Hint分片策略没有完全按照SQL解析树来构建分片策略,绕开了SQL解析,对某些比较复杂语句,Hint分片策略性能有可能会比较好。但Hint强制路由在使用时有非常多的限制:

1
2
3
4
5
6
7
-- 不支持UNION
SELECT * FROM t_order1 UNION SELECT * FROM t_order2
INSERT INTO tbl_name (col1, col2, …) SELECT col1, col2, … FROM tbl_name WHERE col3 = ?
-- 不支持多层子查询
SELECT COUNT(*) FROM (SELECT * FROM t_order o WHERE o.id IN (SELECT id FROM t_order WHERE status = ?))
-- 不支持函数计算,ShardingSphere只能通过SQL字面提取用于分片的值
SELECT * FROM t_order WHERE to_date(create_time, 'yyyy-mm-dd') = '2019-01-01';
1
spring.shardingsphere.sharding.tables.course.table-strategy.hint.algorithm-class-name=com.eleven.icode.shardingsphere.algorithem.MyHintTableShardingAlgorithm
1
2
3
4
5
6
7
8
9
10
public class MyHintTableShardingAlgorithm implements HintShardingAlgorithm<Integer> {
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, HintShardingValue<Integer> shardingValue) {
String key = shardingValue.getLogicTableName() + "_" + shardingValue.getValues().toArray()[0];
if (availableTargetNames.contains(key)) {
return Arrays.asList(key);
}
throw new UnsupportedOperationException("route " + key + " is not supported ,please check your config");
}
}

实例

首先定义一个数据源m1,并对m1进行实际的JDBC参数配置,spring.shardingsphere.sharding.tables.course开头的一系列属性即定义了一个名为course的逻辑表actual-data-nodes属性即定义course逻辑表的实际数据分布情况,他分布在m1.course_1和m1.course_2两个表。key-generator属性配置主键列以及主键生成策略ShardingJDBC默认提供了UUIDSNOWFLAKE两种分布式主键生成策略table-strategy属性配置分库分表策略,分片键为cid属性,分片算法为course_$->{cid%2+1},表示按照cid模2+1的结果,然后加上前面的course__ 部分作为前缀就是其实际表结果,该表达式计算出来的结果需要能够与实际数据分布中的一种情况对应上否则就会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 配置数据源,可配置多个,用逗号分隔
spring.shardingsphere.datasource.names=m1
# 数据源m1
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3307/coursedb?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root
# 通过groovy脚本配置真实表分布,指定course表分布情况,配置表在哪个数据库里面,表名称都是什么
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m1.course_$->{1..2}
# 指定course表里中主键cid生成策略SNOWFLAKE
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
# 雪花算法的一个可选参数
spring.shardingsphere.sharding.tables.course.key-generator.props.worker.id=1
# 通过groovy脚本指定分表分片策略,约定cid值偶数添加到course_1表,奇数添加到course_2表
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding-column=cid
# 根据计算的字段算出对应的表名
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm-expression=course_$->{cid%2+1}
# 打开sql输出日志
spring.shardingsphere.props.sql.show=true
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true

通过上面的配置ShardingJDBC会帮我们完成具体的SQL转换以及数据的调用,不需要做其他特别的处理。最终这些配置被转换映射到ShardingRuleConfiguration中:

1
2
3
4
5
6
7
8
9
10
11
12
13
tables:
course:
actualDataNodes: m1.course_$->{1..2} # 数据节点
keyGenerator: # 主键生成策略配置
column: cid # 主键
props:
worker.id: '1'
type: SNOWFLAKE # 具体的主键生成策略
logicTable: course # 逻辑表
tableStrategy: # 分表策略
inline: # 分片策略
algorithmExpression: course_$->{cid%2+1} # 具体的分片算法
shardingColumn: cid # 分片键

多数据源

多数据源的情况除了要配置分表分片策略,还需要配置分库分片策略,与前面的区别在于指定了多个数据源,actual-data-nodes数据节点配置所有变化,且增加了分库分片策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 配置数据源,可配置多个,用逗号分隔
spring.shardingsphere.datasource.names=m1,m2
# 数据源m1
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3307/coursedb?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root
# 数据源m2
spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3307/coursedb2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=root
# 通过groovy脚本配置真实表分布,指定course表分布情况,配置表在哪个数据库里面,表名称都是什么
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m$->{1..2}.course_$->{1..2}
# 指定course表里中主键cid生成策略SNOWFLAKE
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
# 雪花算法的一个可选参数
spring.shardingsphere.sharding.tables.course.key-generator.props.worker.id=1
# 通过groovy脚本指定分表分片策略,约定cid值偶数添加到course_1表,奇数添加到course_2表
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding-column=cid
# 根据计算的字段算出对应的表名
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm-expression=course_$->{cid%2+1}
# 分库策略
spring.shardingsphere.sharding.tables.course.database-strategy.inline.sharding-column=cid
spring.shardingsphere.sharding.tables.course.database-strategy.inline.algorithm-expression=m$->{cid%2+1}
# 打开sql输出日志
spring.shardingsphere.props.sql.show=true
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true

广播表配置

1
2
3
4
# 广播表配置
spring.shardingsphere.sharding.broadcast-tables=t_dict
spring.shardingsphere.sharding.tables.t_dict.key-generator.column=dict_id
spring.shardingsphere.sharding.tables.t_dict.key-generator.type=SNOWFLAKE

绑定表

绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将⼤⼤提升。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 配置数据源,可配置多个,用逗号分隔
spring.shardingsphere.datasource.names=m1
# 数据源m1
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/coursedb?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root
# 通过groovy脚本配置真实表分布,指定t_dict表分布情况,配置表在哪个数据库里面,表名称都是什么
spring.shardingsphere.sharding.tables.t_dict.actual-data-nodes=m1.t_dict_$->{1..2}

spring.shardingsphere.sharding.tables.t_dict.key-generator.column=dict_id
spring.shardingsphere.sharding.tables.t_dict.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.t_dict.key-generator.props.worker.id=1
# t_dict表的分片键和分片算法
spring.shardingsphere.sharding.tables.t_dict.table-strategy.inline.sharding-column=ustatus
spring.shardingsphere.sharding.tables.t_dict.table-strategy.inline.algorithm-expression=t_dict_$->{ustatus.toInteger()%2+1}
# 通过groovy脚本配置真实表分布,指定user表分布情况,配置表在哪个数据库里面,表名称都是什么
spring.shardingsphere.sharding.tables.user.actual-data-nodes=m1.t_user_$->{1..2}
# 指定user表里中主键user_id生成策略SNOWFLAKE
spring.shardingsphere.sharding.tables.user.key-generator.column=user_id
spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.user.key-generator.props.worker.id=1
# user表的分片键和分片算法
spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=ustatus
spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=t_user_$->{ustatus.toInteger()%2+1}
# 绑定表示
spring.shardingsphere.sharding.binding-tables[0]=user,t_dict
# 打开sql输出日志
spring.shardingsphere.props.sql.show = true
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true

读写分离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 配置主从数据源,要基于MySQL主从架构。
spring.shardingsphere.datasource.names=m0,s0
# 数据源m0
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3307/masterdemo?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=root
# 数据源s0
spring.shardingsphere.datasource.s0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.s0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.s0.url=jdbc:mysql://localhost:3308/masterdemo?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.s0.username=root
spring.shardingsphere.datasource.s0.password=root
# 读写分离规则,m0主库,s0从库
spring.shardingsphere.sharding.master-slave-rules.ds0.master-data-source-name=m0
spring.shardingsphere.sharding.master-slave-rules.ds0.slave-data-source-names[0]=s0
# 基于读写分离的表分片
spring.shardingsphere.sharding.tables.t_dict.actual-data-nodes=ds0.t_dict
# 指定t_dict表里中主键dict_id生成策略SNOWFLAKE
spring.shardingsphere.sharding.tables.t_dict.key-generator.column=dict_id
spring.shardingsphere.sharding.tables.t_dict.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.t_dict.key-generator.props.worker.id=1
# 打开sql输出日志
spring.shardingsphere.props.sql.show = true
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true

核心原理

解析引擎

解析过程分为词法解析语法解析,词法解析器用于将SQL拆解为不可再分的原子符号称为Token,并根据不同数据库方言所提供的字典,将其归类为关键字表达式字面量操作符,再使用语法解析器将SQL转换为Abstract Syntax Tree抽象语法树简称AST。

1
SELECT id, name FROM t_user WHERE status = 'ACTIVE' AND age > 18

SQL解析是整个分库分表的核心,其性能和兼容性是最重要的衡量指标,灰色表示需要进⼀步拆分,关键字的Token用绿色表示。

路由引擎

根据解析上下文匹配数据库和表的分片策略,生成路由路径,ShardingSphere分片策略主要分为单片路由即分片键操作符是等号多片路由即分片键的操作符是IN范围路由即分片键的操作符是Between不携带分片键的SQL则是广播路由

分片策略通常可由数据库内置也可由用户方配置内置分片策略大致可分为尾数取模哈希范围标签时间等,由用户方配置的分片策略则更加灵活,可根据使用方需求定制复合分片策略。实际使用时应尽量使用分片路由明确路由策略,因为广播路由影响过大不利于集群管理及扩展

  • 全库表路由不带分片键DQLDMLDDL语句会遍历所有库表逐一执行。如select * from courseselect * from course where ustatus='1'不带分片键
  • 全库路由:对数据库的操作都会遍历所有真实库,如set autocommit=0
  • 全实例路由DCL语句每个数据库实例只执行一次,如**CREATE USER customer@127.0.0.1 identified BY '123'**;
  • 单播路由从任意库中获取数据即可,如DESCRIBE course
  • 阻断路由屏蔽SQL对数据库的操作,如USE coursedb不会在真实库中执行,虚拟表操作不需要切换数据库

改写引擎

只需要面向逻辑库逻辑表来写SQL,最终由ShardigSphere改写引擎将SQL改写为在真实数据库中可正确执行的语句,SQL改写分为正确性改写优化改写

执行引擎

ShardingSphere并不是简单的将改写完的SQL提交到数据库执行,执行引擎的目标是自动化的平衡资源控制和执行效率连接模式分为内存限制模式MEMORY_STRICTLY连接限制模式CONNECTION_STRICTLY

  • 内存限制模式只关注一个数据库连接的处理数量,通常一张真实表一个数据库连接
  • 连接限制模式只关注数据库连接的数量,较大的查询会进行串行操作

这两个模式通过spring.shardingsphere.props.max.connections.size.per.query=50参数配置,默认值为1,参见源码ConfigurationPropertyKey类。ShardingSphere会根据路由到某一个数据源的路由结果计算出所有需在数据库上执行的SQL数量用该数量除以用户的配置项,得到每个数据库连接需执行的SQL数量。若数量>1选择连接限制模式数量<=1就会选择内存限制模式

  • 内存限制模式不限制连接数建立多个数据连接并发控制每个连接只去读取一个数据分片的数据,可最快把所有需要的数据读出来。且在归并阶段选择以每一条数据为单位进行归并流式归并,归并完一批数据后释放内存,可很好的提高数据归并的效率,且防止出现内存溢出垃圾回收频繁,吞吐量比较大,适合OLAP场景
  • 连接限制模式限制连接数至少有一个数据库连接会要去读取多个数据分片的数据,数据库连接采用串行方式依次读取多个数据分片的数据,将数据全部读到内存,进行统一数据归并即内存归并,归并效率会比较高适合OLTP场景

归并引擎

将从各个数据节点获取的多数据结果集,组合成为一个结果集并正确的返回至请求客户端,有流式归并内存归并两种归并方式:

  • 流式归并一条一条数据的方式进行归并
  • 内存归并将所有结果集都查询到内存中进行统一归并

分布式主键

内置生成器支持UUIDSNOWFLAKE,并抽离出分布式主键生成器的接口,方便用户自行实现自定义的自增主键生成器。

UUID

采用UUID.randomUUID()的方式产生唯一且不重复的分布式主键。最终生成一个字符串类型的主键。缺点是生成的主键无序。

SNOWFLAKE

雪花算法能保证不同进程主键不重复相同进程主键有序,二进制形式包含4部分,从高位到低位分表为:1bit符号位41bit时间戳位10bit工作进程位12bit序列号位毫秒数在高位自增序列在低位,整个ID趋势递增;不依赖第三方组件稳定性高,生成ID性能也非常高可根据自身业务特性分配bit非常灵活;但强依赖机器时钟,若机器上时钟回拨会导致发号重复

  • 1bit符号位:预留的符号位恒为零

  • 41bit时间戳位:41位时间戳可容纳毫秒数为2的41次幂,一年所使用的毫秒数为365 * 24 * 60 * 60 * 1000 Math.pow(2, 41) / (365 * 24 * 60 * 60 * 1000L) = 69.73年不重复

  • 10bit工作进程位:该标志Java进程内唯一,若分布式应用部署应保证每个工作进程的id不同,默认为0可通过属性设置

  • 12bit序列号位:该序列用来在同一个毫秒内生成不同的ID,若在该毫秒内生成数量超过4096即2的12次幂,则生成器会等待到下个毫秒继续生成。

SQL使用限制

支持的SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
SELECT * FROM tbl_name	
SELECT * FROM tbl_name WHERE (col1 = ? or col2 = ?) and col3 = ?
SELECT * FROM tbl_name WHERE col1 = ? ORDER BY col2 DESC LIMIT ?
SELECT COUNT(*), SUM(col1), MIN(col1), MAX(col1), AVG(col1) FROM tbl_name WHERE col1 = ?
SELECT COUNT(col1) FROM tbl_name WHERE col2 = ? GROUP BY col1 ORDER BY col3 DESC LIMIT ?, ?
INSERT INTO tbl_name (col1, col2,…) VALUES (?, ?, ….)
INSERT INTO tbl_name VALUES (?, ?,….)
INSERT INTO tbl_name (col1, col2, …) VALUES (?, ?, ….), (?, ?, ….)
-- INSERT表和SELECT表必须为相同表或绑定表
INSERT INTO tbl_name (col1, col2, …) SELECT col1, col2, … FROM tbl_name WHERE col3 = ?
-- REPLACE表和SELECT表必须为相同表或绑定表
REPLACE INTO tbl_name (col1, col2, …) SELECT col1, col2, … FROM tbl_name WHERE col3 = ?
UPDATE tbl_name SET col1 = ? WHERE col2 = ?
DELETE FROM tbl_name WHERE col1 = ?
CREATE TABLE tbl_name (col1 int, …)
ALTER TABLE tbl_name ADD col1 varchar(10)
DROP TABLE tbl_name
TRUNCATE TABLE tbl_name
CREATE INDEX idx_name ON tbl_name
DROP INDEX idx_name ON tbl_name
DROP INDEX idx_name
SELECT DISTINCT * FROM tbl_name WHERE col1 = ?
SELECT COUNT(DISTINCT col1) FROM tbl_name
SELECT subquery_alias.col1 FROM (select tbl_name.col1 from tbl_name where tbl_name.col2=?) subquery_alias

不支持的SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- VALUES语句不支持运算表达式
INSERT INTO tbl_name (col1, col2, …) VALUES(1+2, ?, …)
-- SELECT子句暂不支持使用*号简写及内置的分布式主键生成器
INSERT INTO tbl_name (col1, col2, …) SELECT * FROM tbl_name WHERE col3 = ?
-- SELECT子句暂不支持使用*号简写及内置的分布式主键生成器
REPLACE INTO tbl_name (col1, col2, …) SELECT * FROM tbl_name WHERE col3 = ?
-- UNION
SELECT * FROM tbl_name1 UNION SELECT * FROM tbl_name2
-- UNION ALL
SELECT * FROM tbl_name1 UNION ALL SELECT * FROM tbl_name2
-- 详见DISTINCT支持情况详细说明
SELECT SUM(DISTINCT col1), SUM(col1) FROM tbl_name
-- 会导致全路由
SELECT * FROM tbl_name WHERE to_date(create_time, 'yyyy-mm-dd') = ?
-- 暂不支持加括号的查询
(SELECT * FROM tbl_name)
-- 查询列是函数表达式时,查询列前不能使用表名,若查询表存在别名,则可使用表的别名
SELECT MAX(tbl_name.col1) FROM tbl_name

DISTINCT支持的SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT DISTINCT * FROM tbl_name WHERE col1 = ?
SELECT DISTINCT col1 FROM tbl_name
SELECT DISTINCT col1, col2, col3 FROM tbl_name
SELECT DISTINCT col1 FROM tbl_name ORDER BY col1
SELECT DISTINCT col1 FROM tbl_name ORDER BY col2
SELECT DISTINCT(col1) FROM tbl_name
SELECT AVG(DISTINCT col1) FROM tbl_name
SELECT SUM(DISTINCT col1) FROM tbl_name
SELECT COUNT(DISTINCT col1) FROM tbl_name
SELECT COUNT(DISTINCT col1) FROM tbl_name GROUP BY col1
SELECT COUNT(DISTINCT col1 + col2) FROM tbl_name
SELECT COUNT(DISTINCT col1), SUM(DISTINCT col1) FROM tbl_name
SELECT COUNT(DISTINCT col1), col1 FROM tbl_name GROUP BY col1
SELECT col1, COUNT(DISTINCT col1) FROM tbl_name GROUP BY col1

DISTINCT不支持的SQL

1
2
-- 查询列是函数表达式时,查询列前不能使用表名,若查询表存在别名,则可使用表的别名
SELECT SUM(DISTINCT tbl_name.col1), SUM(tbl_name.col1) FROM tbl_name