配景:之前的项目做读写分离的时候用的 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>
-
- <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>
-
- <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
如果觉得对你有帮助,请给我点一个star,非常感谢 !
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |