sharding-jdbc使用
一、说明
这里有两个分支的包,主要介绍如下:
在2016年开始、开源,当时它的名字还是Sharding JDBC。2017年发布了2.0版本,主打数据库治理功能;2018年正式更名为Sharding Sphere,而Sharding JDBC则演变为生态圈中的一部分,并且在同年开发了具有独立形态的Proxy代理,当年10月份通过Apache的投票,正式成为Apache孵化项目,也再次更名为Apache Sharding Sphere。大概一年半时间左右,成为Apache顶级项目,到了2021年迎来了5.0版本,从而开启一个新的机缘,提出了Databace Plus理念,是指构建数据库的上层标准。
注意:sharding jdbc支持读写分离,可实现一主多从,但不支持多主多从
参考官方文档的FAQ:https://shardingsphere.apache.org/document/current/cn/faq/
在3.x和4.x版本当中,如果只有部分数据库分库分表,需要将不分库分表的表也配置在分片规则
因为ShardingSphere是将多个数据源合并为一个统一的逻辑数据源。因此即使不分库分表的部分,不配置分片规则ShardingSphere即无法精确的断定应该路由至哪个数据源。 但是ShardingSphere提供了两种变通的方式,有助于简化配置。
- 配置default-data-source,凡是在默认数据源中的表可以无需配置在分片规则中,ShardingSphere将在找不到分片数据源的情况下将表路由至默认数据源。
- 将不参与分库分表的数据源独立于ShardingSphere之外,在应用中使用多个数据源分别处理分片和不分片的情况。
在shardingjdbc5.x版本的官方文档FAQ中,关于不参与分库分表的说法,则是不需要配置,框架会自动识别,这样当然就简化了。
二、4.1.1版本
2.1 pom
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
<!-- 对于springboot2.6及以上的版本需要加 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
2.2 配置文件
spring:
application:
name: sharding
shardingsphere:
# 是否打印sql
props:
sql:
show: true
datasource:
# 有几个库
names: db1,db2
# 库1 的配置
db1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/study_1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
# 库2 的配置
db2:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/study_2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
sharding:
# 默认的库,其它数据可能会用到
default-data-source-name: db1
# 绑定的表 不配置也没找出啥问题
# binding-tables: user
# 配置表的分片规则
tables:
# 指定某个表的分片配置,这里是指的表名
user:
# 这个配置是告诉sharding有多少个库和多少个表
actual-data-nodes: db$->{1..2}.user_$->{1..2}
#分库策略
database-strategy:
# 行表达式模式
inline:
# 选择需要分库的字段,根据那个字段进行区分
sharding-column: sex
# 表达式,分库的算法,这个是通过年龄取模然后决定落到哪个库
algorithm-expression: db$->{sex % 2 + 1}
# 主键生成策略(如果是自动生成的,在插入数据的sql中就不要传id,null也不行,直接插入字段中就不要有主键的字段),如果是自定义的id,不需要自动生成,下面的配置不需要进行
key-generator:
# 对应的数据库表的主键
column: id
# 生成方式, 雪花模式
type: SNOWFLAKE
# 配置表分片策略
table-strategy:
# 行表达式
inline:
# 配置表分片的字段
sharding-column: id
# 配置表分片算法
algorithm-expression: user_$->{id % 2 + 1 }
props:
# 日志显示具体的SQL
sql:
show: true
2.3 配置类
springboot2.3健康检查配置:
@Configuration
public class DataSourceHealthConfig extends DataSourceHealthContributorAutoConfiguration {
@Value("${spring.datasource.dbcp2.validation-query:select 1}")
private String defaultQuery;
public DataSourceHealthConfig(Map<String,DataSource> dataSources,ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders){
super(dataSources,metadataProviders);
}
@Override
protected AbstractHealthIndicator createIndicator(DataSource source){
DataSourceHealthIndicator indicator = (DataSourceHealthIndicator)super.createIndicator(source);
if (!StringUtils.hasText(indicator.getQuery())) {
indicator.setQuery(defaultQuery);
}
return indicator;
}
}
如果是mybayisplus比较低版本,不兼容LocalDate、LocalDateTime等jdk8日期,需要手动引入,否则报错。以LocalDateTime为例子:
public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {
private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public void setNonNullParameter(PreparedStatement ps,int i,LocalDateTime parameter,JdbcType jdbcType)
throws SQLException{
if (parameter != null) {
ps.setString(i,dateTimeFormatter.format(parameter));
}
}
@Override
public LocalDateTime getNullableResult(ResultSet rs,String columnName) throws SQLException{
String target = rs.getString(columnName);
if (Tools.isEmpty(target)) {
return null;
}
return LocalDateTime.parse(target,dateTimeFormatter);
}
@Override
public LocalDateTime getNullableResult(ResultSet rs,int columnIndex) throws SQLException{
String target = rs.getString(columnIndex);
if (Tools.isEmpty(target)) {
return null;
}
return LocalDateTime.parse(target,dateTimeFormatter);
}
@Override
public LocalDateTime getNullableResult(CallableStatement cs,int columnIndex) throws SQLException{
String target = cs.getString(columnIndex);
if (Tools.isEmpty(target)) {
return null;
}
return LocalDateTime.parse(target,dateTimeFormatter);
}
}
mybatisplus配置
@Configuration
public class MybatisPlusConfiguration {
public MybatisPlusConfiguration(MybatisPlusProperties mybatisPlusProperties){
MybatisConfiguration configuration = mybatisPlusProperties.getConfiguration();
GlobalConfig globalConfig = mybatisPlusProperties.getGlobalConfig();
GlobalConfigUtils.setGlobalConfig(configuration,globalConfig);
configuration.getTypeHandlerRegistry().register(LocalDateTime.class,new LocalDateTimeTypeHandler());
configuration.getTypeHandlerRegistry().register(LocalDate.class,new LocalDateTypeHandler());
configuration.getTypeHandlerRegistry().register(LocalTime.class,new LocalTimeTypeHandler());
GlobalConfigUtils.setGlobalConfig(configuration,globalConfig);
}
}
三、5.2.1版本
3.1 pom
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.1</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
<!-- 对于springboot2.3以上的版本需要加 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
3.2 配置文件
3.2.1 数据源配置
这里配置数据源,用于路由选择,注意配置names为名称,使用,隔开。下面对应的是数据源名称对应的配置
spring:
shardingsphere:
datasource:
names: master-0,master-1,slave-0,slave-1
master-0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/concurrency?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
username: root
password: root
master-1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/concurrency?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
username: root
password: root
slave-0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3308/concurrency?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
username: root
password: root
slave-1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3309/concurrency?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
username: root
password: root
3.2.2 分库分表配置
这里主要介绍算法名称对应算法:
sharding-algorithm-name对应自定义算法名称。
算法type:
- HASH_MOD 进行hash然后取模,后面必须跟sharding-count配置分片的大小
- inline:通过algorithm-expression配置,使用groovy表达式,如 master-$->{id % 2},这里的参数必须要long或者integer
rules:
sharding:
#默认数据源配置
default-database-strategy:
standard:
sharding-column: id
sharding-algorithm-name: default_inline
tables:
# 逻辑表的名称
concurrency:
# 数据节点配置,采用Groovy表达式
actual-data-nodes: master-$->{0..1}.concurrency_$->{0..5}
# 配置策略
table-strategy:
# 用于单分片键的标准分片场景
standard:
sharding-column: code
# 分片算法名字
sharding-algorithm-name: concurrency_inline
database-strategy:
# 用于单分片键的标准分片场景
standard:
sharding-column: code
# 分片算法名字
sharding-algorithm-name: database_inline
key-generate-strategy: # 主键生成策略
column: id # 主键列
key-generator-name: id-key # 策略算法名称(推荐使用雪花算法)
key-generators:
id-key:
type: SNOWFLAKE # 分布式序列算法类型
sharding-algorithms:
database_inline:
type: HASH_MOD
props:
sharding-count: 2
concurrency_inline:
type: HASH_MOD
props:
sharding-count: 6
default_inline:
type: inline
props:
algorithm-expression: master-0
props:
# 日志显示具体的SQL
sql-show: true
3.2.3 读写分离
因为sharding-jdbc只支持一主多从的读写分离,不支持多主的情况,所以下面的配置只拿单独的主从出来说
支持项
- 提供了一主多从的读写分离配置,可独立使用,也可配合分库分表使用。
- 同个调用线程,执行多条语句,其中一旦发现有非读操作,后续所有读操作均从主库读取。
- Spring命名空间。
- 基于Hint的强制主库路由。
不支持范围
- 主库和从库的数据同步。
- 主库和从库的数据同步延迟导致的数据不一致。
- 主库双写或多写。
#读写分离配置
readwrite-splitting:
data-sources:
ds: # 逻辑数据源名字 不要乱写名字,否则读写分离不生效
type: STATIC #静态类型,(动态Dynamic)
props:
# 主库
write-data-source-name: master-0
# 从库
read-data-source-names: slave-0
# 负载均衡算法名称
load-balancer-name: round
# 负载均衡算法
load-balancers:
round: # 负载均衡算法名称
type: ROUND_ROBIN #负载均衡算法类型轮询算法
四、异常解决
4.1 Compatible version of org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1
5.2 版本及以上会出现这个问题,因为需要的解析yaml文件的库版本不满足框架要求,pom文件中的 properties位置 调整yaml版本号即可(Springboot框架自带了这个框架,不需要重新引用)。
<properties>
<snakeyaml.version>1.33</snakeyaml.version>
</properties>
如果上述配置不生效,使用下面的整体jar包配置
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
4.2 Database type inconsistent with...
所有数据源必须是相同的数据库类型;要么全是MySQL,要么全是Postgresql;否则抛出异常:Database type inconsistent with 'org.apache.shardingsphere.infra.database.type.dialect.MySQLDatabaseType@3083e6ef' and 'org.apache.shardingsphere.infra.database.type.dialect.PostgreSQLDatabaseType@6d57b264'
4.3 Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required
druid 要使用spring 版本而不是spring boot 版本,否则启动会报错
该问题其实是dataSource数据源冲突问题,sharding-jdbc会创建数据源,而druid也会创建数据源,所以导致冲突