Spring Boot demo系列(十二):ShardingSphere + MyBatisPlus 讀寫分離 + 主從複製
1 概述
本文講述瞭如何使用MyBatisPlus
+ShardingSphere
進行讀寫分離,以及利用MySQL
進行一主一從的主從複製。
具體步驟包括:
MySQL
主從複製環境準備(Docker
)- 搭建
ShardingShpere
+MyBatisPlus
+Druid
環境 - 測試
2 環境
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
3 一些基礎理論
3.1 讀寫分離
讀寫分離,顧名思義就是讀和寫分開,更具體來說,就是:
- 寫操作在主資料庫進行
- 讀操作在從資料庫進行
使用讀寫分離的根本目的就是為了提高併發效能,如果讀寫都在同一臺MySQL
上實現,相信會不如一臺MySQL
寫,另外兩臺MySQL
讀這樣的配置效能高。另一方面,在很多時候都是讀操作的請求要遠遠高於寫操作,這樣就顯得讀寫分離非常有必要了。
3.2 主從複製
主從複製,顧名思義就是把主庫的資料複製到從庫中,因為讀寫分離之後,寫操作都在主庫進行,但是讀操作是在從庫進行的,也就是說,主庫上的資料如果不能複製到從庫中,那麼從庫就不會讀到主庫中的資料。嚴格意義上說,讀寫分離並不要求主從複製,只需要在主庫寫從庫讀即可,但是如果沒有了主從複製,讀寫分離將失去了它的意義。因此讀寫分離通常與主從複製配合使用。
因為本示例使用的是MySQL
,這裡就說一下MySQL
主從複製的原理,如下圖所示:
工作流程如下:
- 主庫修改資料後,將修改日誌寫入
binlog
- 從庫的
I/O
執行緒讀取主庫的binlog
,並拷貝到從庫本地的binlog
中 - 從庫本地的
binlog
被SQL
執行緒讀取,執行其中的內容並同步到從庫中
3.3 資料庫中介軟體簡介
資料庫中介軟體可以簡化對讀寫分離以及分庫分表的操作,並隱藏底層實現細節,可以像操作單庫單表那樣操作多庫多表,主流的設計方案主要有兩種:
- 服務端代理:需要獨立部署一個代理服務,該代理服務後面管理多個數據庫例項,在應用中通過一個數據源與該代理伺服器建立連線,由該代理去操作底層資料庫,並返回相應結果。優點是支援多語言,對業務透明,缺點是實現複雜,實現難度大,同時代理需要確保自身高可用
- 客戶端代理:在連線池或資料庫驅動上進行一層封裝,內部與不同的資料庫建立連線,並對
SQL
進行必要的操作,比如讀寫分離選擇走主庫還是從庫,分庫分表select
後如何聚合結果。優點是實現簡單,天然去中心化,缺點是支援語言較少,版本升級困難
一些常見的資料庫中介軟體如下:
Cobar
:阿里開源的關係型資料庫分散式服務中介軟體,已停更DRDS
:脫胎於Cobar
,全稱分散式關係型資料庫服務
MyCat
:開源資料庫中介軟體,目前更新了MyCat2
版本Atlas
:Qihoo 360
公司Web
平臺部基礎架構團隊開發維護的一個基於MySQL
協議的資料中間層專案,同時還有一個NoSQL
的版本,叫Pika
tddl
:阿里巴巴自主研發的分散式資料庫服務Sharding-JDBC
:ShardingShpere
的一個子產品,一個輕量級Java
框架
4 MySQL
主從複製環境準備
看完了一些基礎理論就可以進行動手了,本小節先準備好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.3 netmask 255.255.0.0 broadcast 172.17.255.255
那麼主庫中用於複製的使用者就可以是[email protected]
。
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
版本:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.realityforge.org.jetbrains.annotations</groupId>
<artifactId>org.jetbrains.annotations</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
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
版:
Kotlin
版:
8 參考
如果覺得文章好看,歡迎點贊。
同時歡迎關注微信公眾號:氷泠之路。