ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景。
分表执行的5个步骤:
1.Sql解析
分表的表达式:order_${0..1}
分为两个表:order_0,order_1
2.Sql路由
就是根据分表策略,找到具体要curd的表
根据解析上下文匹配数据库和表的分片策略,并生成路由路径。 对于携带分片键的SQL,根据分片键的不同可以划分为单片路由(分片键的操作符是等号)、多片路由(分片键的操作符是IN)和范围路由(分片键的操作符是BETWEEN)。 不携带分片键的SQL则采用广播路由。
3.Sql改写
将逻辑SQL改写为可以在真实数据库中正确执行的SQL。 它包括正确性改写和优化改写两部分。
4.Sql执行
ShardingSphere采用一套自动化的执行引擎,负责将路由和改写完成之后的真实SQL安全且高效发送到底层数据源执行。 它不是简单地将SQL通过JDBC直接发送至数据源执行;也并非直接将执行请求放入线程池去并发执行。它更关注平衡数据源连接创建以及内存占用所产生的消耗,以及最大限度地合理利用并发等问题。 执行引擎的目标是自动化的平衡资源控制与执行效率。
5.Sql归并
将从各个数据节点获取的多数据结果集,组合成为一个结果集并正确的返回至请求客户端,称为结果归并。
ShardingSphere支持的结果归并从功能上分为遍历、排序、分组、分页和聚合5种类型,它们是组合而非互斥的关系。 从结构划分,可分为流式归并、内存归并和装饰者归并。流式归并和内存归并是互斥的,装饰者归并可以在流式归并和内存归并之上做进一步的处理。
中文手册:shardingsphere中文手册
我们在项目中怎么用:
1.maven配置
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>3.0.0.M2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
2.properties配置
#分表,数据源由sharding配置,管理所有的表(包括不分片的表)
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=false
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
#分表所在的数据库
sharding.jdbc.datasource.names=dbName
sharding.jdbc.datasource.dbName.type=com.alibaba.druid.pool.DruidDataSource
sharding.jdbc.datasource.dbName.driver-class-name=com.mysql.jdbc.Driver
sharding.jdbc.datasource.dbName.url=jdbc:mysql://xxx:3306/dbName?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
sharding.jdbc.datasource.dbName.username=xx
sharding.jdbc.datasource.dbName.password=xx
#为tableNameA分表,需要事先在数据库建立对应的表,这里为:tableNameA_1,tableNameA_2,....,tableNameA_49,以及tableNameA
sharding.jdbc.config.sharding.tables.tableNameA.actual-data-nodes=dbName.tableNameA_$->{1..49},dbName.tableNameA
#分表字段
sharding.jdbc.config.sharding.tables.tableNameA.table-strategy.standard.sharding-column=id
#实现分表算法的类的名字
sharding.jdbc.config.sharding.tables.tableNameA.table-strategy.standard.precise-algorithm-class-name=com.device.bind.config.ShardingTableConfig
#为tableNameB分10张表,tableNameB_0...tableNameB_9这10张表
sharding.jdbc.config.sharding.tables.tableNameB.actual-data-nodes=dbName.tableNameB_$->{0..9}
sharding.jdbc.config.sharding.tables.tableNameB.table-strategy.standard.sharding-column=id
sharding.jdbc.config.sharding.tables.tableNameB.table-strategy.standard.precise-algorithm-class-name=com.device.bind.config.ShardingTableConfig
#Druid连接池配置
spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
spring.datasource.druid.max-wait=60000
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.druid.pool-prepared-statements=true
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20
spring.datasource.druid.validation-query=SELECT 1 FROM DUAL
spring.datasource.druid.validation-query-timeout=30000
#是否在获得连接后检测其可用性
spring.datasource.druid.test-on-borrow=false
#是否在连接放回连接池后检测其可用性
spring.datasource.druid.test-on-return=false
这里对tableNameA及tableNameB两张表进行分表,一个分了50张表,一个分了10张表,这些表都需要自己在数据库中建立。
分表的字段均为id,分表的算法写在ShardingTableConfig类中,该类实现了PreciseShardingAlgorithm<T extends Comparable<?>>接口,该接口又继承了ShardingAlgorithm类。从接口名字上我们可以看出,我们要实现的是精确分表算法。
注意:不分片的表(不进行分表的表)也会被ShardingSphere管理,可以通过配置两个数据源来讲分片的表与不分片的表分隔开。ShardingSphere3.0目前不支持distant查询,3.1支持。
3.实现自己的精确分表算法
/**
* 数据库分表
*/
public class ShardingTableConfig implements PreciseShardingAlgorithm<Long> {
//tableNameA表的分表条件,当id大于该值时,向tableNameA_1...tableNameA_49插入或查询数据
private static long tableNameATableThreshold = 85000;
//tableNameA表 分为50张表
private static int tableNameASplitTableSize = 50;
//tableNameB表 分为10张表
private static int tableNameBSplitTableSize = 10;
/**
* 精确分片算法 用于=和IN
* @param availableTargetNames
* @param shardingValue
* @return
*/
@Override
public String doSharding(Collection availableTargetNames, PreciseShardingValue shardingValue) {
System.out.println(availableTargetNames);
String tableNameA = "tableNameA";
if (availableTargetNames.contains(tableNameA)) {
if (shardingValue.getColumnName().equalsIgnoreCase("id")) {
int suffix = -1;
Long id = (Long) shardingValue.getValue();
if (id < tableNameATableThreshold ) {
return tableNameA;
} else {
suffix = (int) (id % tableNameASplitTableSize );
String s = (tableNameA + "_" + suffix).toLowerCase();
return s;
}
}
throw new IllegalArgumentException("分表字段:member_id缺失");
}
String tableNameB = "tableNameB ";
if (availableTargetNames.contains(tableNameB )) {
if (shardingValue.getColumnName().equalsIgnoreCase("id")) {
int suffix = -1;
Long id = (Long) shardingValue.getValue();
suffix = (int) (id % tableNameBSplitTableSize );
String s = ("tableNameB " + "_" + suffix).toLowerCase();
return s;
}
throw new IllegalArgumentException("分表字段:member_id缺失");
}
return null;
}
}