1. 程式人生 > 其它 >Spring Boot demo系列(十二):ShardingSphere + MyBatisPlus 讀寫分離 + 主從複製

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
  • 從庫本地的binlogSQL執行緒讀取,執行其中的內容並同步到從庫中

3.3 資料庫中介軟體簡介

資料庫中介軟體可以簡化對讀寫分離以及分庫分表的操作,並隱藏底層實現細節,可以像操作單庫單表那樣操作多庫多表,主流的設計方案主要有兩種:

  • 服務端代理:需要獨立部署一個代理服務,該代理服務後面管理多個數據庫例項,在應用中通過一個數據源與該代理伺服器建立連線,由該代理去操作底層資料庫,並返回相應結果。優點是支援多語言,對業務透明,缺點是實現複雜,實現難度大,同時代理需要確保自身高可用
  • 客戶端代理:在連線池或資料庫驅動上進行一層封裝,內部與不同的資料庫建立連線,並對SQL進行必要的操作,比如讀寫分離選擇走主庫還是從庫,分庫分表select後如何聚合結果。優點是實現簡單,天然去中心化,缺點是支援語言較少,版本升級困難

一些常見的資料庫中介軟體如下:

  • Cobar:阿里開源的關係型資料庫分散式服務中介軟體,已停更
  • DRDS:脫胎於Cobar,全稱分散式關係型資料庫服務
  • MyCat:開源資料庫中介軟體,目前更新了MyCat2版本
  • AtlasQihoo 360公司Web平臺部基礎架構團隊開發維護的一個基於MySQL協議的資料中間層專案,同時還有一個NoSQL的版本,叫Pika
  • tddl:阿里巴巴自主研發的分散式資料庫服務
  • Sharding-JDBCShardingShpere的一個子產品,一個輕量級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

進入容器後,像主庫一樣更新源然後安裝vimnet-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 to8.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

需要IOSQL執行緒顯示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 參考

如果覺得文章好看,歡迎點贊。

同時歡迎關注微信公眾號:氷泠之路。