面試官:你項目是如何實現讀寫分離的?
讀寫分離(Read-Write Splitting)是一種常見的數據庫架構優化策略,通過將數據庫的讀操作(查詢)和寫操作(插入、更新、刪除)分離到不同的數據庫實例上,從而提高系統的性能、可擴展性和高可用性。
圖片
在項目中實現讀寫分離目前主流的實現技術是通過 Apache ShardingSphere 來實現數據庫的讀寫分離的。
從 Apache ShardingSphere 官網也可以看出讀寫分離是其提供的主要功能之一:
圖片
ShardingSphere 官網地址:https://shardingsphere.apache.org/document/current/cn/features/readwrite-splitting/
通過 ShardingSphere 可以輕松實現 MySQL 數據庫的讀寫分離,以下是基于最新 ShardingSphere 5.x 版本的實現步驟和關鍵代碼:
1.核心實現原理
ShardingSphere 通過 JDBC 驅動層透明代理實現讀寫分離,其核心邏輯為:
- SQL 路由:根據 SQL 類型(SELECT/WRITE)自動路由到主庫或從庫。
- 負載均衡:支持輪詢、隨機權重等算法分配讀請求到多個從庫。
- 主從同步:依賴 MySQL 原生主從復制機制保障數據一致性。
圖片
2.具體實現步驟
步驟 1:搭建MySQL主從復制(前置條件)
-- 主庫配置(my.cnf)
server-id=1
log-bin=mysql-bin
binlog-format=ROW
-- 從庫配置(my.cnf)
server-id=2
relay-log=relay-bin
read-notallow=1
-- 主庫創建復制賬號
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 和數據庫連接池的依賴:
<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 中配置數據源和讀寫分離規則:
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 # 顯示實際路由的SQL
配置說明
- 數據源配置:
a.master:主庫數據源,用于寫操作。
b.slave0 和 slave1:從庫數據源,用于讀操作。
- 讀寫分離規則:
a.write-data-source-name:指定寫操作的數據源。
b.read-data-source-names:指定讀操作的數據源列表。
c.load-balancer-name:指定讀操作的負載均衡算法。
- 負載均衡算法:
a.ROUND_ROBIN:輪詢算法,讀請求會在 slave0 和 slave1 之間輪詢。
b.其他可選算法:RANDOM(隨機)、WEIGHT(權重)等。
3.驗證讀寫分離
- 寫操作測試
public void createUser(User user) {
userMapper.insert(user); // INSERT 語句自動路由到master
}
- 讀操作測試
public List<User> listUsers() {
return userMapper.selectList(null); // SELECT 語句路由到slave0
}
- 查看執行日志
控制臺會輸出類似日志:
Actual SQL: master ::: INSERT INTO user (...)
Actual SQL: slave0 ::: SELECT * FROM user
3.高級配置(可選)
- 強制主庫讀通過 Hint 強制路由到主庫:
HintManager.getInstance().setPrimaryRouteOnly();
- 故障轉移配置心跳檢測實現從庫故障自動剔除:
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
注意事項
主從延遲問題:異步復制場景下,剛寫入的數據可能無法立即從從庫讀取,可通過 HintManager 強制讀主庫臨時解決。
4.優缺點分析
- 優點分析:
a.提升性能:寫操作通常對性能要求較高,而讀操作可以通過從庫分擔壓力,避免主庫因高并發查詢而過載。從庫可以進行水平擴展(增加更多從庫實例),進一步提升系統的讀取能力。
b.提高可用性:主庫和從庫可以部署在不同的服務器或機房,增加系統的容錯性。即使某個從庫出現故障,其他從庫仍然可以繼續提供讀服務。
c.優化資源利用:主庫可以專注于處理寫操作,從庫可以優化查詢性能(如添加更多的索引、緩存等)。
- 缺點分析:
- 數據一致性延遲:由于從庫的數據是通過主庫同步而來,可能存在一定的延遲(秒級或更長),導致讀操作可能讀取到舊數據。
- 復雜性增加:需要管理主從復制的配置和同步機制。需要處理主從切換、故障轉移等復雜情況。
- 成本增加:需要額外的硬件資源來部署從庫。需要額外的運維成本來維護主從架構。
5.應用場景
讀寫分離適用于以下場景:
- 讀操作遠多于寫操作系統:如電商系統、社交平臺等,讀操作遠多于寫操作。
- 需要高可用性:通過主從架構提高系統的容錯能力。
小結
讀寫分離是一種常見的數據庫架構優化策略,通過將數據庫的讀操作和寫操作分離,提高了系統的性能、可擴展性和高可用性。讀寫分離主流的實現技術是 Apache ShardingSphere,通過添加依賴,配置讀寫分離規則的方式就可以輕松的實現讀寫分離。