一、前言
通过以下系列章节:
Spring Boot集成ShardingSphere实现数据分片(一) | Spring Cloud 40
Spring Boot集成ShardingSphere实现数据分片(二) | Spring Cloud 41
Spring Boot集成ShardingSphere实现数据分片(三) | Spring Cloud 42
Spring Boot集成ShardingSphere实现读写分离 | Spring Cloud 43
Spring Boot集成ShardingSphere实现按月数据分片及创建自定义分片算法 | Spring Cloud 44
Spring Boot集成ShardingSphere分片利器 AutoTable (一)—— 简单体验 | Spring Cloud 45
Spring Boot集成ShardingSphere分片利器 AutoTable (二)—— 自动分片算法示例 | Spring Cloud 46
ShardingSphere 5.3 系列Spring 配置升级指南 | Spring Cloud 47
Spring Boot集成ShardingSphere实现数据加密及数据脱敏 | Spring Cloud 48
对ShardingSphere的数据分片、各分片算法应用、读写分离、数据加密、数据脱敏、最新版本升级等情况有了详细的了解,今天我们继续对其与dynamic-datasource集成实现多数据源进行演示学习。
本章节中会应用以上系列章节的部分示例,shardingsphere-jdbc-core版本为5.3.x版本
阅读本章前建议先阅读:ShardingSphere 5.3 系列Spring 配置升级指南 | Spring Cloud 47
二、背景
可能存在以下场景:原项目中使用的是dynamic-datasource进行数据源管理,由于项目经历一段时间的线上运行,数据库压力越来越大,计划使用shardingsphere-jdbc进行数据分片或读写分离。
2.1 dynamic-datasource介绍
dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。
dynamic-datasource官方文档:https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
2.1.1 特征
- 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
- 支持数据库敏感配置信息加密 ENC()。
- 支持每个数据库独立初始化表结构schema和数据库database。
- 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。
- 支持 自定义注解 ,需继承DS(3.2.0+)。
- 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。
- 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。
- 提供 自定义数据源来源 方案(如全从数据库加载)。
- 提供项目启动后 动态增加移除数据源 方案。
- 提供Mybatis环境下的 纯读写分离 方案。
- 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
- 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
- 提供 基于seata的分布式事务 方案。
- 提供 本地多数据源事务 方案。 附:不能和原生spring事务混用。
2.1.2 约定
- 框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD。
- 配置文件所有以下划线 _ 分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。
- 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。
- 默认的数据源名称为 master ,你可以通过 spring.datasource.dynamic.primary 修改。
- 方法上的注解优先于类上注解。
- DS支持继承抽象类上的DS,暂不支持继承接口上的DS。
三、使用示例
示例采用springboot集成shardingsphere-jdbc方式搭建。
3.1 项目总体结构
3.2 Maven依赖
shading-sphere/shading-dynamic-datasource/pom.xml:
shading-sphere com.gm 0.0.1-SNAPSHOT 4.0.0 shading-dynamic-datasource 8 8 UTF-8 com.mysql mysql-connector-j org.springframework.boot spring-boot-starter-web org.apache.shardingsphere shardingsphere-jdbc-core 5.3.2 org.mybatis mybatis-typehandlers-jsr310 1.0.2 com.baomidou mybatis-plus-boot-starter 3.5.3.1 org.yaml snakeyaml 1.33 com.baomidou dynamic-datasource-spring-boot-starter 3.6.1
- shardingsphere-jdbc-core使用最新的5.3.2版本
- dynamic-datasource-spring-boot-starter使用最新的3.6.1版本
- mybatis-plus-boot-starter使用最新的3.5.3.1版本
3.3 配置文件
src/main/resources/application.yml:
server: port: 8844spring: application: name: @artifactId@ datasource: dynamic: primary: master_1 # 设置默认的数据源或者数据源组,默认值即为master strict: false # 严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 datasource: master_1: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.0.35:3306/seata-demo-consumer?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai username: root password: '1qaz@WSX' master_2: driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver url: jdbc:shardingsphere:classpath:shading-auto-tables-algorithm.yamlmybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
配置简要说明:
dynamic-datasource多数据源配置,其中数据源master_2基于 ShardingSphereDriver 配置项加载shardingsphere数据源及分片规则
src/main/resources/shading-auto-tables-algorithm.yaml:
dataSources: ds1: dataSourceClassName: com.zaxxer.hikari.HikariDataSource driverClassName: com.mysql.cj.jdbc.Driver jdbcUrl: jdbc:mysql://192.168.0.35:3306/db1?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai username: root password: '1qaz@WSX' connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 minPoolSize: 1 ds2: dataSourceClassName: com.zaxxer.hikari.HikariDataSource driverClassName: com.mysql.cj.jdbc.Driver jdbcUrl: jdbc:mysql://192.168.0.46:3306/db2?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai username: root password: '1qaz@WSX' connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 minPoolSize: 1rules: - !SHARDING autoTables: # 取模 t_auto_order_mod: actualDataSources: ds$->{1..2} shardingStrategy: standard: shardingColumn: order_id shardingAlgorithmName: auto_order_mod # 分布式序列策略 keyGenerateStrategy: # 自增列名称,缺省表示不使用自增主键生成器 column: order_id # 分布式序列算法名称 keyGeneratorName: snowflake # 分片算法配置 shardingAlgorithms: # 取模 auto_order_mod: type: MOD props: sharding-count: 6 # 分布式序列算法配置(如果是自动生成的,在插入数据的sql中就不要传id,null也不行,直接插入字段中就不要有主键的字段) keyGenerators: # 分布式序列算法名称 snowflake: # 分布式序列算法类型 type: SNOWFLAKEprops: sql-show: true
配置简要说明:
逻辑表 t_auto_order_mod按照order_id分片键使用MOD自动分片算法进行分片
3.4 实体类
com/gm/shading/dynamic/datasource/entity/AutoOrderMod.java:
import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import lombok.Data;import java.math.BigDecimal;@Data@TableName("t_auto_order_mod")public class AutoOrderMod { @TableId(type = IdType.ASSIGN_ID) private Long orderId; private BigDecimal price; private Long userId; private String status;}
3.5 Mapper
com/gm/shading/dynamic/datasource/mapper/AutoOrderModMapper.java:
import com.baomidou.dynamic.datasource.annotation.DS;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.gm.shading.dynamic.datasource.entity.AutoOrderMod;import org.apache.ibatis.annotations.Mapper;@DS("master_2")@Mapperpublic interface AutoOrderModMapper extends BaseMapper{ void save(AutoOrderMod autoOrder);}
通过注解@DS方式制定使用数据源master_2
src/main/resources/mapper/AutoOrderModMapper.xml:
insert into t_auto_order_mod (price, user_id, status) values (#{price}, #{userId}, #{status})
3.6 启动类
com/gm/shading/dynamic/datasource/ShadingDynamicDatasourceApplication.java:
import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan("com.gm.shading.dynamic.datasource.mapper")public class ShadingDynamicDatasourceApplication { public static void main(String[] args) { SpringApplication.run(ShadingDynamicDatasourceApplication.class, args); }}
3.7 单元测试
src/test/java/com/gm/shading/dynamic/datasource/ShadingDynamicDatasourceApplicationTests.java:
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.gm.shading.dynamic.datasource.entity.AutoOrderMod;import com.gm.shading.dynamic.datasource.mapper.AutoOrderModMapper;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.jdbc.core.JdbcTemplate;import java.math.BigDecimal;import java.util.List;import java.util.Random;@SpringBootTest@Slf4jpublic class ShadingDynamicDatasourceApplicationTests { @Autowired AutoOrderModMapper autoOrderModMapper; @Autowired JdbcTemplate jdbcTemplate; @Test public void testCreateAutoOrderMod() { jdbcTemplate.execute("CREATE TABLE `t_auto_order_mod` (n" + " `order_id` bigint(20) NOT NULL COMMENT '订单id',n" + " `price` decimal(10,2) NOT NULL COMMENT '订单价格',n" + " `user_id` bigint(20) NOT NULL COMMENT '下单用户id',n" + " `status` varchar(50) NOT NULL COMMENT '订单状态',n" + " PRIMARY KEY (`order_id`) USING BTREEn" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;"); } @Test public void testInsertAutoOrderMod() { Random random = new Random(); for (int i = 1; i < 10; i++) { AutoOrderMod order = new AutoOrderMod(); order.setPrice(new BigDecimal(i)); order.setUserId(Integer.valueOf(random.nextInt(25)).longValue()); order.setStatus(i + ""); autoOrderModMapper.save(order); } } @Test public void testSelectAutoOrderMod() { Listlist = autoOrderModMapper.selectList(new QueryWrapper ()); for (AutoOrderMod autoordermod : list) { log.info("{}", autoordermod); } }}
- 执行testCreateAutoOrderMod方法根据自动配置的分片规则进行建表
- 执行testInsertAutoOrderMod方法完成数据插入
- 执行testSelectAutoOrderMod方法完成数据查询