面試官:你項目是如何實(shí)現(xiàn)讀寫分離的?
讀寫分離(Read-Write Splitting)是一種常見的數(shù)據(jù)庫架構(gòu)優(yōu)化策略,通過將數(shù)據(jù)庫的讀操作(查詢)和寫操作(插入、更新、刪除)分離到不同的數(shù)據(jù)庫實(shí)例上,從而提高系統(tǒng)的性能、可擴(kuò)展性和高可用性。
圖片
在項目中實(shí)現(xiàn)讀寫分離目前主流的實(shí)現(xiàn)技術(shù)是通過 Apache ShardingSphere 來實(shí)現(xiàn)數(shù)據(jù)庫的讀寫分離的。
從 Apache ShardingSphere 官網(wǎng)也可以看出讀寫分離是其提供的主要功能之一:
圖片
ShardingSphere 官網(wǎng)地址:https://shardingsphere.apache.org/document/current/cn/features/readwrite-splitting/
通過 ShardingSphere 可以輕松實(shí)現(xiàn) MySQL 數(shù)據(jù)庫的讀寫分離,以下是基于最新 ShardingSphere 5.x 版本的實(shí)現(xiàn)步驟和關(guān)鍵代碼:
1.核心實(shí)現(xiàn)原理
ShardingSphere 通過 JDBC 驅(qū)動層透明代理實(shí)現(xiàn)讀寫分離,其核心邏輯為:
- SQL 路由:根據(jù) SQL 類型(SELECT/WRITE)自動路由到主庫或從庫。
- 負(fù)載均衡:支持輪詢、隨機(jī)權(quán)重等算法分配讀請求到多個從庫。
- 主從同步:依賴 MySQL 原生主從復(fù)制機(jī)制保障數(shù)據(jù)一致性。
圖片
2.具體實(shí)現(xiàn)步驟
步驟 1:搭建MySQL主從復(fù)制(前置條件)
-- 主庫配置(my.cnf)
server-id=1
log-bin=mysql-bin
binlog-format=ROW
-- 從庫配置(my.cnf)
server-id=2
relay-log=relay-bin
read-notallow=1
-- 主庫創(chuàng)建復(fù)制賬號
CREATE USER 'repl'@'%' IDENTIFIED BY 'P@ssw0rd';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
-- 從庫配置主庫連接
CHANGE MASTER TO
MASTER_HOST='master_ip',
MASTER_USER='repl',
MASTER_PASSWORD='P@ssw0rd',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=592;
START SLAVE;
步驟 2:SpringBoot項目集成ShardingSphere-JDBC
- 添加 Maven 依賴
在 pom.xml 中添加 ShardingSphere 和數(shù)據(jù)庫連接池的依賴:
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
- 配置 application.yml
在 application.yml 中配置數(shù)據(jù)源和讀寫分離規(guī)則:
spring:
shardingsphere:
datasource:
names: master,slave0
# 主庫配置
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://master_ip:3306/db?useSSL=false
username: root
password: Master@123
# 從庫配置
slave0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://slave_ip:3306/db?useSSL=false
username: root
password: Slave@123
# 從庫2配置
slave1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://slave_ip:3306/db?useSSL=false
username: root
password: Slave@123
rules:
readwrite-splitting:
data-sources:
readwrite_ds:
type: Static
props:
write-data-source-name: master
read-data-source-names:
- slave0
- slave1
load-balancer-name: round_robin
load-balancers:
round_robin:
type: ROUND_ROBIN # 輪詢
props:
sql-show: true # 顯示實(shí)際路由的SQL
配置說明
- 數(shù)據(jù)源配置:
a.master:主庫數(shù)據(jù)源,用于寫操作。
b.slave0 和 slave1:從庫數(shù)據(jù)源,用于讀操作。
- 讀寫分離規(guī)則:
a.write-data-source-name:指定寫操作的數(shù)據(jù)源。
b.read-data-source-names:指定讀操作的數(shù)據(jù)源列表。
c.load-balancer-name:指定讀操作的負(fù)載均衡算法。
- 負(fù)載均衡算法:
a.ROUND_ROBIN:輪詢算法,讀請求會在 slave0 和 slave1 之間輪詢。
b.其他可選算法:RANDOM(隨機(jī))、WEIGHT(權(quán)重)等。
3.驗(yàn)證讀寫分離
- 寫操作測試
public void createUser(User user) {
userMapper.insert(user); // INSERT 語句自動路由到master
}
- 讀操作測試
public List<User> listUsers() {
return userMapper.selectList(null); // SELECT 語句路由到slave0
}
- 查看執(zhí)行日志
控制臺會輸出類似日志:
Actual SQL: master ::: INSERT INTO user (...)
Actual SQL: slave0 ::: SELECT * FROM user
3.高級配置(可選)
- 強(qiáng)制主庫讀通過 Hint 強(qiáng)制路由到主庫:
HintManager.getInstance().setPrimaryRouteOnly();
- 故障轉(zhuǎn)移配置心跳檢測實(shí)現(xiàn)從庫故障自動剔除:
spring:
shardingsphere:
rules:
readwrite-splitting:
data-sources:
readwrite_ds:
type: Dynamic
props:
auto-aware-data-source-name: readwrite_ds
health-check-enabled: true
health-check-max-retry-count: 3
health-check-retry-interval: 5000
注意事項
主從延遲問題:異步復(fù)制場景下,剛寫入的數(shù)據(jù)可能無法立即從從庫讀取,可通過 HintManager 強(qiáng)制讀主庫臨時解決。
4.優(yōu)缺點(diǎn)分析
- 優(yōu)點(diǎn)分析:
a.提升性能:寫操作通常對性能要求較高,而讀操作可以通過從庫分擔(dān)壓力,避免主庫因高并發(fā)查詢而過載。從庫可以進(jìn)行水平擴(kuò)展(增加更多從庫實(shí)例),進(jìn)一步提升系統(tǒng)的讀取能力。
b.提高可用性:主庫和從庫可以部署在不同的服務(wù)器或機(jī)房,增加系統(tǒng)的容錯性。即使某個從庫出現(xiàn)故障,其他從庫仍然可以繼續(xù)提供讀服務(wù)。
c.優(yōu)化資源利用:主庫可以專注于處理寫操作,從庫可以優(yōu)化查詢性能(如添加更多的索引、緩存等)。
- 缺點(diǎn)分析:
- 數(shù)據(jù)一致性延遲:由于從庫的數(shù)據(jù)是通過主庫同步而來,可能存在一定的延遲(秒級或更長),導(dǎo)致讀操作可能讀取到舊數(shù)據(jù)。
- 復(fù)雜性增加:需要管理主從復(fù)制的配置和同步機(jī)制。需要處理主從切換、故障轉(zhuǎn)移等復(fù)雜情況。
- 成本增加:需要額外的硬件資源來部署從庫。需要額外的運(yùn)維成本來維護(hù)主從架構(gòu)。
5.應(yīng)用場景
讀寫分離適用于以下場景:
- 讀操作遠(yuǎn)多于寫操作系統(tǒng):如電商系統(tǒng)、社交平臺等,讀操作遠(yuǎn)多于寫操作。
- 需要高可用性:通過主從架構(gòu)提高系統(tǒng)的容錯能力。
小結(jié)
讀寫分離是一種常見的數(shù)據(jù)庫架構(gòu)優(yōu)化策略,通過將數(shù)據(jù)庫的讀操作和寫操作分離,提高了系統(tǒng)的性能、可擴(kuò)展性和高可用性。讀寫分離主流的實(shí)現(xiàn)技術(shù)是 Apache ShardingSphere,通過添加依賴,配置讀寫分離規(guī)則的方式就可以輕松的實(shí)現(xiàn)讀寫分離。