ShardingSphere數(shù)據(jù)庫(kù)讀寫(xiě)分離算法及測(cè)試示例詳解
碼農(nóng)在囧途
最近這段時(shí)間來(lái)經(jīng)歷了太多東西,無(wú)論是個(gè)人的壓力還是個(gè)人和團(tuán)隊(duì)失誤所帶來(lái)的損失,都太多,被罵了很多,也被檢討,甚至一些不方便說(shuō)的東西都經(jīng)歷了,不過(guò)還好,一切都得到了解決,無(wú)論好壞,這對(duì)于個(gè)人來(lái)說(shuō)也是一種成長(zhǎng)吧,事后自己也做了一些深刻的檢討,總結(jié)為一句話(huà)“挫敗使你難受,使你睡不著覺(jué),使你痛苦,不過(guò)最后一定會(huì)使你變得成熟,變得認(rèn)真,變得負(fù)責(zé)”,每次面臨挫敗,我都會(huì)告訴自己,這不算什么,十年之后,你回過(guò)頭來(lái)看待這件事的時(shí)候,你一定會(huì)覺(jué)得,這算什么屁事。
背景
在現(xiàn)在這個(gè)數(shù)據(jù)量與日俱增的時(shí)代,傳統(tǒng)的單表,單庫(kù)已經(jīng)無(wú)法滿(mǎn)足我們的需求,可能早期數(shù)據(jù)量不是很大,CRUD都集中在一個(gè)庫(kù)中,但是當(dāng)數(shù)據(jù)量 到達(dá)一定的規(guī)模的時(shí)候,使用單庫(kù)可能就無(wú)法滿(mǎn)足需求了,在實(shí)際場(chǎng)景中,讀的頻率是遠(yuǎn)遠(yuǎn)大于寫(xiě)的,所以我們一般會(huì)做讀寫(xiě)分離,主庫(kù)一般用于寫(xiě),而從庫(kù) 用于讀,而主從分離有好幾種模式。
一主多從
一主多從是只有一臺(tái)主機(jī)用于寫(xiě)操作,多臺(tái)從機(jī)用于讀操作,一主多從是存在風(fēng)險(xiǎn)的,當(dāng)主機(jī)宕機(jī)后,那么寫(xiě)服務(wù)就會(huì)癱瘓,本文我們主要說(shuō)的是ShardingSphere讀寫(xiě)分離, 而目前ShardingSphere只支持單主庫(kù),所以如果要保證業(yè)務(wù)的高可用,那么目前ShardingSphere不是很好的選擇,不過(guò)希望ShardingSphere后面支持多主機(jī)模式。
多主多從
從上面的一主多從我們看出了它的弊端,所以為了保證高可用,我們可能需要多個(gè)主機(jī)用于寫(xiě)操作,這樣當(dāng)某個(gè)主機(jī)宕機(jī),其他主機(jī)還能繼續(xù)工作,ShardingSphere只支持 單主機(jī)。
ShardingSphere只需要簡(jiǎn)單的配置就能實(shí)現(xiàn)數(shù)據(jù)庫(kù)的讀寫(xiě)的分離,我們甚至感知不到是在操作多個(gè)數(shù)據(jù)庫(kù),極大的簡(jiǎn)化了我們的開(kāi)發(fā),但是ShardingSphere 不支持多主庫(kù),也無(wú)法進(jìn)行主從數(shù)據(jù)庫(kù)的同步。
ShardingSphere整合SpringBoot項(xiàng)目進(jìn)行主從分離
ShardingSphere和SpringBoot能夠很簡(jiǎn)單的進(jìn)行組合,只需要簡(jiǎn)單的配置,ShardingSphere能夠和主流的ORM框架進(jìn)行整合,ShardingSphere會(huì) 從ORM框架中解析出SQL語(yǔ)句,判斷是讀操作還是寫(xiě)操作,如果是讀操作,則會(huì)落到主庫(kù)上,如果是讀操作,那么ShardingSphere會(huì)使用對(duì)應(yīng)的負(fù)載均衡算法負(fù)載到 對(duì)應(yīng)的從庫(kù)上面。
maven引入ShardingSphere starter
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.2</version>
</dependency>
yml文件配置
names為數(shù)據(jù)庫(kù)名稱(chēng)字符串,然后需要一個(gè)一個(gè)的進(jìn)行配置JDBC連接,對(duì)于讀寫(xiě)分離,我們需要關(guān)注rules下面的readwrite-splitting 通過(guò)load-balancers配置負(fù)載均衡策略,data-sources配置對(duì)應(yīng)的讀寫(xiě)庫(kù),目前ShardingSphere只支持單主庫(kù),多從庫(kù),如下我們寫(xiě) 庫(kù)使用write-data-source-name,庫(kù)為db1,讀庫(kù)使用read-data-source-names,庫(kù)db2,db3,db4。
spring:
shardingsphere:
datasource:
names: db1,db2,db3,db4
db1:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: qwer123@
type: com.zaxxer.hikari.HikariDataSource
maximumPoolSize: 10
db2:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: qwer123@
type: com.zaxxer.hikari.HikariDataSource
maximumPoolSize: 10
db3:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db3?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: qwer123@
type: com.zaxxer.hikari.HikariDataSource
maximumPoolSize: 10
db4:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db4?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: qwer123@
type: com.zaxxer.hikari.HikariDataSource
maximumPoolSize: 10
rules:
sharding:
readwrite-splitting:
load-balancers:
round_robin:
type: ROUND_ROBIN
data-sources:
read_write_db:
type: Static
props:
write-data-source-name: db1
read-data-source-names: db2,db3,db4
load-balancer-name: round_robin
props:
sql-show: true
測(cè)試寫(xiě)操作。
因?yàn)閷?xiě)操作配置的數(shù)據(jù)庫(kù)是db1,所以所有寫(xiě)操作都應(yīng)該進(jìn)入db1,如下圖所示,解析出來(lái)的ShardingSphere-SQL中顯示的都是db1。

