SpringBoot+ShardingSphereJDBC實現讀寫分離詳情
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.29
(Docker
)
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
框架
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
進入容器後,像主庫一樣更新源然後安裝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
添加如下兩行:
[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
那麼主庫中用於復制的用戶就可以是[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 BIGINT PRIMARY KEY, name VARCHAR(30) 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.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讀寫分離內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- docker容器中安裝vim問題解決
- 使用sharding-jdbc實現水平分表的示例代碼
- docker 編輯Dockerfile 添加php7.2 acpu的問題
- 利用Sharding-Jdbc進行分庫分表的操作代碼
- SpringBoot詳解如何實現讀寫分離