分庫分表雖然可以優化資料庫操作,但是要實現高並行,主從架構就應運而生了。資料庫的主從複製架構,將資料庫的寫操作定位到主庫中進行,主庫和從庫之間通過非同步複製、半同步複製保持資料一致。所有的讀操作都在主庫的N個從庫上進行。通過負載均衡使得每一次查詢均勻的落在每一個從庫上。
一主n從,做讀寫分離(資料寫入主庫,通過mysql資料同步機制將主庫資料同步到從庫–>程式讀取從庫資料),多個從庫之間可以實現負載均衡。次外,ShardingSphere-ShardingJdbc可手動強制部分讀請求到主庫上。(因為主從同步有延遲,對實時性要求高的系統,可以將部分讀請求也走主庫)
MySQL設定主從同步可參考另一篇部落格:https://blog.csdn.net/u014553029/article/details/108832268
程式環境:SpringBoot+MyBatis-plus
資料庫環境:
資料庫ip | 資料庫 | 作用 |
---|---|---|
127.0.0.1 | ShardingSphere | master |
127.0.0.1 | ShardingSphere1 | slave1 |
127.0.0.1 | ShardingSphere2 | slave2 |
<!--shardingsphere資料分片、脫敏工具-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
#### spring ####
spring:
# 設定說明地址 https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/configuration/config-spring-boot/#%E6%95%B0%E6%8D%AE%E5%88%86%E7%89%87
shardingsphere:
# 資料庫
datasource:
# 資料庫的別名
names: ds0,ds1,ds2
# 主庫1 ,master資料庫
ds0:
### 資料來源類別
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://146.56.192.87:3306/shardingsphere?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
username: oyc
password: oyc@123456
# 從庫1 ,slave資料庫
ds1:
### 資料來源類別
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://146.56.192.87:3306/shardingsphere1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
username: oyc
password: oyc@123456
# 從庫2 ,slave資料庫
ds2:
### 資料來源類別
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://146.56.192.87:3306/shardingsphere2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
username: oyc
password: oyc@123456
# *** 資料庫分庫分表設定 start
masterslave:
# 查詢時的負載均衡演演算法,目前有2種演演算法,round_robin(輪詢)和random(隨機),
# 演演算法介面是io.shardingjdbc.core.api.algorithm.masterslave.MasterSlaveLoadBalanceAlgorithm。
# 實現類有RandomMasterSlaveLoadBalanceAlgorithm 和 RoundRobinMasterSlaveLoadBalanceAlgorithm。
load-balance-algorithm-type: round_robin
name: dataSource
# 主資料來源名稱
master-data-source-name: ds0
# 從資料來源名稱,多個用逗號隔開
slave-data-source-names: ds1,ds2
# *** 資料庫分庫分表設定 end
props:
# 列印SQL
sql.show: true
check:
table:
metadata: true
# 是否在啟動時檢查分表後設資料一致性
enabled: true
query:
with:
cipher:
column: true
讀延遲問題解決方案:
剛插入一條資料,然後馬上就要去讀取,這個時候有可能會讀取不到?歸根到底是因為主節點寫入完之後資料是要複製給從節點的,讀不到的原因是複製的時間比較長,也就是說資料還沒複製到從節點,你就已經去從節點讀取了,肯定讀不到。mysql5.7 的主從複製是多執行緒了,意味著速度會變快,但是不一定能保證百分百馬上讀取到,這個問題我們可以有兩種方式解決:
(1)業務層面妥協,是否操作完之後馬上要進行讀取
(2)對於操作完馬上要讀出來的,且業務上不能妥協的,我們可以對於這類的讀取直接走主庫,當然Sharding-JDBC也是考慮到這個問題的存在,所以給我們提供了一個功能,可以讓使用者在使用的時候指定要不要走主庫進行讀取。在讀取前使用下面的方式進行設定就可以了:
/**
* 使用者列表,強制路由主庫
*/
@RequestMapping("ds0")
public List<User> userListDs0() {
logger.info("********TestController userListDs0():強制路由主庫");
HintManager hintManager = HintManager.getInstance();
List<User> users = userMapper.selectList(null);
//清除分片鍵值,分片鍵值儲存在ThreadLocal中,所以需要在操作結束時呼叫hintManager.close()來清除ThreadLocal中的內容。hintManager實現了AutoCloseable介面,可推薦使用try with resource自動關閉。
hintManager.close();
List<User> users1 = userMapper.selectList(null);
users.addAll(users1);
return users;
}
強制路由到主庫:
強制讀取主庫結果:
從上圖可以看出,我們設定的強制讀取主庫之後,每次查詢都是去獲取主庫的資料。
官網傳送門:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/usage/read-write-splitting/
原始碼傳送門:https://github.com/oycyqr/springboot-learning-demo/tree/master/springboot-shardingsphere-load