測(cè)試讀操作
讀操作配置的數(shù)據(jù)庫(kù)是db2,db3,db4,配置的負(fù)載均衡算法是ROUND_ROBIN(輪詢(xún)算法),所以查詢(xún)請(qǐng)求會(huì)在三個(gè)庫(kù)順序查詢(xún)。

ShardingSphere負(fù)載均衡算法
因?yàn)閺膸?kù)有多個(gè),所以我們需要根據(jù)一定的策略將請(qǐng)求分發(fā)到不同的數(shù)據(jù)庫(kù)上,防止單節(jié)點(diǎn)的壓力過(guò)大或者空閑,ShardingSphere內(nèi)置了多種負(fù)載均衡算法,如果我們想實(shí)現(xiàn)自己的 算法,那么可以實(shí)現(xiàn)ReadQueryLoadBalanceAlgorithm接口,下面我們列舉幾種來(lái)看下。
ROUND_ROBIN 輪詢(xún)算法
配置負(fù)載均衡算法為輪詢(xún)算法,那么所有請(qǐng)求都會(huì)均勻的分發(fā)到對(duì)應(yīng)的數(shù)據(jù)庫(kù),這樣,每臺(tái)數(shù)據(jù)庫(kù)所承受的壓力都是一樣的,輪詢(xún)算法對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)是RoundRobinReplicaLoadBalanceAlgorithm。
public final class RoundRobinReplicaLoadBalanceAlgorithm implements ReadQueryLoadBalanceAlgorithm {
private final AtomicInteger count = new AtomicInteger(0);
@Getter
private Properties props;
@Override
public void init(final Properties props) {
this.props = props;
}
@Override
public String getDataSource(final String name, final String writeDataSourceName, final List<String> readDataSourceNames) {
if (TransactionHolder.isTransaction()) {
return writeDataSourceName;
}
return readDataSourceNames.get(Math.abs(count.getAndIncrement()) % readDataSourceNames.size());
}
@Override
public String getType() {
return "ROUND_ROBIN";
}
@Override
public boolean isDefault() {
return true;
}
}
RANDOM 隨機(jī)算法
如果使用隨機(jī)算法,那么請(qǐng)求過(guò)來(lái)以后就會(huì)隨機(jī)的分發(fā)到其中的一個(gè)數(shù)據(jù)庫(kù)上面,使用隨機(jī)算法可能會(huì)導(dǎo)致請(qǐng)求的分發(fā)不均勻,可能某一臺(tái) 接受到了大量的請(qǐng)求,某一臺(tái)接受到的請(qǐng)求相對(duì)來(lái)說(shuō)較少。
WEIGHT 基于權(quán)重的算法
基于權(quán)重的算法需要做相應(yīng)的配置,我們可以將某一臺(tái)數(shù)據(jù)庫(kù)的權(quán)重加大,某一臺(tái)數(shù)據(jù)庫(kù)的權(quán)重減小,這樣,權(quán)重大的數(shù)據(jù)庫(kù) 就會(huì)接收到更多的請(qǐng)求,權(quán)重小的接收到的請(qǐng)求就會(huì)比較少。
在ShardingSphere中自定義負(fù)載均衡算法
ShardingSphere中使用了大量的SPI,所以我們開(kāi)發(fā)者可以自由的實(shí)現(xiàn)自己的規(guī)則,然后無(wú)縫的切換到自己的規(guī)則,我們可以實(shí)現(xiàn)自己的一套負(fù)載均衡算法,其實(shí)ShardingSphere內(nèi)置的集中負(fù)載均衡算法完全能滿(mǎn)足數(shù)據(jù)庫(kù)負(fù)載均衡,只不過(guò)為了更加深入的學(xué)習(xí)ShardingSphere,所以我們很有必要自己簡(jiǎn)單的實(shí)現(xiàn)一下。
下面我們簡(jiǎn)單的實(shí)現(xiàn)一下,我們就不去實(shí)現(xiàn)一些復(fù)雜的了,為了演示,我們將所有請(qǐng)求全部都負(fù)載到db2。
定義SPI
我們從ShardingSphere的讀寫(xiě)分離模塊shardingspere-readwrite-spliltting-core中的META-INF/services下面看到了負(fù)載均衡的SPI。

