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 參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章