Spring|Spring Boot demo系列(十二)(ShardingSphere + MyBatisPlus 读写分离 + 主从复制)
1 概述
本文讲述了如何使用MyBatisPlus
+ShardingSphere
进行读写分离,以及利用MySQL
进行一主一从的主从复制。
具体步骤包括:
MySQL
主从复制环境准备(Docker
)- 搭建
ShardingShpere
+MyBatisPlus
+Druid
环境 - 测试
OpenJDK 11.0.11
Spring Boot 2.5.1
MyBatis Plus 3.4.3.1
MyBatis Plus Generator 3.5.0
Druid 1.2.6
ShardingSphere 4.1.1
MySQL 8.0.25
- 写操作在主数据库进行
- 读操作在从数据库进行
MySQL
上实现,相信会不如一台MySQL
写,另外两台MySQL
读这样的配置性能高。另一方面,在很多时候都是读操作的请求要远远高于写操作,这样就显得读写分离非常有必要了。3.2 主从复制 主从复制,顾名思义就是把主库的数据复制到从库中,因为读写分离之后,写操作都在主库进行,但是读操作是在从库进行的,也就是说,主库上的数据如果不能复制到从库中,那么从库就不会读到主库中的数据。严格意义上说,读写分离并不要求主从复制,只需要在主库写从库读即可,但是如果没有了主从复制,读写分离将失去了它的意义。因此读写分离通常与主从复制配合使用。
因为本示例使用的是
MySQL
,这里就说一下MySQL
主从复制的原理,如下图所示:文章图片
工作流程如下:
- 主库修改数据后,将修改日志写入
binlog
- 从库的
I/O
线程读取主库的binlog
,并拷贝到从库本地的binlog
中 - 从库本地的
binlog
被SQL
线程读取,执行其中的内容并同步到从库中
- 服务端代理:需要独立部署一个代理服务,该代理服务后面管理多个数据库实例,在应用中通过一个数据源与该代理服务器建立连接,由该代理去操作底层数据库,并返回相应结果。优点是支持多语言,对业务透明,缺点是实现复杂,实现难度大,同时代理需要确保自身高可用
- 客户端代理:在连接池或数据库驱动上进行一层封装,内部与不同的数据库建立连接,并对
SQL
进行必要的操作,比如读写分离选择走主库还是从库,分库分表select
后如何聚合结果。优点是实现简单,天然去中心化,缺点是支持语言较少,版本升级困难
Cobar
:阿里开源的关系型数据库分布式服务中间件,已停更DRDS
:脱胎于Cobar
,全称分布式关系型数据库服务
MyCat
:开源数据库中间件,目前更新了MyCat2
版本Atlas
:Qihoo 360
公司Web
平台部基础架构团队开发维护的一个基于MySQL
协议的数据中间层项目,同时还有一个NoSQL
的版本,叫Pika
tddl
:阿里巴巴自主研发的分布式数据库服务Sharding-JDBC
:ShardingShpere
的一个子产品,一个轻量级Java
框架
MySQL
主从复制环境准备
【Spring|Spring Boot demo系列(十二)(ShardingSphere + MyBatisPlus 读写分离 + 主从复制)】看完了一些基础理论就可以进行动手了,本小节先准备好MySQL
主从复制的环境,基于Docker
+MySQL
官方文档搭建。4.1 主库操作 4.1.1 拉取镜像并创建容器运行
docker pull mysql
docker run -itd -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql-master mysql
docker exec -it mysql-master /bin/bash
在主库中进行更新镜像源,安装
vim
以及net-tools
的操作:cd /etc/apt
echo deb http://mirrors.aliyun.com/debian/ buster main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib deb http://mirrors.aliyun.com/debian-security buster/updates main deb-src http://mirrors.aliyun.com/debian-security buster/updates main deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib > sources.list
apt update && apt upgrade
apt install vim net-tools
4.1.2 修改配置文件
vim /etc/mysql/my.cnf
添加下面两行数据:
[mysqld]
server-id=1# 全局唯一,取值[1,2^32-1],默认为1
binlog-do-db=test# 表示需要复制的是哪个库
修改完成后重启。
4.1.3 准备数据源
create database test;
use test;
create table user(
id int primary key auto_increment,
name varchar(30) not null,
age int not null
);
4.1.4 创建一个复制操作的用户(可选但推荐)
注意创建用户需要加上
mysql_native_password
,否则会导致从库一直处于连接状态:create user 'repl'@'172.17.0.3' identified with mysql_native_password by '123456';
grant replication slave on *.* to 'repl'@'172.17.0.3';
具体的地址请根据从库的地址修改,可以先看后面的从库配置部分。
4.1.5 数据备份(可选)
如果原来的主库中是有数据的,那么这部分数据需要手动同步到从库中:
flush tables with read lock;
开启主库的另一个终端,使用
mysqldump
导出:mysqldump -u root -p --all-databases --master-data > dbdump.db
导出完成后,解除读锁:
unlock tables;
4.1.6 查看主库状态
show master status;
文章图片
需要把
File
以及Position
记录下来,后面从库的配置需要用到。4.2 从库操作 4.2.1 拉取镜像并创建容器运行
docker pull mysql
docker run -itd -p 3307:3306 -p 33061:33060 -e MYSQL_ROOT_PASSWORD=123456 --name mysql-slave mysql
docker exec -it mysql-slave /bin/bash
进入容器后,像主库一样更新源然后安装
vim
和net-tools
:cd /etc/apt
echo deb http://mirrors.aliyun.com/debian/ buster main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster main non-free contrib deb http://mirrors.aliyun.com/debian-security buster/updates main deb-src http://mirrors.aliyun.com/debian-security buster/updates main deb http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-updates main non-free contrib deb http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib deb-src http://mirrors.aliyun.com/debian/ buster-backports main non-free contrib > sources.list
apt update && apt upgrade
apt install vim net-tools
4.2.2 修改配置文件
vim /etc/mysql/my.cnf
添加如下两行:
server-id=2# 全局唯一,不能与主库相同
replicate-do-db=test# 与主库相同,表示对该库进行复制
修改完成后重启。
4.2.3 查看
ip
地址查看从库的
ip
地址,用于给主库设置同步的用户:ifconfig
输出:
inet 172.17.0.3netmask 255.255.0.0broadcast 172.17.255.255
那么主库中用于复制的用户就可以是
repl@172.17.0.3
。4.2.4 导入数据(可选)
如果主库有数据可以先导入到从库:
mysqldump -u root -p --all-databases < dbdump.db
4.2.5 准备数据源
create database test;
use test;
create table user(
id int primary key auto_increment,
name varchar(30) not null,
age int not null
);
4.2.6 设置主库
可以使用
change master to
/change replication source to
(8.0.23+
)命令:change replication source to
source_host='172.17.0.2',# 可以使用ifconfig查看主库ip
source_user='repl',# 之前主库创建的用户
source_password='123456',# 密码
source_log_file='binlog.000003',# 之前在主库上使用show master status查看的日志文件
source_log_pos=594;
# 同样使用show master status查看
4.2.7 开启从库
start slave;
show slave status\G
新版本(
8.0.22+
)可使用:start replica;
show replica status\G
需要
IO
和SQL
线程显示Yes
才算成功:文章图片
4.3 测试 主库选择插入一条数据:
insert into user values(1,"name",3);
然后从库就能
select
到了:文章图片
5 搭建
Spring Boot
环境
5.1 新建项目并引入依赖
新建Spring Boot
项目,并引入如下依赖:implementation 'com.baomidou:mybatis-plus-boot-starter:3.4.3.1'
implementation 'com.baomidou:mybatis-plus-generator:3.5.0'
implementation 'org.apache.velocity:velocity-engine-core:2.3'
implementation 'org.realityforge.org.jetbrains.annotations:org.jetbrains.annotations:1.7.0'
implementation 'com.alibaba:druid:1.2.6' # 注意不能使用druid的starter依赖,会出现模板找不到的问题
implementation 'org.apache.shardingsphere:sharding-jdbc-spring-boot-starter:4.1.1'
Maven
版本:
com.baomidou
mybatis-plus-boot-starter
3.4.3.1
com.baomidou
mybatis-plus-generator
3.5.0
org.apache.velocity
velocity-engine-core
2.3
org.realityforge.org.jetbrains.annotations
org.jetbrains.annotations
1.7.0
com.alibaba
druid
1.2.6
org.apache.shardingsphere
sharding-jdbc-spring-boot-starter
4.1.1
5.2 使用生成器
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
public class MyBatisPlusGenerator {
public static void main(String[] args) {
DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:mysql://localhost:3306/test","root","123456").build();
String projectPath = System.getProperty("user.dir");
GlobalConfig globalConfig = new GlobalConfig.Builder().outputDir(projectPath+"/src/main/java").openDir(false).build();
PackageConfig packageConfig = new PackageConfig.Builder().moduleName("test").parent("com.example.demo").build();
AutoGenerator autoGenerator = new AutoGenerator(dataSourceConfig);
autoGenerator.global(globalConfig).packageInfo(packageConfig);
autoGenerator.execute();
}
}
直接运行
main
方法即可生成代码,配置请根据个人需要进行更改,更详细的配置可以参考笔者的另一篇文章。5.3 配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
shardingsphere:
datasource:
names: master,slave# 数据源名字
master:
type: com.alibaba.druid.pool.DruidDataSource# 连接池
url: jdbc:mysql://127.0.0.1:3306/test# 主库地址
username: root# 主库用户名
password: 123456# 主库密码
slave:
type: com.alibaba.druid.pool.DruidDataSource# 连接池
url: jdbc:mysql://127.0.0.1:3307/test# 从库地址
username: root
password: 123456
masterslave:
load-balance-algorithm-type: round_robin# 负载均衡算法,
name: ms
master-data-source-name: master# 主库数据源名字
slave-data-source-names: slave# 从库数据源名字
props:
sql:
show: true# 打印SQL
关于负载均衡算法,目前只支持两种:
文章图片
5.4 准备
Controller
@RestController
@RequestMapping("/test/user")
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class UserController {
private final UserServiceImpl userService;
@GetMapping("/write")
public boolean write(){
return userService.save(User.builder().age(3).name("234").build());
}@GetMapping("/read")
public User read(){
return userService.getById(1);
}
}
6 测试 访问
http://localhost:8080/test/user/write
,可以看到写操作在主库进行:文章图片
访问
http://localhost:8080/test/user/read
,可以看到读操作在从库进行:文章图片
这样读写分离就算是可以了。
7 参考源码
Java
版:- Github
- 码云
- CODE CHINA
Kotlin
版:- Github
- 码云
- CODE CHINA
- MySQL 官方文档
- 腾讯云-面试题:你们有没有做 MySQL 读写分离?如何实现 MySQL 的读写分离?
- 知乎-数据库中间件详解(精品长文)
推荐阅读
- Activiti(一)SpringBoot2集成Activiti6
- SpringBoot调用公共模块的自定义注解失效的解决
- 解决SpringBoot引用别的模块无法注入的问题
- 2018-07-09|2018-07-09 Spring 的DBCP,c3p0
- spring|spring boot项目启动websocket
- Spring|Spring Boot 整合 Activiti6.0.0
- Spring集成|Spring集成 Mina
- springboot使用redis缓存
- Spring|Spring 框架之 AOP 原理剖析已经出炉!!!预定的童鞋可以识别下发二维码去看了
- Spring|Spring Boot之ImportSelector