org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.RoundRobinReplicaLoadBalanceAlgorithm org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.RandomReplicaLoadBalanceAlgorithm org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.WeightReplicaLoadBalanceAlgorithm org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.FixedPrimaryLoadBalanceAlgorithm org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.FixedReplicaRandomLoadBalanceAlgorithm org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.FixedReplicaRoundRobinLoadBalanceAlgorithm org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.FixedReplicaWeightLoadBalanceAlgorithm org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.TransactionRandomReplicaLoadBalanceAlgorithm org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.TransactionRoundRobinReplicaLoadBalanceAlgorithm org.apache.shardingsphere.readwritesplitting.algorithm.loadbalance.TransactionWeightReplicaLoadBalanceAlgorithm
為了實(shí)現(xiàn)自己的負(fù)載均衡算法,我們需要在自己的模塊中定義SPI,如下,在自己項(xiàng)目的META-INF/services目錄下編寫(xiě)負(fù)載均衡SPI接口,里面內(nèi)容為我們自定義的負(fù)載均衡算法的類(lèi)文件的位置。

編寫(xiě)負(fù)載均衡算法核心代碼
自定義負(fù)載均衡算法需要實(shí)現(xiàn)ReadQueryLoadBalanceAlgorithm接口,里面核心的兩個(gè)方法是getDataSource和getType,getDataSource是算法的邏輯實(shí)現(xiàn)部分,其目的是選出一個(gè)目標(biāo)數(shù)據(jù)庫(kù),此方法會(huì)傳入readDataSourceNames,它是讀庫(kù)的集合,我們此處直接返回db2,那么會(huì)一直讀db2,getType是返回負(fù)載均衡算法的名稱(chēng)。
/**
* 功能說(shuō)明: 自定義負(fù)載均衡算法
* <p>
* Original @Author: steakliu-劉牌, 2022-07-20 18:05
* <p>
* Copyright (C)2020-2022 steakliu All rights reserved.
*/
public class CustomReplicaLoadBalanceAlgorithm implements ReadQueryLoadBalanceAlgorithm {
@Getter
private Properties props;
@Override
public String getDataSource(final String name, final String writeDataSourceName, final List<String> readDataSourceNames) {
return "db2";
}
@Override
public String getType() {
return "CUSTOM";
}
@Override
public void init(Properties props) {
this.props = props;
}
@Override
public boolean isDefault() {
return false;
}
}
在yml中使用自己實(shí)現(xiàn)的負(fù)載均衡算法
rules:
sharding:
readwrite-splitting:
load-balancers:
custom:
type: CUSTOM
data-sources:
read_write_db:
type: Static
props:
write-data-source-name: db1
read-data-source-names: db2,db3,db4
load-balancer-name: custom
發(fā)起大量的查詢(xún)操作
從日志輸出來(lái)看,所有的請(qǐng)求全部落在了db2上面,于是證明我們自定義的負(fù)載均衡算法成功了。

