ShardingSphere數(shù)據(jù)庫讀寫分離算法及測試示例詳解
碼農(nóng)在囧途
最近這段時間來經(jīng)歷了太多東西,無論是個人的壓力還是個人和團(tuán)隊(duì)失誤所帶來的損失,都太多,被罵了很多,也被檢討,甚至一些不方便說的東西都經(jīng)歷了,不過還好,一切都得到了解決,無論好壞,這對于個人來說也是一種成長吧,事后自己也做了一些深刻的檢討,總結(jié)為一句話“挫敗使你難受,使你睡不著覺,使你痛苦,不過最后一定會使你變得成熟,變得認(rèn)真,變得負(fù)責(zé)”,每次面臨挫敗,我都會告訴自己,這不算什么,十年之后,你回過頭來看待這件事的時候,你一定會覺得,這算什么屁事。
背景
在現(xiàn)在這個數(shù)據(jù)量與日俱增的時代,傳統(tǒng)的單表,單庫已經(jīng)無法滿足我們的需求,可能早期數(shù)據(jù)量不是很大,CRUD都集中在一個庫中,但是當(dāng)數(shù)據(jù)量 到達(dá)一定的規(guī)模的時候,使用單庫可能就無法滿足需求了,在實(shí)際場景中,讀的頻率是遠(yuǎn)遠(yuǎn)大于寫的,所以我們一般會做讀寫分離,主庫一般用于寫,而從庫 用于讀,而主從分離有好幾種模式。
一主多從
一主多從是只有一臺主機(jī)用于寫操作,多臺從機(jī)用于讀操作,一主多從是存在風(fēng)險(xiǎn)的,當(dāng)主機(jī)宕機(jī)后,那么寫服務(wù)就會癱瘓,本文我們主要說的是ShardingSphere讀寫分離, 而目前ShardingSphere只支持單主庫,所以如果要保證業(yè)務(wù)的高可用,那么目前ShardingSphere不是很好的選擇,不過希望ShardingSphere后面支持多主機(jī)模式。
多主多從
從上面的一主多從我們看出了它的弊端,所以為了保證高可用,我們可能需要多個主機(jī)用于寫操作,這樣當(dāng)某個主機(jī)宕機(jī),其他主機(jī)還能繼續(xù)工作,ShardingSphere只支持 單主機(jī)。
ShardingSphere只需要簡單的配置就能實(shí)現(xiàn)數(shù)據(jù)庫的讀寫的分離,我們甚至感知不到是在操作多個數(shù)據(jù)庫,極大的簡化了我們的開發(fā),但是ShardingSphere 不支持多主庫,也無法進(jìn)行主從數(shù)據(jù)庫的同步。
ShardingSphere整合SpringBoot項(xiàng)目進(jìn)行主從分離
ShardingSphere和SpringBoot能夠很簡單的進(jìn)行組合,只需要簡單的配置,ShardingSphere能夠和主流的ORM框架進(jìn)行整合,ShardingSphere會 從ORM框架中解析出SQL語句,判斷是讀操作還是寫操作,如果是讀操作,則會落到主庫上,如果是讀操作,那么ShardingSphere會使用對應(yīng)的負(fù)載均衡算法負(fù)載到 對應(yīng)的從庫上面。
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ù)庫名稱字符串,然后需要一個一個的進(jìn)行配置JDBC連接,對于讀寫分離,我們需要關(guān)注rules下面的readwrite-splitting
通過load-balancers
配置負(fù)載均衡策略,data-sources
配置對應(yīng)的讀寫庫,目前ShardingSphere只支持單主庫,多從庫,如下我們寫 庫使用write-data-source-name
,庫為db1
,讀庫使用read-data-source-names
,庫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
測試寫操作。
因?yàn)閷懖僮髋渲玫臄?shù)據(jù)庫是db1
,所以所有寫操作都應(yīng)該進(jìn)入db1
,如下圖所示,解析出來的ShardingSphere-SQL
中顯示的都是db1。
測試讀操作
讀操作配置的數(shù)據(jù)庫是db2
,db3
,db4
,配置的負(fù)載均衡算法是ROUND_ROBIN
(輪詢算法),所以查詢請求會在三個庫順序查詢。
ShardingSphere負(fù)載均衡算法
因?yàn)閺膸煊卸鄠€,所以我們需要根據(jù)一定的策略將請求分發(fā)到不同的數(shù)據(jù)庫上,防止單節(jié)點(diǎn)的壓力過大或者空閑,ShardingSphere內(nèi)置了多種負(fù)載均衡算法,如果我們想實(shí)現(xiàn)自己的 算法,那么可以實(shí)現(xiàn)ReadQueryLoadBalanceAlgorithm
接口,下面我們列舉幾種來看下。
ROUND_ROBIN 輪詢算法
配置負(fù)載均衡算法為輪詢算法,那么所有請求都會均勻的分發(fā)到對應(yīng)的數(shù)據(jù)庫,這樣,每臺數(shù)據(jù)庫所承受的壓力都是一樣的,輪詢算法對應(yīng)的實(shí)現(xiàn)類是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ī)算法,那么請求過來以后就會隨機(jī)的分發(fā)到其中的一個數(shù)據(jù)庫上面,使用隨機(jī)算法可能會導(dǎo)致請求的分發(fā)不均勻,可能某一臺 接受到了大量的請求,某一臺接受到的請求相對來說較少。
WEIGHT 基于權(quán)重的算法
基于權(quán)重的算法需要做相應(yīng)的配置,我們可以將某一臺數(shù)據(jù)庫的權(quán)重加大,某一臺數(shù)據(jù)庫的權(quán)重減小,這樣,權(quán)重大的數(shù)據(jù)庫 就會接收到更多的請求,權(quán)重小的接收到的請求就會比較少。
在ShardingSphere中自定義負(fù)載均衡算法
ShardingSphere中使用了大量的SPI
,所以我們開發(fā)者可以自由的實(shí)現(xiàn)自己的規(guī)則,然后無縫的切換到自己的規(guī)則,我們可以實(shí)現(xiàn)自己的一套負(fù)載均衡算法,其實(shí)ShardingSphere內(nèi)置的集中負(fù)載均衡算法完全能滿足數(shù)據(jù)庫負(fù)載均衡,只不過為了更加深入的學(xué)習(xí)ShardingSphere,所以我們很有必要自己簡單的實(shí)現(xiàn)一下。
下面我們簡單的實(shí)現(xiàn)一下,我們就不去實(shí)現(xiàn)一些復(fù)雜的了,為了演示,我們將所有請求全部都負(fù)載到db2
。
定義SPI
我們從ShardingSphere的讀寫分離模塊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目錄下編寫負(fù)載均衡SPI接口,里面內(nèi)容為我們自定義的負(fù)載均衡算法的類文件的位置。
編寫負(fù)載均衡算法核心代碼
自定義負(fù)載均衡算法需要實(shí)現(xiàn)ReadQueryLoadBalanceAlgorithm
接口,里面核心的兩個方法是getDataSource
和getType
,getDataSource
是算法的邏輯實(shí)現(xiàn)部分,其目的是選出一個目標(biāo)數(shù)據(jù)庫,此方法會傳入readDataSourceNames
,它是讀庫的集合,我們此處直接返回db2
,那么會一直讀db2
,getType
是返回負(fù)載均衡算法的名稱。
/** * 功能說明: 自定義負(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ā)起大量的查詢操作
從日志輸出來看,所有的請求全部落在了db2
上面,于是證明我們自定義的負(fù)載均衡算法成功了。
讀寫分離的中間件其實(shí)有很多,ShardingSphere旨在構(gòu)建異構(gòu)數(shù)據(jù)庫上層的標(biāo)準(zhǔn)和生態(tài),使用它我們基本上能解決數(shù)據(jù)庫中的大部分問題,但是ShardingSphere也并不是萬能的,還有一些東西沒有實(shí)現(xiàn),我們期待ShardingSphere能夠?qū)崿F(xiàn)更多強(qiáng)大,好用的功能。
關(guān)于ShardingSphere讀寫分離的分享,我們今天就先說到這里,后面我們會繼續(xù)探索ShardingSphere的更多強(qiáng)大的功能,比如數(shù)據(jù)分片,高可用,數(shù)據(jù)加密,影子庫等,今天的分享就到這里,更多關(guān)于ShardingSphere讀寫分離的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot編程式事務(wù)TransactionTemplate的使用說明
這篇文章主要介紹了springboot編程式事務(wù)TransactionTemplate的使用說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06Mybatis in條件傳參的三種實(shí)現(xiàn)方式(直接$,List,[])
這篇文章主要介紹了Mybatis in條件傳參的三種實(shí)現(xiàn)方式(直接$,List,[]),具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12基于Java語言在窗體上實(shí)現(xiàn)飛機(jī)大戰(zhàn)小游戲的完整步驟
這篇文章主要給大家介紹了基于Java語言在窗體上實(shí)現(xiàn)飛機(jī)大戰(zhàn)小游戲的完整步驟,文中通過圖文以及實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-02-02springboot集成springsecurity 使用OAUTH2做權(quán)限管理的教程
這篇文章主要介紹了springboot集成springsecurity 使用OAUTH2做權(quán)限管理的教程,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12詳解JDK中ExecutorService與Callable和Future對線程的支持
這篇文章主要介紹了詳解JDK中ExecutorService與Callable和Future對線程的支持的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09詳解Spring Boot 自定義PropertySourceLoader
這篇文章主要介紹了詳解Spring Boot 自定義PropertySourceLoader,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05Java 生成隨機(jī)字符串?dāng)?shù)組的實(shí)例詳解
這篇文章主要介紹了Java 生成隨機(jī)字符串?dāng)?shù)組的實(shí)例詳解的相關(guān)資料,主要是利用Collections.sort()方法對泛型為String的List 進(jìn)行排序,需要的朋友可以參考下2017-08-08