Docker配置SpringBoot+ShardingJDBC+MybatisPlus项目实现分库分表与读写分离
Docker配置SpringBoot+ShardingJDBC+MybatisPlus项目实现分库分表与读写分离
本文ShardingJDBC相关知识主要参考自ShardingJDBC官方文档,更多的用法建议到官网文档查看。
前言
传统的业务系统都是将数据集中存储至单一数据节点的解决方案,如今随着互联网数据的海量增长,单点存储早已无法满足性能、可用性和运维成本上的要求了,分库分表变成了目前数据库存储问题的主流解决方案。
目前最常见的MySQL分库分表选择主要为ShardingJDBC(基于JDBC驱动的轻量级Java框架)与MyCat(基于Proxy)。在这两个分库分表方案中个人更倾向于ShardingJDBC,主要原因如下:
- 官方文档详细优雅,入门容易
- 轻量级服务,搭建简便
- 相关产品与架构孵化演进都让人感到该开源项目未来可期
下图为ShardingSphere官网中的Sharding-JDBC+Sharding-Proxy架构图,通过混合使用Sharding-JDBC和Sharding-Proxy,并采用同一注册中心统一配置分片策略,灵活的搭建适用于各种场景的应用系统,更加自由的调整适合于当前业务的最佳系统架构。
项目环境
本文例子主要基于Spring Boot+Mybatis plus+ShardingJDBC框架集成搭建项目,通过Docker配置运行Mysql主从容器,Spring Boot项目运行连接Docker Mysql容器进行实现完整的分库分表与读写分离,主要的搭建流程如下:
- Docker与Docker Compose安装
- Docker Compose启动容器
- Mysql从机容器同步主机配置
- 启动Spring Boot项目
由于该项目主要是为了记录与展现分库分表与读写分离的实现搭建细节,所以Mysql的主从配置也是以双Master同时各配单Slave最低需求搭建的,具体架构图如(project-architecture)下:
搭建流程
数据表order_info_x
1
|
create table order_info
|
该项目中实际的操作数据表为order_info_x,order_info表仅用于代码文件逆向生成。当生成的order.id%2==0时,则存到Master1数据库上,若order.id%==1时,则存到Master2数据库上;当确认存到哪台机器上时,再根据user_id%2判断是存到order_info_0还是order_info_1。
每当Master有数据库创建或数据更新时,Slave会通过日志将主机数据库信息同步。
项目搭建
-
项目文件树
-
Maven依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71<properties>
<shardingsphere.version>4.0.0</shardingsphere.version>
</properties>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 个人参数校验框架,可忽略 -->
<dependency>
<groupId>io.github.wilson-he</groupId>
<artifactId>spring-boot-common</artifactId>
<version>0.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>${shardingsphere.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 个人开源mybatis-plus代码生成器插件 -->
<plugin>
<groupId>io.github.wilson-he</groupId>
<artifactId>generator-maven-plugin</artifactId>
<version>0.2.4</version>
<configuration>
<basePackage>io.wilson.sharding</basePackage>
<dataSource>
<url>jdbc:mysql://localhost/order_1</url>
<username>root</username>
<password>tiger</password>
</dataSource>
<templates>
<excludeController>false</excludeController>
</templates>
<crudIdType>Long</crudIdType>
<!-- 只生成表order_info的实体类 -->
<inclusions>order_info</inclusions>
</configuration>
</plugin>
</plugins>
</build>-
Mybatis Plus语句打印优化配置:ExtMybatisXMLLanguageDriver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35/**
* 解决Mybatis Plus原生Mapper语句的格式问题
*
* @author Wilson
* @since 2020/1/13
**/
public class ExtMybatisXMLLanguageDriver extends MybatisXMLLanguageDriver {
/**
* 缓存反射字段,减少反射带来的性能损耗
*/
private final Field sqlField;
public ExtMybatisXMLLanguageDriver() throws NoSuchFieldException {
Class boundSqlClass = BoundSql.class;
sqlField = boundSqlClass.getDeclaredField("sql");
try {
sqlField.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
该配置类主要用于优化在使用Sharding JDBC时Mybatis Plus的语句格式问题,下图为去除当前类配置后ShardingJDBC打印MybtisPlus原生Mapper语句的格式问题图:
-
OrderController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 -
主程序ShardingApplication.java
1
2
3
4
5
6
7 -
配置文件application-sharding.yml,分片规则根据以上project-architecture图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65spring:
shardingsphere:
# 根据个人实际配置,该实例中配置的是个人的ecs ip
host: localhost
datasource:
ds0:
username: root
password: tiger
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://${spring.shardingsphere.host}:3306/order_0
ds0-slave:
username: root
password: tiger
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://${spring.shardingsphere.host}:3307/order_0
ds1:
username: root
password: tiger
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://${spring.shardingsphere.host}:4306/order_0
ds1-slave:
username: root
password: tiger
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://${spring.shardingsphere.host}:4306/order_0
# 配置数据源名称
names: ds0,ds0-slave,ds1,ds1-slave
sharding:
default-database-strategy:
inline:
# 根据id%2取模判断当前数据存取是在ds0还是ds1对应的数据库
algorithm-expression: ds$->{id % 2}
# 根据user_id分片存取数据
sharding-column: id
tables:
order_info:
# 根据sharding生成的id%2是0还是1分配到数据库_0还是_1
actual-data-nodes: ds$->{0..1}.order_info_$->{0..1}
# 雪花算法生成id
keyGenerator:
type: SNOWFLAKE
column: id
# 根据user_id%2取模确定分配到数据表_0还是_1
table-strategy:
inline:
# 根据user_id%2取模判断当前数据存取是在表order_info_0还是order_info_1
algorithm-expression: order_info_$->{user_id % 2}
# 根据user_id分片存取数据
sharding-column: user_id
# 多主从配置
master-slave-rules:
ds0:
masterDataSourceName: ds0
slave-data-source-names: [ds0-slave]
ds1:
masterDataSourceName: ds1
slaveDataSourceNames: [ds1-slave]
# 单主从配置
# masterslave:
# name: ds0_ms
# masterDataSourceName: ds0
# slaveDataSourceNames: [ds0-slave]
props:
# 打印分片信息与SQL语句
sql.show: true -
配置文件application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14spring:
common:
validation:
msg-locale: zh_CN
serialization:
longToString: true
profiles:
include: sharding
swagger:
docket:
base-package: io.wilson.sharding.controller
mybatis-plus:
configuration:
default-scripting-language: io.wilson.sharding.mybatis.ExtMybatisXMLLanguageDriver
-
Docker容器配置
该例子中为了简便Docker只用于部署Mysql集群,若想更全面的使用Docker将Mysql与SpringBoot项目整合可参考 Docker+Jenkins+Nginx+SpringBoot自动化部署项目,然后在docker-compose.yml中将Spring Boot容器depends_on Mysql容器。
- 配置文件
docker-compose.yml
1
|
version: "3.7"
|
在docker-compose.yml目录下执行docker-compose up -d
指令,查看mysql master容器mysql binary log文件(File)与位置(Position):
1
|
docker exec -it mysql-master1 /bin/bash
|
slave容器mysql master连接配置并进行同步:
1
|
docker exec -it mysql-master1-slave1 /bin/bash
|
Slave1根据Master1配置binlog,Slave2根据Master2配置binlog,更具体的主从同步可参考Docker搭建基于binlog的Mysql主从同步,此处便省略一些配置文件讲解了,更详细的项目信息可到文末查看项目地址,当完成配置后即可启动本地Spring Boot项目,可以看到2主2从都已被初始化。
运行测试
-
插入数据根据id与user_id是否成功写入到相应的库与数据表,并查看从库数据是否同步
插入数据的id%2=0,user_id%2=1,根据前面定义的规则会放入master1的order_0.order_info_0表,查看控制台与master-slave1数据库数据校验数据是否正确、数据是否同步,如下图:
id与user_id都为基数时,根据规则数据放入master2的order_1.order_info_1,以下为测试数据与校验图: -
读分离测试
1中已经成功验证了只会写Master库并从库通过binlog同步数据,读分离验证则只需获取OrderInfo列表(OrderController.list接口)查看JDBC从哪查即可:
从上图可以看出ShardingJDBC已成功从所有从库中获取数据