Sharding-JDBC(数据分片)学习记录
一、sharding-jdbc是什么?
定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。
它使用客户端直连数据库,以 jar
包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC
和各种 ORM 框架。
-
适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC
Template 或直接使用 JDBC。 -
支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。
-
支持任意实现 JDBC 规范的数据库,目前支持
MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 标准的数据库。
二、sharding-jdbc的核心概念
-
逻辑表(LogicTable):进行水平拆分的时候同一类型(逻辑、数据结构相同)的表的总称。例:订单数据根据主键尾数拆分为10张表,分别是t_order_0到t_order_9,他们的逻辑表名为t_order。
-
真实表(ActualTable):在分片的数据库中真实存在的物理表。即上个示例中的t_order_0到t_order_9。
-
数据节点(DataNode):数据分片的最小单元。由数据源名称和数据表组成,例:ds_0.t_order_0。
-
动态表(DynamicTable):逻辑表和物理表不一定需要在配置规则中静态配置。如,按照日期分片的场景,物理表的名称随着时间的推移会产生变化。
-
绑定表(BindingTable):指分片规则一致的主表和子表。例如:t_order表和t_order_item表,均按照order_id分片,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。
-
分片键(ShardingColumn):分片字段用于将数据库(表)水平拆分的字段,支持单字段及多字段分片。例如上例中的order_id。
-
分片算法(ShardingAlgorithm):进行水平拆分时采用的算法,分片算法需要应用方开发者自行实现,可实现的灵活度非常高。
-
分片策略(ShardingStrategy):包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。
三、如何使用sharing-jdbc配置
1、引入maven依赖
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>${latest.release.version}</version>
</dependency>
注意: 请将${latest.release.version}更改为实际的版本号。
2、规则配置
Sharding-JDBC可以通过Java,YAML,Spring命名空间和Spring Boot
Starter四种方式配置,开发者可根据场景选择适合的配置方式。(此处我通过Spring
Boot进行配置)
3、创建数据源
# 配置真实数据源
spring.shardingsphere.datasource.names=ds0,ds1
(此处的数据源名称可以配置多个,ds0、ds1相当于给数据源起别名)
# 配置第 1 个数据源
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/test_sharding_jdbc_01\?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=*****
# 配置第 2 个数据源
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/test_sharding_jdbc_02\?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=*****
#配置默认数据库
spring.shardingsphere.sharding.default-data-source-name=ds0
四、分库案例
新建数据库test_sharding_jdbc_01、test_sharding_jdbc_02
两个库中建表tb_user0(结构一致)
1、精准分片(分库)
配置:
#由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持inline表达式。
spring.shardingsphere.sharding.tables.tb_user.actual-data-nodes=ds$->{0..1}.tb_user0
#分片列名称
spring.shardingsphere.sharding.tables.tb_user.database-strategy.standard.sharding-column=age
#精确分片算法类名称(需实现PreciseShardingAlgorithm类,提供具体的分片规则)
spring.shardingsphere.sharding.tables.tb_user.database-strategy.standard.precise-algorithm-class-name=org.quzb.sharding_jdbc.config.MyPreciseShardingAlgorithm
#自增列生成器类型,缺省表示不使用自增主键生成器(内置类型:SNOWFLAKE、UUID)
spring.shardingsphere.sharding.tables.tb_user.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.tb_user.key-generator.column=id
精准分片实现类:
public class MyPreciseShardingAlgorithm implements
PreciseShardingAlgorithm<Integer> {
@Override
public String doSharding(Collection<String> collection,
PreciseShardingValue<Integer> preciseShardingValue) {
//接收到的分片列"age"的值
Integer value = preciseShardingValue.getValue();
//collection:配置的数据库集合,分片规则(自行定义):将接收到的age%2返回对应的数据库名
return (String) collection.toArray()[value % collection.size()];
}
}
测试使用:
针对指定字段将数据插入不同的库中,说明分库分片成功
2、范围分片(分库)
配置:
spring.shardingsphere.sharding.tables.tb_user.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.tb_user.key-generator.column=id
# 范围分片
spring.shardingsphere.sharding.tables.tb_user.actual-data-nodes=ds$->{0..1}.tb_user0
spring.shardingsphere.sharding.tables.tb_user.database-strategy.standard.sharding-column=age
spring.shardingsphere.sharding.tables.tb_user.database-strategy.standard.range-algorithm-class-name=org.quzb.sharding_jdbc.config.MyRangeShardingAlgorithm
spring.shardingsphere.sharding.tables.tb_user.database-strategy.standard.precise-algorithm-class-name=org.quzb.sharding_jdbc.config.MyRangeShardingAlgorithm
范围分片实现类:
public class MyRangeShardingAlgorithm implements
PreciseShardingAlgorithm<Integer>, RangeShardingAlgorithm<Integer> {
@Override
public Collection<String> doSharding(Collection<String> collection,
RangeShardingValue<Integer> rangeShardingValue) {
Set<String> result= Sets.newHashSet();
Integer lowerEndpoint = rangeShardingValue.getValueRange().lowerEndpoint();
Integer upperEndpoint = rangeShardingValue.getValueRange().upperEndpoint();
for(int i=lowerEndpoint;i<upperEndpoint;i++){
result.add((String) collection.toArray()[i%2]);
}
return result;
}
@Override
public String doSharding(Collection<String> collection,
PreciseShardingValue<Integer> preciseShardingValue) {
Integer value = preciseShardingValue.getValue();
if (value>10){
return (String) collection.toArray()[0];
}
return (String) collection.toArray()[1];
}
}
测试结果:
3、复合算法分片(分库)
配置:
spring.shardingsphere.sharding.tables.tb_user.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.tb_user.key-generator.column=id
spring.shardingsphere.sharding.tables.tb_user.actual-data-nodes=ds$->{0..1}.tb_user0
#对user_year、age进行分片
spring.shardingsphere.sharding.tables.tb_user.database-strategy.complex.sharding-columns=user_year,age
spring.shardingsphere.sharding.tables.tb_user.database-strategy.complex.algorithm-class-name=org.quzb.sharding_jdbc.config.MartinComplexKeysShardingAlgorithm
实现类:
public class MartinComplexKeysShardingAlgorithm implements
ComplexKeysShardingAlgorithm<Integer> {
@Override
public Collection<String> doSharding(Collection<String> collection,
ComplexKeysShardingValue<Integer> complexKeysShardingValue) {
Map<String, Collection<Integer> columnNameAndShardingValuesMap =
complexKeysShardingValue.getColumnNameAndShardingValuesMap();
if
(columnNameAndShardingValuesMap.containsKey("age")&&columnNameAndShardingValuesMap.containsKey("user_year")){
Collection<Integer> ageList = columnNameAndShardingValuesMap.get("age");
Collection<Integer> yearList =columnNameAndShardingValuesMap.get("user_year");
Integer year = yearList.iterator().next();
Integer age = ageList.iterator().next();
int i =Objects.equals(age % collection.size(),0)?0:1;
if (Objects.equals(0,i)&&year>1998){
return Lists.newArrayList((String) collection.toArray()[0]);
}
return Lists.newArrayList((String) collection.toArray()[1]);
}
return collection;
}
}
测试结果:
五、分表示例
分表与分库类似
精准分片(分表)
配置:
#配置表名
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds0.t_order_$->{['us','uk']}
#配置分片字段
spring.shardingsphere.sharding.tables.t_order.table-strategy.standard.sharding-column=station
spring.shardingsphere.sharding.tables.t_order.table-strategy.standard.precise-algorithm-class-name=org.quzb.sharding_jdbc.config.OrderPreciseShardingAlgorithm
spring.shardingsphere.sharding.tables.t_order.key-generator.column=id
spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE
实现类:
public class OrderPreciseShardingAlgorithm implements
PreciseShardingAlgorithm<String> {
@Override
public String doSharding(Collection<String> collection,
PreciseShardingValue<String> preciseShardingValue) {
#value值为接收到的站点值
String value = preciseShardingValue.getValue();
String lowerCase = String.join("","t_order_",value.toLowerCase());
#collection为配置的表名集合
for (String coll:collection) {
if (Objects.equals(lowerCase, coll)) {
return coll;
}
}
return null;
}
}
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VRNWO501-1611389911606)(media/2faf7a33ab8423318c73e3dfebe0a5b9.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lukNPZVw-1611389911607)(media/c3daeb124851e08cfa98ac026649d148.png)]
rCase = String.join("",“t_order_”,value.toLowerCase());
#collection为配置的表名集合
for (String coll:collection) {
if (Objects.equals(lowerCase, coll)) {
return coll;
}
}
return null;
}
}
测试结果: