一、前言
通过以下系列章节:
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
:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>shading-sphere</artifactId>
<groupId>com.gm</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shading-dynamic-datasource</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.1</version>
</dependency>-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>5.3.2</version>
</dependency>
<!-- 解决Mybatis中LocalDateTime和SQL中datetime的交互 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-typehandlers-jsr310</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.6.1</version>
</dependency>
</dependencies>
</project>
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: 8844
spring:
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.yaml
mybatis:
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: 1
rules:
- !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: SNOWFLAKE
props:
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")
@Mapper
public interface AutoOrderModMapper extends BaseMapper<AutoOrderMod> {
void save(AutoOrderMod autoOrder);
}
通过注解
@DS
方式制定使用数据源master_2
src/main/resources/mapper/AutoOrderModMapper.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gm.shading.dynamic.datasource.mapper.AutoOrderModMapper">
<insert id="save" parameterType="com.gm.shading.dynamic.datasource.entity.AutoOrderMod">
insert into t_auto_order_mod (price, user_id, status)
values (#{price}, #{userId}, #{status})
</insert>
</mapper>
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
@Slf4j
public 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 BTREE\n" +
") 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() {
List<AutoOrderMod> list = autoOrderModMapper.selectList(new QueryWrapper<AutoOrderMod>());
for (AutoOrderMod autoordermod : list) {
log.info("{}", autoordermod);
}
}
}
- 执行
testCreateAutoOrderMod
方法根据自动配置的分片规则进行建表 - 执行
testInsertAutoOrderMod
方法完成数据插入 - 执行
testSelectAutoOrderMod
方法完成数据查询