首頁 > 軟體

SpringBoot+ShardingSphereJDBC實現讀寫分離詳情

2022-08-09 14:02:57

1 概述

本文講述瞭如何使用MyBatisPlus+ShardingSphereJDBC進行讀寫分離,以及利用MySQL進行一主一從的主從複製。

具體步驟包括:

  • MySQL主從複製環境準備(Docker
  • 搭建ShardingShpereJDBC+MyBatisPlus+Druid環境
  • 測試

2 環境

  • OpenJDK 17.0.3
  • Spring Boot 2.7.0
  • MyBatis Plus 3.5.1
  • MyBatis Plus Generator 3.5.2
  • Druid 1.2.10
  • ShardingSphereJDBC 5.1.1
  • MySQL 8.0.29Docker

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框架

4MySQL主從複製環境準備

看完了一些基礎理論就可以進行動手了,本小節先準備好MySQL主從複製的環境,基於Docker+MySQL官方檔案搭建。

4.1 主庫操作

4.1.1 拉取映象並建立容器執行

docker pull mysql
docker run -itd -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name master mysql
docker exec -it 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 BIGINT PRIMARY KEY,
name VARCHAR(30) 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 -e MYSQL_ROOT_PASSWORD=123456 --name slave mysql
docker exec -it 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

新增如下兩行:

[mysqld]
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

那麼主庫中用於複製的使用者就可以是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 BIGINT PRIMARY KEY,
name VARCHAR(30) 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 STATUSG

新版本(8.0.22+)可使用:

START REPLICA;
SHOW REPLICA STATUSG

需要IOSQL執行緒顯示Yes才算成功:

4.3 測試

主庫選擇插入一條資料:

INSERT INTO user VALUES(1,"name",3);

然後從庫就能select到了:

5 搭建Spring Boot環境

5.1 新建專案並引入依賴

新建Spring Boot專案,並引入如下依賴:

implementation 'com.alibaba:druid:1.2.10'
implementation 'com.baomidou:mybatis-plus-boot-starter:3.5.1'
implementation 'org.freemarker:freemarker:2.3.31'
implementation 'com.baomidou:mybatis-plus-generator:3.5.2'
implementation 'org.apache.shardingsphere:shardingsphere-jdbc-core-spring-boot-starter:5.1.1'

Maven版本:

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.10</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>5.1.1</version>
</dependency>

5.2 使用生成器

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

public class Generator {
public static void main(String[] args){
FastAutoGenerator.create("jdbc:mysql://localhost:3306/test", "root", "123456")
.globalConfig(builder ->
builder.author("author").outputDir(System.getProperty("user.dir") + "/src/main/java").build())
.packageConfig(builder ->
builder.parent("com.example.demo").moduleName("user").build())
.strategyConfig(builder ->
builder.addInclude("user").entityBuilder().enableLombok().disableSerialVersionUID().build())
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
}

直接執行main方法即可生成程式碼,設定請根據個人需要進行更改。

5.3 組態檔

spring:
shardingsphere:
mode:
type: Memory # 記憶體模式,後設資料儲存在當前程序中
datasource:
names: master,slave # 資料來源名稱,這裡有兩個
master: # 跟上面的資料來源對應
type: com.alibaba.druid.pool.DruidDataSource # 連線池
url: jdbc:mysql://127.0.0.1:3306/test # 連線url
username: root
password: 123456
slave: # 跟上面的資料來源對應
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://127.0.0.1:3306/test
username: root
password: 123456
rules:
readwrite-splitting: # 讀寫分離規則
data-sources: # 資料來源設定
random: # 這個名字隨便起
type: Static # 靜態型別
load-balancer-name: round_robin # 負載均衡演演算法名字
props:
write-data-source-name: master # 寫資料來源
read-data-source-names: slave # 讀資料來源
load-balancers: # 負載均衡設定
round_robin: # 跟上面負載均衡演演算法的名字對應
type: ROUND_ROBIN # 負載均衡演演算法
props:
sql-show: true # 列印SQL

因為組態檔的內容比較多,以下進行分開說明。

5.3.1 模式

spring.shardingsphere.mode.type模式有三種:

  • Memory:記憶體模式,初始化設定或執行SQL等操作均在當前程序生效
  • Standalone:單機模式,可以將資料來源和規則等後設資料資訊持久化,但是這些後設資料不會在叢集中同步
  • Cluster:叢集模式,提供了多個Apache ShardingSphere範例之間後設資料共用以及分散式場景下的狀態協調的能力,也提供水平擴充套件以及高可用的能力

這裡使用記憶體模式,如果想將後設資料等資訊持久化,請使用單機模式,單機模式需要設定以下屬性:

  • spring.shardingsphere.mode.type=Standalone:設定單機模式
  • spring.shardingsphere.mode.repository.type=:持久化倉庫的型別,單機模式適用型別為File
  • spring.shardingsphere.mode.repository.props.path=:後設資料儲存路徑,預設.shardingsphere
  • spring.shardingsphere.mode.overwrite=:是否覆蓋

而採用叢集模式,需要設定以下屬性:

  • spring.shardingsphere.mode.type=Cluster:設定叢集模式
  • spring.shardingsphere.mode.repository.type=:持久化倉庫型別,叢集模式支援ZooKeeper以及Etcd持久化
  • spring.shardingsphere.mode.repository.props.namespace=:註冊中心名稱空間
  • spring.shardingsphere.mode.repository.props.server-lists=:註冊中心伺服器列表
  • spring.shardingsphere.mode.overwrite=:是否覆蓋
  • spring.shardingsphere.mode.repository.props.<key>=:註冊中心的屬性設定,對於ZooKeeper,可以設定retryIntervalMilliseconds(重試間隔毫秒)、maxRetries(使用者端連線最大重試數)、timeToLiveSeconds(臨時資料存活秒數)、operationTimeoutMilliseconds(使用者端操作超時毫秒數)、digest(登入密碼),對於Etcd,可以設定timeToLiveSeconds(臨時資料存活秒數)、connectionTimeout(連線超時秒數)

5.3.2 資料來源設定

spring.shardingsphere.datasource.names,後面接資料來源的名稱,使用,分隔,比如此處有兩個資料來源:

  • master
  • slave

然後每個資料來源可以設定:

  • type:資料庫連線池型別,這裡使用的是Druid
  • username:使用者名稱
  • password:密碼
  • jdbc-url:連線url,注意,對於此處使用的Druid連線池,需要使用url而不是jdbc-url

5.3.3 讀寫分離規則設定

spring.shardingsphere.rules.readwrite-splitting,需要設定其中的資料來源以及負載均衡型別:

  • spring.shardingsphere.rules.readwrite-splitting.data-sources
  • spring.shardingsphere.rules.readwrite-splitting.load-balancers

5.3.3.1 資料來源設定

資料來源設定首先需要新增一個資料來源的名字,隨便起一個,比如這裡是random,然後需要設定三個屬性:

  • spring.shardingsphere.rules.readwrite-splitting.data-sources.random.type:讀寫分離的型別,可選值為Static與Dynamic,這裡選擇Static,如果選擇Dynamic,也就是動態資料來源,請配合dynamic-datasource-spring-boot-starter使用
  • spring.shardingsphere.rules.readwrite-splitting.data-sources.random.props.write-data-source-name:寫資料來源
  • spring.shardingsphere.rules.readwrite-splitting.data-sources.random.props.read-data-source-name:讀資料來源
  • spring.shardingsphere.rules.readwrite-splitting.data-sources.random.load-balancer-name:負載均衡演演算法的名稱,這裡寫的是round_robin

5.3.3.2 負載均衡設定

負載均衡設定需要與上面的spring.shardingsphere.rules.readwrite-splitting.data-sources.random.load-balancer-name屬性對應,比如這裡是round_robin,那麼需要設定的就是spring.shardingsphere.rules.readwrite-splitting.load-balancers.round_robin。然後下一步就是設定具體的負載均衡演演算法。

內建的負載均衡演演算法有三個:

  • 輪詢演演算法:ROUND_ROBIN,設定type=ROUND_ROBIN即可,也就是spring.shardingsphere.rules.readwrite-splitting.load-balancers.round_robin.type=ROUND_ROBIN
  • 隨機存取演演算法:RANDOM,設定type=RANDOM
  • 權重存取演演算法:WEIGHT,設定type=WEIGHT,同時需要設定props,在其中設定各個讀節點的權重

5.3.4 屬性設定

屬性的話這裡只設定了一個spring.shardingsphere.props.sql-show=true,也就是列印SQL,其他支援的屬性有:

  • spring.shardingsphere.props.sql-simple:是否列印簡單風格的SQL,預設為false
  • spring.shardingsphere.props.kernel-exector-size:設定任務處理執行緒池大小,預設為infinite
  • spring.shardingsphere.props.max-connections-size-per-query:每次查詢所能使用的最多資料庫連線數,預設為1
  • spring.shardingsphere.props.check-table-metadata-enabled:啟動時是否檢查分片後設資料的一致性,預設為false
  • spring.shardingsphere.props.check-duplicate-table-enabled:啟動時是否檢查重複表,預設為false
  • spring.shardingsphere.props.sql-federation-enabled:是否開啟聯邦查詢,預設為false

5.4 準備Controller

@RestController
@RequestMapping("/user")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserController {
private final UserServiceImpl userService;

@GetMapping("/select")
public User select(){
return userService.getById(1);
}
@GetMapping("/insert")
public boolean insert(){
return userService.saveOrUpdate(User.builder().id(3L).name("name3").build());
}
}

6 測試

存取http://localhost:8080/user/insert,可以看到寫操作在主庫進行:

存取http://localhost:8080/user/select,可以看到讀操作在從庫進行:

這樣讀寫分離就算是完成了。

到此這篇關於SpringBoot+ShardingSphereJDBC實現讀寫分離詳情的文章就介紹到這了,更多相關SpringBoot讀寫分離內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com