讀寫(xiě)分離的中間件其實(shí)有很多,ShardingSphere旨在構(gòu)建異構(gòu)數(shù)據(jù)庫(kù)上層的標(biāo)準(zhǔn)和生態(tài),使用它我們基本上能解決數(shù)據(jù)庫(kù)中的大部分問(wèn)題,但是ShardingSphere也并不是萬(wàn)能的,還有一些東西沒(méi)有實(shí)現(xiàn),我們期待ShardingSphere能夠?qū)崿F(xiàn)更多強(qiáng)大,好用的功能。
關(guān)于ShardingSphere讀寫(xiě)分離的分享,我們今天就先說(shuō)到這里,后面我們會(huì)繼續(xù)探索ShardingSphere的更多強(qiáng)大的功能,比如數(shù)據(jù)分片,高可用,數(shù)據(jù)加密,影子庫(kù)等,今天的分享就到這里,更多關(guān)于ShardingSphere讀寫(xiě)分離的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java中將字符串String轉(zhuǎn)換為整數(shù)int的多種方法
在Java中將String類(lèi)型轉(zhuǎn)換為int類(lèi)型是一個(gè)常見(jiàn)的操作,下面這篇文章主要給大家介紹了關(guān)于Java中將字符串String轉(zhuǎn)換為整數(shù)int的多種方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-07-07
SpringBoot構(gòu)建Restful service完成Get和Post請(qǐng)求
這篇文章主要介紹了SpringBoot構(gòu)建Restful service完成Get和Post請(qǐng)求的示例代碼,感興趣的朋友一起看看吧2017-08-08
SpringBoot中集成串口通信的項(xiàng)目實(shí)踐
本文主要介紹了SpringBoot中集成串口通信,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
深入理解Java中的volatile關(guān)鍵字(總結(jié)篇)
volatile這個(gè)關(guān)鍵字,不僅僅在Java語(yǔ)言中有,在很多語(yǔ)言中都有的,而且其用法和語(yǔ)義也都是不盡相同的。這篇文章主要介紹了Java中的volatile關(guān)鍵字,需要的朋友可以參考下2018-10-10
Java實(shí)現(xiàn)OJ多組測(cè)試數(shù)據(jù)的輸入方法
今天小編就為大家分享一篇Java實(shí)現(xiàn)OJ多組測(cè)試數(shù)據(jù)的輸入方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
spring事務(wù)的REQUIRES_NEW源碼示例解析
這篇文章主要為大家介紹了spring事務(wù)的REQUIRES_NEW源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09

