sharding-jdbc 兼容 MybatisPlus动态数据源的配置方法
作者:房东的Tom
背景:之前的项目做读写分离的时候用的 MybatisPlus的动态数据做的,很多地方使用的@DS直接指定的读库或者写库实现的业务;随着表数据量越来越大,现在打算把比较大的表进行水平拆分,准备使用 ShardingJDBC实现,但是发现两者配合起来并不是那么顺利,网上大部分文章都是直接把整个Sharding的数据源当成MybatisPlus的一个数据源,那么在原本@DS上面指定的数据源就无法直接使用Sharding的分库等逻辑,所以我研究了一下源码,实现了这一逻辑,给后面有需要的朋友提供一个案例,避免浪费不必要的时间
一. 版本选择
目前ShardingJDBC主要有两个版本,一个是ShardingJDBC早期版本,一个是ShardingSphere项目中的ShardingSphere-JDBC
- Sharding-JDBC:Sharding-JDBC 最初由当时的项目发起人在2016年发布。它最早作为一个轻量级的 JDBC 层解决方案,旨在解决数据库分片和读写分离的问题。
- ShardingSphere:ShardingSphere 项目是由 Sharding-JDBC 项目发展而来的,并在2018年正式发布。Apache ShardingSphere 致力于构建更为完整的分布式数据库管理生态系统,包含了 Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar等多个组件。
目前独立的ShardingJDBC已经停更,使用到的最多的版本是 4.1.1
<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>4.1.1</version> </dependency>
ShardingSphere项目目前一直处于更新迭代中,ShardingSphere-JDBC 是通过ShardingJDBC 更新迭代过来的,在原有代码的基础进行了一些优化和新功能加入,对于开发者而言,主要是参数的配置发生了一些调整。但是参数的作用和配置方式和以前一样;
这里我为了方便以后会使用到新特性,我直接使用的是 ShardingSphere-JDBC 5.2.1
官方帮助文档:https://www.bookstack.cn/read/shardingsphere-5.1.0-zh/ecf18b21ab3f559c.md
<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId> <version>5.2.1</version> </dependency>
二. 项目依赖
案例全部的 Maven依赖如下:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>3.4.5</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.0</version> </dependency> <!-- 读写分离 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!--Shardingjdbc--> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId> <version>5.2.1</version> <exclusions> <exclusion> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> </exclusion> </exclusions> </dependency> <!-- 添加正确版本的 SnakeYAML shardingsphere-jdbc里面的依赖版本有问题,会报错--> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.33</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
三. 参数配置
application.yml 配置
server: port: 8080 mybatis-plus: mapper-locations: classpath*:mybatis/*.xml type-aliases-package: com.game.sharding.dto configuration: map-underscore-to-camel-case: false log-impl: org.apache.ibatis.logging.stdout.StdOutImpl spring: application: name: sharding-jdbc-test sharding-sphere: datasource: names: master,write,read,read2 master: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai username: root password: 123456 write: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai username: root password: 123456 read: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev_read?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai username: root password: 123456 read2: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev_read?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai username: root password: 123456 rules: sharding: tables: team_msg: ## 这里的customer-ds是下面配置的读写分离的数据源名称 actual-data-nodes: customer-ds.team_msg_${0..1} table-strategy: standard: sharding-column: id sharding-algorithm-name: msg-id # 对应下面的sharding-algorithms sharding-algorithms: ## 注意这里名称(例如msg-id)不能用下划线,会加载不了下面的参数导致启动报错 msg-id: type: INLINE props: ## 使用id取模算法 algorithm-expression: team_msg_${id % 2} ## 读写分离相关 readwrite-splitting: data-sources: customer-ds: load-balancer-name: customer-lb static-strategy: write-data-source-name: master read-data-source-names: read,read2,write load-balancers: customer-lb: ## 使用自定义的复杂均衡算法 type: CUSTOM props: # 显示处理之后的真实sql sql-show: true
四. 代码配置
最关键的配置就是需要把MybatisPlus的数据源注册为使用 shardingsphere-jdbc 的数据源,并且保证数据源的名称和原来MybatisPlus的数据源一致,shardingSphereDataSource里面其实有一个Map保存了application.yml中所有配置的数据源,这里主要是为了方便后续使用@DS做动态数据源切换,所以把同一个ShardingSphere的数据库注册为4个动态数据源,避免使用@DS找不到对应的数据源;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider; import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; import org.apache.commons.lang3.StringUtils; import org.apache.shardingsphere.driver.jdbc.adapter.AbstractDataSourceAdapter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; import javax.annotation.Resource; import javax.sql.DataSource; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @Configuration @AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class}) public class MyDataSourceConfiguration { /** * mybatisplus 动态数据源配置项 */ @Autowired private DynamicDataSourceProperties properties; /** * shardingjdbc的数据源 */ @Lazy @Resource(name = "shardingSphereDataSource") private AbstractDataSourceAdapter shardingSphereDataSource; @Value("${spring.sharding-sphere.datasource.names}") private String shardingDataSourceNames; /** * 注册动态数据源 这里非常关键,因为我们需要用到@DS注解配置动态选择数据源,同上又要让选择的数据源使用shardingjdbc的数据源 * 所以,这里需要动态的把所有的数据源都注册为 shardingjdbc的数据源 */ @Bean public DynamicDataSourceProvider dynamicDataSourceProvider() { if (StringUtils.isBlank(shardingDataSourceNames)) { throw new RuntimeException("配置 spring.sharding-sphere.datasource.names 不能为空"); } String[] names = shardingDataSourceNames.split(","); return new AbstractDataSourceProvider() { @Override public Map<String, DataSource> loadDataSources() { Map<String, DataSource> dataSourceMap = new HashMap<>(); Arrays.stream(names).forEach(name -> dataSourceMap.put(name, shardingSphereDataSource)); return dataSourceMap; } }; } /** * 将动态数据源设置为首选数据源 */ @Primary @Bean public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) { DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource(); dataSource.setPrimary(properties.getPrimary()); dataSource.setStrict(properties.getStrict()); dataSource.setStrategy(properties.getStrategy()); dataSource.setProvider(dynamicDataSourceProvider); dataSource.setP6spy(properties.getP6spy()); dataSource.setSeata(properties.getSeata()); return dataSource; } }
五. 自定义ShardingSphere中的复杂均衡算法
shardingsphere中的负载均衡需要实现ReadQueryLoadBalanceAlgorithm接口并在getType方法中返回自定义的算法名称,官方自带的又RoundRobinReadQueryLoadBalanceAlgorithm,RandomReadQueryLoadBalanceAlgorithm等,这里我们必须自定义算法才能兼容@DS注解实现自由切换数据源;
ShardingSphere使用的是SPI机制加载的,对应的加载源码部分如下:
所以如果我们要让自定义的ReadQueryLoadBalanceAlgorithm类生效,需要在项目中的 META-INF的services文件夹中创建org.apache.shardingsphere.readwritesplitting.spi.ReadQueryLoadBalanceAlgorithm 文件,并且把自定义的类填入该文件中
源码中的配置如下:
那么我们按照源码的配置直接在自己的项目中创建即可
最后自定义的CustomLoadBalanceAlgorithm 实现
public class CustomLoadBalanceAlgorithm implements ReadQueryLoadBalanceAlgorithm { private Properties props; public CustomLoadBalanceAlgorithm() { } @Override public void init(Properties props) { this.props = props; } /** * 获取数据源 * * @param name 数据源名称(ShardingJDBC使用的) * @param writeDataSourceName 写数据源名称 * @param readDataSourceNames 所有配置的复杂均衡中读数据源名称 * @param context 事务上下文对象,可以获取context.isInTransaction() 判断是否需要事务,可通过这个来判断是否使用 写数据源 * @return java.lang.String */ @Override public String getDataSource(String name, String writeDataSourceName, List<String> readDataSourceNames, TransactionConnectionContext context) { // 获取当前MybatisPlus指定的数据源 String dsKey = DynamicDataSourceContextHolder.peek(); if (StringUtils.isNotBlank(dsKey)) { if (writeDataSourceName.equals(dsKey)) { return dsKey; } if (readDataSourceNames.contains(dsKey)) { return dsKey; } throw new RuntimeException("@DS 配置错误,当前数据源[" + dsKey + "]不在SharingJDBC数据源列表[" + readDataSourceNames + "]中"); } return writeDataSourceName; } @Override public String getType() { return "CUSTOM"; } @Override public boolean isDefault() { return true; } @Override @Generated public Properties getProps() { return this.props; } }
那么此时你的ShardingSphere就已经完全适配之前MybatisPlus动态数据源了
六. 源码
Gitee: https://gitee.com/luowenjie98/sharing-sphere-mybatisplus-demo
到此这篇关于sharding-jdbc 兼容 MybatisPlus的动态数据源的文章就介绍到这了,更多相关sharding-jdbc MybatisPlus的动态数据源内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!