MySQL主從復(fù)制數(shù)據(jù)同步的實(shí)現(xiàn)步驟
一、什么是 MySQL 的主從復(fù)制
MySQL 的主從復(fù)制(Master-Slave Replication)是一種將數(shù)據(jù)從一個(gè)主數(shù)據(jù)庫服務(wù)器(主庫)復(fù)制到一個(gè)或多個(gè)從數(shù)據(jù)庫服務(wù)器(從庫)的技術(shù)。主庫負(fù)責(zé)所有的數(shù)據(jù)寫操作,從庫則通過讀取主庫的二進(jìn)制日志來同步主庫的數(shù)據(jù)變化。主從復(fù)制主要用于實(shí)現(xiàn)數(shù)據(jù)的備份、負(fù)載分擔(dān)和高可用性。
二、具體流程
1. 主庫記錄寫操作
當(dāng)主庫執(zhí)行寫操作(如 INSERT
、UPDATE
或 DELETE
)時(shí),這些操作會(huì)被記錄到二進(jìn)制日志(binlog)中。二進(jìn)制日志保存了所有數(shù)據(jù)更改的歷史記錄,這些記錄將成為從庫同步數(shù)據(jù)的來源。
步驟:主庫將所有寫操作(數(shù)據(jù)變更)記錄到二進(jìn)制日志中。
2. 從庫連接主庫并讀取二進(jìn)制日志
從庫通過一個(gè)稱為IO 線程的進(jìn)程與主庫建立連接,開始讀取主庫的二進(jìn)制日志。主庫會(huì)將日志內(nèi)容發(fā)送給從庫的 IO 線程。
步驟:從庫的 IO 線程連接主庫,持續(xù)接收主庫二進(jìn)制日志的最新內(nèi)容。
3. 從庫將二進(jìn)制日志寫入中繼日志
從庫的 IO 線程將接收到的主庫二進(jìn)制日志復(fù)制到從庫的**中繼日志(relay log)**中。中繼日志在從庫本地保存了一份主庫數(shù)據(jù)變更的副本,供從庫執(zhí)行使用。
步驟:從庫的 IO 線程將接收的日志寫入中繼日志,為數(shù)據(jù)變更做好準(zhǔn)備。
4. 從庫應(yīng)用中繼日志,執(zhí)行數(shù)據(jù)同步
從庫的另一個(gè)線程(稱為SQL 線程)負(fù)責(zé)讀取中繼日志中的內(nèi)容,并逐條在從庫上執(zhí)行,以實(shí)現(xiàn)數(shù)據(jù)的同步。每當(dāng)主庫有新的數(shù)據(jù)變更,從庫都會(huì)從中繼日志中獲取并執(zhí)行這些變更,從而保持與主庫的數(shù)據(jù)一致。
步驟:從庫的 SQL 線程從中繼日志中讀取并執(zhí)行記錄的操作,逐步更新從庫數(shù)據(jù)。
5. 持續(xù)復(fù)制和同步
整個(gè)主從復(fù)制過程是一個(gè)持續(xù)的流程,只要主庫有新的數(shù)據(jù)變更,從庫就會(huì)自動(dòng)獲取并執(zhí)行對(duì)應(yīng)的更改,從而保持與主庫數(shù)據(jù)的一致性。主從復(fù)制會(huì)持續(xù)保持同步,以確保從庫能夠?qū)崟r(shí)或接近實(shí)時(shí)地反映主庫的最新數(shù)據(jù)。
步驟:主庫的所有寫操作都會(huì)記錄到日志中,實(shí)時(shí)同步到從庫;從庫持續(xù)讀取并執(zhí)行日志內(nèi)容,更新自身數(shù)據(jù)。
三、作用和好處
讀寫分離,提高性能
- 減輕主庫壓力:將大量的讀操作分散到從庫,主庫主要負(fù)責(zé)寫操作,大大降低了主庫的負(fù)載壓力。
- 提升并發(fā)處理能力:增加從庫的數(shù)量可以提升系統(tǒng)的讀并發(fā)能力,處理更多用戶的并發(fā)請(qǐng)求,提升系統(tǒng)整體性能。
高可用性和容錯(cuò)性
- 數(shù)據(jù)冗余與容災(zāi):主從復(fù)制提供了數(shù)據(jù)的實(shí)時(shí)備份,從庫可以在主庫發(fā)生故障時(shí)快速接管服務(wù),提高系統(tǒng)的可用性。
- 故障切換:當(dāng)主庫出現(xiàn)故障時(shí),可以臨時(shí)將從庫升級(jí)為主庫,保障服務(wù)持續(xù)運(yùn)行。
負(fù)載均衡
- 分擔(dān)查詢壓力:多個(gè)從庫可以共同承擔(dān)讀取請(qǐng)求,通過負(fù)載均衡算法(如輪詢、隨機(jī)等)分配請(qǐng)求,提升系統(tǒng)的穩(wěn)定性。
- 避免單點(diǎn)瓶頸:讀操作分散到不同的從庫上執(zhí)行,避免了單一數(shù)據(jù)庫因訪問量過大而成為系統(tǒng)瓶頸。
擴(kuò)展性
- 水平擴(kuò)展:根據(jù)業(yè)務(wù)增長,靈活增加從庫數(shù)量,滿足性能需求,而無需對(duì)主庫進(jìn)行大幅度改造。
- 彈性擴(kuò)展:可以根據(jù)實(shí)際流量,增加或減少從庫,做到靈活應(yīng)對(duì)負(fù)載變化。
數(shù)據(jù)備份和安全性
- 數(shù)據(jù)保護(hù):從庫可以用于數(shù)據(jù)備份和容災(zāi),防止主庫故障導(dǎo)致的數(shù)據(jù)丟失。
- 快速恢復(fù):在數(shù)據(jù)意外丟失或損壞時(shí),可以通過從庫恢復(fù)主庫的數(shù)據(jù)。
四、在實(shí)際應(yīng)用中的場(chǎng)景與示例
1. 電商平臺(tái)
在電商平臺(tái)中,用戶的瀏覽和查詢操作會(huì)產(chǎn)生大量的讀請(qǐng)求,而訂單創(chuàng)建、支付等操作會(huì)產(chǎn)生寫請(qǐng)求。通過主從復(fù)制的讀寫分離:
- 查詢庫存、商品詳情等高頻率的讀操作可以由從庫承擔(dān),減輕主庫壓力。
- 用戶下單、支付等寫操作由主庫負(fù)責(zé),確保數(shù)據(jù)的完整性和一致性。
2. 社交媒體或內(nèi)容網(wǎng)站
在社交媒體應(yīng)用中,大量的內(nèi)容瀏覽和搜索會(huì)產(chǎn)生大量的讀操作,而發(fā)布、點(diǎn)贊等則是寫操作。通過主從復(fù)制,平臺(tái)可以:
- 使用從庫來承擔(dān)瀏覽、查詢等操作,確保高并發(fā)下的快速響應(yīng)。
- 將寫操作指向主庫,確保用戶發(fā)布或點(diǎn)贊的實(shí)時(shí)更新。
五、在 Spring Boot 項(xiàng)目中集成 MySQL 主從復(fù)制
在 Spring Boot 項(xiàng)目中集成 MySQL 的主從復(fù)制數(shù)據(jù)同步,指的是將 Spring Boot 應(yīng)用程序連接到配置有主從復(fù)制的 MySQL 數(shù)據(jù)庫系統(tǒng)上,完成主從數(shù)據(jù)庫的配置管理,實(shí)現(xiàn)自動(dòng)的讀寫分離。具體來說,就是通過多數(shù)據(jù)源配置,讓 Spring Boot 自動(dòng)識(shí)別是寫請(qǐng)求還是讀請(qǐng)求,并將寫請(qǐng)求發(fā)送到主庫,讀請(qǐng)求發(fā)送到從庫。
集成的關(guān)鍵要素:
- 多數(shù)據(jù)源配置:在 Spring Boot 中配置主數(shù)據(jù)庫和從數(shù)據(jù)庫的連接。
- 動(dòng)態(tài)數(shù)據(jù)源路由:在應(yīng)用層面實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)選擇,讀操作使用從庫,寫操作使用主庫。
- 讀寫分離注解或切面:利用自定義注解或 AOP 切面控制數(shù)據(jù)源的切換。
1.配置 MySQL 的主從復(fù)制環(huán)境
配置 MySQL 的主從復(fù)制環(huán)境是實(shí)現(xiàn) MySQL 主從復(fù)制數(shù)據(jù)同步的第一步。主要步驟包括設(shè)置主庫(Master)和從庫(Slave),并驗(yàn)證主從復(fù)制的成功。
1.1 設(shè)置主庫(Master)
配置主庫是主從復(fù)制的第一步。主庫負(fù)責(zé)記錄數(shù)據(jù)變更并將其傳遞給從庫。
1.1.1 修改主庫的配置文件
在主庫的 MySQL 配置文件中(通常位于 /etc/my.cnf
或 /etc/mysql/my.cnf
),需要啟用二進(jìn)制日志并為主庫設(shè)置一個(gè)唯一的 server-id
。在 [mysqld]
部分添加如下配置:
[mysqld] server-id=1 # 主庫的唯一 ID log-bin=mysql-bin # 啟用二進(jìn)制日志,主從復(fù)制依賴于此 binlog-do-db=your_database # 需要同步的數(shù)據(jù)庫名稱,多個(gè)數(shù)據(jù)庫可添加多行
- server-id:用于標(biāo)識(shí)每個(gè) MySQL 實(shí)例的唯一標(biāo)識(shí)符,主庫的
server-id
一般設(shè)置為 1。 - log-bin:啟用二進(jìn)制日志,這是主從復(fù)制的基礎(chǔ)。
- binlog-do-db:指定需要同步的數(shù)據(jù)庫(多個(gè)數(shù)據(jù)庫可以多行設(shè)置)。
注意:如果需要同步多個(gè)數(shù)據(jù)庫,可以多次添加 binlog-do-db
行,例如:
binlog-do-db=database1 binlog-do-db=database2
1.1.2 重啟 MySQL 服務(wù)
修改配置文件后,需要重啟 MySQL 以使配置生效:
# Linux 系統(tǒng) sudo systemctl restart mysqld # Windows 系統(tǒng) net stop mysql net start mysql
1.1.3 創(chuàng)建用于復(fù)制的用戶
在主庫中創(chuàng)建一個(gè)用于復(fù)制的用戶,并授予 REPLICATION SLAVE
權(quán)限。這個(gè)用戶用于從庫連接主庫并進(jìn)行數(shù)據(jù)同步。
在主庫的 MySQL 命令行中執(zhí)行以下命令:
CREATE USER 'replica_user'@'%' IDENTIFIED BY 'password'; GRANT REPLICATION SLAVE ON *.* TO 'replica_user'@'%'; FLUSH PRIVILEGES;
- replica_user:用于復(fù)制的用戶名,可以自定義。
- password:復(fù)制用戶的密碼,注意使用強(qiáng)密碼。
- %:允許所有遠(yuǎn)程 IP 訪問。如果只允許特定從庫連接,可以用從庫的 IP 地址代替
%
。
1.1.4 獲取主庫的二進(jìn)制日志信息
為了讓從庫知道從何處開始復(fù)制數(shù)據(jù),主庫需要提供當(dāng)前的二進(jìn)制日志位置??梢酝ㄟ^以下命令查看:
SHOW MASTER STATUS;
命令執(zhí)行后,會(huì)輸出如下內(nèi)容:
+------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000001 | 154 | your_database| | +------------------+----------+--------------+------------------+
- File:當(dāng)前的二進(jìn)制日志文件名。
- Position:二進(jìn)制日志的偏移量,從該位置開始讀取數(shù)據(jù)。
建議:在生產(chǎn)環(huán)境中,執(zhí)行 SHOW MASTER STATUS; 之前,可以先執(zhí)行 FLUSH TABLES WITH READ LOCK; 來鎖定表,防止數(shù)據(jù)寫入導(dǎo)致的數(shù)據(jù)不一致。獲取信息后再執(zhí)行 UNLOCK TABLES; 解鎖。
1.2 設(shè)置從庫(Slave)
配置從庫是主從復(fù)制的第二步。配置從庫連接到主庫并開始同步數(shù)據(jù)。
1.2.1 修改從庫的配置文件
在從庫的 MySQL 配置文件中(通常是 /etc/my.cnf
或 /etc/mysql/my.cnf
),設(shè)置一個(gè)唯一的 server-id
并配置中繼日志和只讀模式。 在 [mysqld]
部分添加以下配置:
[mysqld] server-id=2 # 從庫的唯一 ID,不能與主庫或其他從庫相同 relay-log=relay-log # 設(shè)置中繼日志文件名前綴 read-only=1 # 設(shè)置只讀模式,防止誤操作
- server-id:從庫的唯一標(biāo)識(shí)符,每個(gè)從庫的
server-id
也需要是唯一的,通常從 2 開始。 - relay-log:指定中繼日志的前綴,用于存儲(chǔ)從主庫同步的數(shù)據(jù)。
- read-only:開啟只讀模式,防止意外寫入。此模式下,擁有
SUPER
權(quán)限的用戶仍可以寫入數(shù)據(jù)。
1.2.2 重啟 MySQL 服務(wù)
修改配置文件后,重啟從庫的 MySQL 服務(wù)以應(yīng)用配置:
# Linux 系統(tǒng) sudo systemctl restart mysqld # Windows 系統(tǒng) net stop mysql net start mysql
1.2.3 配置從庫連接到主庫
在從庫的 MySQL 中,配置主庫信息以便開始復(fù)制。需要用到在主庫上記錄的 File
和 Position
值。
CHANGE MASTER TO MASTER_HOST='主庫的 IP 地址', MASTER_USER='replica_user', MASTER_PASSWORD='password', MASTER_LOG_FILE='mysql-bin.000001', -- 主庫的 File 值 MASTER_LOG_POS=154; -- 主庫的 Position 值
- MASTER_HOST:主庫的 IP 地址。
- MASTER_USER:在主庫上創(chuàng)建的用于復(fù)制的用戶(如
replica_user
)。 - MASTER_PASSWORD:復(fù)制用戶的密碼。
- MASTER_LOG_FILE:主庫的二進(jìn)制日志文件名。
- MASTER_LOG_POS:二進(jìn)制日志的位置偏移量。
1.2.4 啟動(dòng)復(fù)制進(jìn)程
配置完成后,啟動(dòng)從庫的復(fù)制進(jìn)程以開始從主庫復(fù)制數(shù)據(jù):
START SLAVE;
1.2.5 查看從庫的復(fù)制狀態(tài)
運(yùn)行以下命令檢查從庫的狀態(tài),確認(rèn)從庫已成功連接到主庫并開始復(fù)制數(shù)據(jù):
SHOW SLAVE STATUS\G
- Slave_IO_Running:應(yīng)為
Yes
,表示從庫的 IO 線程正在讀取主庫的日志。 - Slave_SQL_Running:應(yīng)為
Yes
,表示從庫的 SQL 線程正在執(zhí)行主庫傳遞的日志。 - Last_IO_Error 和 Last_SQL_Error:如果存在錯(cuò)誤信息,會(huì)在此處顯示。
如果 Slave_IO_Running 或 Slave_SQL_Running 為 No,請(qǐng)查看 Last_IO_Error 或 Last_SQL_Error 中的錯(cuò)誤消息,并根據(jù)錯(cuò)誤提示排查問題。
1.3 驗(yàn)證主從復(fù)制是否成功
在配置完成后,驗(yàn)證主從復(fù)制的成功性。確保主庫的寫操作能夠被從庫正確同步。
1.3.1 在主庫上創(chuàng)建測(cè)試數(shù)據(jù)
在主庫上選擇用于復(fù)制的數(shù)據(jù)庫,并創(chuàng)建一個(gè)測(cè)試表插入數(shù)據(jù):
USE your_database; CREATE TABLE test_table ( id INT PRIMARY KEY, name VARCHAR(50) ); INSERT INTO test_table (id, name) VALUES (1, 'Test Data');
1.3.2 在從庫上檢查數(shù)據(jù)同步情況
在從庫上選擇相同的數(shù)據(jù)庫,查詢 test_table
表,確認(rèn)是否同步了主庫的數(shù)據(jù):
USE your_database; SELECT * FROM test_table;
- 如果可以看到
Test Data
,則表示主從復(fù)制配置成功并且工作正常。
1.3.3 驗(yàn)證實(shí)時(shí)同步效果
在主庫上繼續(xù)插入或更新數(shù)據(jù),再次在從庫上查詢,驗(yàn)證數(shù)據(jù)是否能夠及時(shí)同步。主從復(fù)制一般情況下會(huì)立即同步,延遲較小。
1.4 故障排查
在配置主從復(fù)制的過程中,可能會(huì)遇到以下常見問題:
1 從庫無法連接到主庫
- 檢查網(wǎng)絡(luò)連接:確保主庫的防火墻允許從庫的 IP 地址訪問 MySQL 的 3306 端口。
2 權(quán)限問題
- 用戶權(quán)限:確保在主庫上創(chuàng)建的用戶擁有
REPLICATION SLAVE
權(quán)限。
3 主從版本兼容性問題
- 版本兼容性:確保主庫和從庫的 MySQL 版本相互兼容,推薦使用相同的版本。
4 日志位置不正確
- 文件和位置錯(cuò)誤:如果配置的
File
和Position
不正確,可以重新設(shè)置主從復(fù)制位置。
2.在 Spring Boot 項(xiàng)目中配置多數(shù)據(jù)源
配置多數(shù)據(jù)源是實(shí)現(xiàn)主從復(fù)制的重要步驟。通過多數(shù)據(jù)源配置,Spring Boot 應(yīng)用可以自動(dòng)區(qū)分并選擇主庫或從庫,從而實(shí)現(xiàn)讀寫分離。這一步的具體操作包括添加依賴、配置數(shù)據(jù)源信息,以及配置數(shù)據(jù)源路由以實(shí)現(xiàn)自動(dòng)選擇主庫或從庫。以下是詳細(xì)的分步講解。
2.1 添加必要的依賴
在使用 Spring Data JPA 和 MySQL 多數(shù)據(jù)源時(shí),需要添加以下依賴項(xiàng):
- MySQL 驅(qū)動(dòng):支持連接 MySQL 數(shù)據(jù)庫。
- Spring Data JPA:提供 JPA 支持,簡化數(shù)據(jù)庫操作。
- HikariCP 連接池:Spring Boot 默認(rèn)的連接池,適合高并發(fā)環(huán)境且支持多數(shù)據(jù)源配置。
在項(xiàng)目的 pom.xml
中添加以下依賴:
<dependencies> <!-- MySQL 驅(qū)動(dòng) --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- Spring Data JPA --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- HikariCP 連接池(Spring Boot 默認(rèn)連接池) --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </dependency> </dependencies>
2.2 在配置文件中定義主庫和從庫的數(shù)據(jù)源信息
接下來,需要在 application.properties
中定義主庫和從庫的連接信息。主庫通常用于寫操作,從庫用于讀操作。
application.properties
文件內(nèi)容
# 主庫配置 spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.master.url=jdbc:mysql://主庫IP地址:3306/your_database?useSSL=false&characterEncoding=utf8 spring.datasource.master.username=主庫用戶名 spring.datasource.master.password=主庫密碼 # 從庫配置 spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.slave.url=jdbc:mysql://從庫IP地址:3306/your_database?useSSL=false&characterEncoding=utf8 spring.datasource.slave.username=從庫用戶名 spring.datasource.slave.password=從庫密碼
說明:
- spring.datasource.master:主庫連接信息,用于寫操作。
- spring.datasource.slave:從庫連接信息,用于讀操作。
- driver-class-name:MySQL 驅(qū)動(dòng)類名。
- url:數(shù)據(jù)庫連接 URL,其中包含數(shù)據(jù)庫的 IP 地址和數(shù)據(jù)庫名稱。
- username 和 password:數(shù)據(jù)庫的用戶名和密碼。
2.3 創(chuàng)建數(shù)據(jù)源配置類,配置主從數(shù)據(jù)源
在項(xiàng)目中添加一個(gè)配置類,用于定義主庫和從庫數(shù)據(jù)源,并通過一個(gè)動(dòng)態(tài)數(shù)據(jù)源實(shí)現(xiàn)自動(dòng)選擇主庫或從庫。
2.3.1 配置主庫和從庫的數(shù)據(jù)源
首先,我們需要在配置類中定義兩個(gè)數(shù)據(jù)源,即主庫和從庫。主庫主要用于寫操作,從庫用于讀操作。
package com.example.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import org.springframework.boot.jdbc.DataSourceBuilder; @Configuration public class DataSourceConfig { // 定義主庫數(shù)據(jù)源 @Bean(name = "masterDataSource") @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } // 定義從庫數(shù)據(jù)源 @Bean(name = "slaveDataSource") @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); } }
說明:
@ConfigurationProperties:這個(gè)注解將
application.properties
中spring.datasource.master
和spring.datasource.slave
配置的信息綁定到masterDataSource
和slaveDataSource
上。這樣,Spring Boot 會(huì)自動(dòng)讀取配置文件中的主從庫信息,并將它們分別注入到兩個(gè)數(shù)據(jù)源對(duì)象中。DataSourceBuilder:這是 Spring 提供的一個(gè)工具類,通過它可以根據(jù)配置文件構(gòu)建
DataSource
對(duì)象。DataSource
是與數(shù)據(jù)庫連接的核心組件,主要用于管理數(shù)據(jù)庫連接、連接池等信息。
2.3.2 動(dòng)態(tài)數(shù)據(jù)源路由
為了實(shí)現(xiàn)主從分離,我們需要根據(jù)請(qǐng)求自動(dòng)選擇主庫或從庫的數(shù)據(jù)源。AbstractRoutingDataSource
是 Spring 提供的一個(gè)抽象類,可以根據(jù)用戶定義的路由規(guī)則動(dòng)態(tài)選擇數(shù)據(jù)源。
創(chuàng)建 DynamicDataSource
類:該類繼承自 AbstractRoutingDataSource
,通過設(shè)置鍵值映射來實(shí)現(xiàn)數(shù)據(jù)源選擇。
package com.example.config; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); // 設(shè)置當(dāng)前線程的數(shù)據(jù)源(主庫或從庫) public static void setDataSource(String dataSourceKey) { contextHolder.set(dataSourceKey); } // 清除當(dāng)前線程的數(shù)據(jù)源 public static void clearDataSource() { contextHolder.remove(); } // 確定當(dāng)前數(shù)據(jù)源(由 AbstractRoutingDataSource 調(diào)用) @Override protected Object determineCurrentLookupKey() { return contextHolder.get(); } }
代碼詳解:
contextHolder:使用
ThreadLocal
存儲(chǔ)當(dāng)前線程的數(shù)據(jù)源標(biāo)識(shí)(master
或slave
)。ThreadLocal
確保每個(gè)線程擁有獨(dú)立的變量副本,不會(huì)干擾其他線程。setDataSource:用于設(shè)置當(dāng)前線程的數(shù)據(jù)源,通過傳入
master
或slave
來指定主庫或從庫。clearDataSource:用于清除當(dāng)前線程的數(shù)據(jù)源,避免數(shù)據(jù)源信息在線程之間混淆。
determineCurrentLookupKey:這是
AbstractRoutingDataSource
提供的方法,它會(huì)在每次數(shù)據(jù)庫操作時(shí)調(diào)用。根據(jù)contextHolder
中的數(shù)據(jù)源標(biāo)識(shí),選擇主庫或從庫。
2.3.3 將主從數(shù)據(jù)源注入到動(dòng)態(tài)數(shù)據(jù)源
我們需要將主庫和從庫的數(shù)據(jù)源注入到動(dòng)態(tài)數(shù)據(jù)源中,并根據(jù)不同的業(yè)務(wù)需求自動(dòng)選擇。
@Bean(name = "dynamicDataSource") public DataSource dynamicDataSource( @Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaveDataSource") DataSource slaveDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("master", masterDataSource); targetDataSources.put("slave", slaveDataSource); DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setDefaultTargetDataSource(masterDataSource); // 設(shè)置默認(rèn)的數(shù)據(jù)源 dynamicDataSource.setTargetDataSources(targetDataSources); // 將主庫和從庫的數(shù)據(jù)源加入路由 return dynamicDataSource; }
代碼詳解:
@Qualifier:指定
masterDataSource
和slaveDataSource
,通過依賴注入的方式將主庫和從庫的數(shù)據(jù)源傳入dynamicDataSource
。targetDataSources:將
master
和slave
作為鍵,分別映射到masterDataSource
和slaveDataSource
。DynamicDataSource
會(huì)通過determineCurrentLookupKey
方法自動(dòng)選擇對(duì)應(yīng)的數(shù)據(jù)源。setDefaultTargetDataSource:設(shè)置默認(rèn)的數(shù)據(jù)源。在沒有指定數(shù)據(jù)源的情況下,系統(tǒng)會(huì)使用默認(rèn)數(shù)據(jù)源(一般為主庫)。
2.3.4 配置 EntityManagerFactory 使用動(dòng)態(tài)數(shù)據(jù)源
對(duì)于使用 JPA 的項(xiàng)目,EntityManagerFactory
是 JPA 的核心組件之一。它負(fù)責(zé)管理實(shí)體管理器,處理 JPA 的持久化操作。這里需要將 EntityManagerFactory
配置為使用動(dòng)態(tài)數(shù)據(jù)源,以實(shí)現(xiàn)主從分離。
@Bean(name = "entityManagerFactory") public LocalContainerEntityManagerFactoryBean entityManagerFactory( @Qualifier("dynamicDataSource") DataSource dynamicDataSource) { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dynamicDataSource); // 使用動(dòng)態(tài)數(shù)據(jù)源 em.setPackagesToScan("com.example.entity"); // 實(shí)體類包名 em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); // 設(shè)置 JPA 供應(yīng)商為 Hibernate return em; }
代碼詳解:
setDataSource:設(shè)置 JPA 的數(shù)據(jù)源為
dynamicDataSource
,這樣 JPA 會(huì)根據(jù)動(dòng)態(tài)數(shù)據(jù)源的路由選擇合適的數(shù)據(jù)源。setPackagesToScan:指定 JPA 實(shí)體類所在的包路徑,JPA 會(huì)掃描該包中的實(shí)體類,進(jìn)行數(shù)據(jù)庫操作的映射。
- HibernateJpaVendorAdapter:設(shè)置 JPA 的供應(yīng)商適配器。這里我們使用 Hibernate 作為 JPA 的實(shí)現(xiàn),它提供了 Hibernate 特有的優(yōu)化和配置支持。
通過配置 EntityManagerFactory
使用 dynamicDataSource
,我們可以讓 JPA 在操作數(shù)據(jù)庫時(shí)自動(dòng)根據(jù)業(yè)務(wù)需要切換到主庫或從庫,從而實(shí)現(xiàn)主從分離和讀寫分離。
2.3.5 配置事務(wù)管理器
Spring Data JPA 默認(rèn)需要一個(gè)事務(wù)管理器來管理事務(wù)。我們需要為動(dòng)態(tài)數(shù)據(jù)源配置一個(gè) JpaTransactionManager
,以確保事務(wù)操作可以正確地應(yīng)用于當(dāng)前選擇的數(shù)據(jù)源(主庫或從庫)。
@Bean(name = "transactionManager") public JpaTransactionManager transactionManager( @Qualifier("entityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactory) { return new JpaTransactionManager(entityManagerFactory.getObject()); }
代碼詳解:
JpaTransactionManager:Spring 提供的 JPA 事務(wù)管理器,它會(huì)自動(dòng)處理
EntityManager
的創(chuàng)建和關(guān)閉,并確保在事務(wù)中對(duì)數(shù)據(jù)庫操作的 ACID 特性(原子性、一致性、隔離性、持久性)。entityManagerFactory.getObject():將
entityManagerFactory
(配置了動(dòng)態(tài)數(shù)據(jù)源的實(shí)體管理器工廠)傳遞給JpaTransactionManager
。這樣,事務(wù)管理器會(huì)根據(jù)動(dòng)態(tài)數(shù)據(jù)源路由來管理事務(wù),確保事務(wù)一致性。
3.實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)切換
為了實(shí)現(xiàn)數(shù)據(jù)庫的主從切換,使得 Spring Boot 項(xiàng)目可以根據(jù)操作類型自動(dòng)選擇主庫或從庫,我們需要實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)切換。實(shí)現(xiàn)動(dòng)態(tài)切換的關(guān)鍵步驟包括:定義自定義注解、創(chuàng)建 AOP 切面,在方法執(zhí)行時(shí)動(dòng)態(tài)地決定使用哪個(gè)數(shù)據(jù)源。
3.1 創(chuàng)建 @DataSource 注解(用于標(biāo)識(shí)使用哪個(gè)數(shù)據(jù)源)
首先,我們創(chuàng)建一個(gè)自定義注解 @DataSource
,用于標(biāo)識(shí)在特定方法或類上指定的數(shù)據(jù)源類型(如主庫 master
或從庫 slave
)。有了這個(gè)注解之后,我們可以在代碼中靈活地指定哪些操作使用主庫,哪些操作使用從庫。
package com.example.annotation; import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.TYPE}) // 可以用在方法或類上 @Retention(RetentionPolicy.RUNTIME) // 在運(yùn)行時(shí)保留,便于通過反射獲取 @Documented public @interface DataSource { String value() default "master"; // 默認(rèn)使用主庫 }
注解參數(shù)說明:
@Target:定義注解的使用位置,
ElementType.METHOD
表示可以作用于方法,ElementType.TYPE
表示可以作用于類。@Retention:指定注解的保留策略為
RUNTIME
,即該注解會(huì)保留到運(yùn)行時(shí),并且可以通過反射獲取,這樣切面類可以在運(yùn)行時(shí)識(shí)別注解。value:注解的屬性,用來指定數(shù)據(jù)源類型,默認(rèn)為
"master"
,表示主庫。我們可以在使用注解時(shí),通過設(shè)置@DataSource("slave")
來指定從庫。
3.2 創(chuàng)建 DataSourceAspect 切面類(根據(jù)注解動(dòng)態(tài)切換數(shù)據(jù)源)
AOP(面向切面編程)可以在方法調(diào)用前后動(dòng)態(tài)地切入代碼邏輯。在這里,我們編寫一個(gè) AOP 切面,用于在方法調(diào)用之前,根據(jù) @DataSource
注解的值設(shè)置數(shù)據(jù)源。在方法調(diào)用之后,清除數(shù)據(jù)源的標(biāo)識(shí),以確保不會(huì)影響后續(xù)操作。
package com.example.aspect; import com.example.annotation.DataSource; import com.example.config.DynamicDataSource; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; @Aspect @Component public class DataSourceAspect { // 定義切點(diǎn):匹配帶有 @DataSource 注解的方法或類 @Pointcut("@annotation(com.example.annotation.DataSource) || @within(com.example.annotation.DataSource)") public void dataSourcePointCut() { } // 在方法執(zhí)行前,根據(jù)注解的值切換數(shù)據(jù)源 @Before("dataSourcePointCut()") public void before(JoinPoint point) { String dataSource = "master"; // 默認(rèn)使用主庫 Class<?> targetClass = point.getTarget().getClass(); MethodSignature signature = (MethodSignature) point.getSignature(); // 獲取方法上的 @DataSource 注解 DataSource ds = signature.getMethod().getAnnotation(DataSource.class); if (ds != null) { dataSource = ds.value(); } else if (targetClass.isAnnotationPresent(DataSource.class)) { // 如果方法上沒有注解,則讀取類上的 @DataSource 注解 ds = targetClass.getAnnotation(DataSource.class); if (ds != null) { dataSource = ds.value(); } } // 設(shè)置當(dāng)前線程的數(shù)據(jù)源標(biāo)識(shí) DynamicDataSource.setDataSource(dataSource); } // 在方法執(zhí)行后,清除數(shù)據(jù)源標(biāo)識(shí) @After("dataSourcePointCut()") public void after(JoinPoint point) { DynamicDataSource.clearDataSource(); } }
切面類代碼詳解:
@Pointcut:定義切點(diǎn)
dataSourcePointCut
,用于匹配所有帶有@DataSource
注解的方法或類。這意味著我們可以在方法上、類上使用@DataSource
來控制數(shù)據(jù)源選擇。@Before:在目標(biāo)方法執(zhí)行之前觸發(fā)
before
方法。MethodSignature:通過
MethodSignature
可以獲取方法的注解。dataSource:默認(rèn)值為
"master"
(主庫)。首先檢查方法上的@DataSource
注解,如果沒有找到,再檢查類上的@DataSource
注解。DynamicDataSource.setDataSource(dataSource):根據(jù)注解值設(shè)置當(dāng)前線程使用的數(shù)據(jù)源(主庫或從庫)。這使得后續(xù)的數(shù)據(jù)庫操作將根據(jù)注解指定的數(shù)據(jù)源執(zhí)行。
@After:在目標(biāo)方法執(zhí)行后觸發(fā)
after
方法,用于清除當(dāng)前線程的數(shù)據(jù)源標(biāo)識(shí),以避免線程復(fù)用時(shí)對(duì)其他請(qǐng)求產(chǎn)生干擾。DynamicDataSource.clearDataSource()
方法會(huì)移除當(dāng)前線程的數(shù)據(jù)源標(biāo)識(shí)。
4.在業(yè)務(wù)代碼中使用
4.1 使用 @DataSource 注解
在業(yè)務(wù)代碼中,可以在需要使用主庫或從庫的業(yè)務(wù)方法上添加 @DataSource
注解。默認(rèn)情況下,@DataSource
注解的 value
屬性是 "master"
,表示主庫。我們可以在查詢類方法上標(biāo)記 @DataSource("slave")
以使用從庫,從而實(shí)現(xiàn)讀寫分離。
4.1.1.在服務(wù)層使用 @DataSource 注解
假設(shè)我們有一個(gè) UserService
服務(wù)類,該類提供了查詢用戶列表和保存用戶的功能。我們可以通過 @DataSource
注解指定使用的數(shù)據(jù)庫。
import com.example.annotation.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserService { @Autowired private UserRepository userRepository; // 使用從庫(slave)進(jìn)行讀取操作 @DataSource("slave") public List<User> getUsers() { // 數(shù)據(jù)庫查詢操作,這里會(huì)通過切面選擇從庫 return userRepository.findAll(); } // 使用主庫(master)進(jìn)行寫入操作 @DataSource("master") public void saveUser(User user) { // 數(shù)據(jù)庫寫入操作,這里會(huì)通過切面選擇主庫 userRepository.save(user); } }
示例說明:
getUsers 方法:由于標(biāo)注了
@DataSource("slave")
注解,因此在執(zhí)行getUsers
方法時(shí)會(huì)選擇從庫作為當(dāng)前數(shù)據(jù)源,從而使查詢操作通過從庫完成,減輕主庫壓力。saveUser 方法:標(biāo)注了
@DataSource("master")
注解,因此在執(zhí)行saveUser
方法時(shí)會(huì)選擇主庫作為當(dāng)前數(shù)據(jù)源,從而保證數(shù)據(jù)寫入操作在主庫上進(jìn)行,確保數(shù)據(jù)的一致性。
4.1.2.在 DAO 層(數(shù)據(jù)持久層)使用 @DataSource 注解
假設(shè)我們將數(shù)據(jù)源控制進(jìn)一步細(xì)化到 DAO 層。例如,在 UserRepository
中的查詢和保存方法上分別指定數(shù)據(jù)源。
import com.example.annotation.DataSource; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface UserRepository extends JpaRepository<User, Long> { // 使用從庫(slave)進(jìn)行讀取操作 @DataSource("slave") List<User> findByName(String name); // 使用主庫(master)進(jìn)行寫入操作 @DataSource("master") User save(User user); }
示例說明:
findByName 方法:該方法標(biāo)注了
@DataSource("slave")
,因此在調(diào)用findByName
時(shí),數(shù)據(jù)查詢操作將通過從庫完成。save 方法:該方法標(biāo)注了
@DataSource("master")
,確保數(shù)據(jù)寫入操作始終通過主庫進(jìn)行,保證了數(shù)據(jù)的完整性。
注意:將 @DataSource
注解放在服務(wù)層和 DAO 層都會(huì)生效。實(shí)際應(yīng)用中可以根據(jù)業(yè)務(wù)邏輯的復(fù)雜程度來決定在哪一層實(shí)現(xiàn)數(shù)據(jù)源的切換。
4.2 確保讀操作走從庫,寫操作走主庫
通過在業(yè)務(wù)方法中使用 @DataSource
注解,可以實(shí)現(xiàn)以下的邏輯:
1.讀操作走從庫:在查詢數(shù)據(jù)的方法上添加 @DataSource("slave")
,使這些方法通過從庫執(zhí)行。
- 從庫通常用于處理讀操作。這樣可以分擔(dān)主庫的負(fù)載,提升系統(tǒng)的讀取性能。
- 因?yàn)閺膸斓臄?shù)據(jù)來自主庫的復(fù)制,存在一定延遲,所以一般不用于強(qiáng)一致性要求的場(chǎng)景,而適合對(duì)實(shí)時(shí)性要求較低的查詢場(chǎng)景。
2.寫操作走主庫:在插入、更新、刪除數(shù)據(jù)的方法上添加 @DataSource("master")
,確保這些操作通過主庫執(zhí)行。
- 主庫是數(shù)據(jù)的源頭,負(fù)責(zé)處理寫入、更新等操作,以確保數(shù)據(jù)的準(zhǔn)確性和一致性。
- 這樣可以避免因?yàn)閺?fù)制延遲導(dǎo)致的數(shù)據(jù)不一致問題。
示例:在 ProductService
中實(shí)現(xiàn)讀寫分離
import com.example.annotation.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class ProductService { @Autowired private ProductRepository productRepository; // 使用從庫讀取產(chǎn)品列表 @DataSource("slave") public List<Product> getAllProducts() { // 從庫執(zhí)行查詢 return productRepository.findAll(); } // 使用主庫保存產(chǎn)品信息 @DataSource("master") public void addProduct(Product product) { // 主庫執(zhí)行插入操作 productRepository.save(product); } // 更新產(chǎn)品信息,使用主庫 @DataSource("master") public void updateProduct(Product product) { // 主庫執(zhí)行更新操作 productRepository.save(product); } // 刪除產(chǎn)品信息,使用主庫 @DataSource("master") public void deleteProductById(Long productId) { // 主庫執(zhí)行刪除操作 productRepository.deleteById(productId); } }
代碼解釋:
getAllProducts() 方法:
- 使用
@DataSource("slave")
注解,從庫將執(zhí)行該方法中的查詢操作。 - 適用于讀取類操作,減輕主庫壓力。
- 使用
addProduct()、updateProduct() 和 deleteProductById() 方法:
- 這些方法使用
@DataSource("master")
注解,因此會(huì)走主庫。 - 適用于寫操作(新增、修改、刪除),確保數(shù)據(jù)的強(qiáng)一致性和實(shí)時(shí)性。
- 這些方法使用
4.3 注意事項(xiàng)
在實(shí)現(xiàn)讀寫分離時(shí),以下幾點(diǎn)需要注意:
主從數(shù)據(jù)一致性:從庫的數(shù)據(jù)源于主庫的復(fù)制,所以存在一定的延遲。對(duì)于不允許數(shù)據(jù)延遲的操作,建議強(qiáng)制使用主庫。比如訂單支付或庫存更新等操作,通常對(duì)實(shí)時(shí)性要求較高,應(yīng)直接走主庫。
事務(wù)管理:在事務(wù)中進(jìn)行讀操作,可能會(huì)導(dǎo)致數(shù)據(jù)源切換失效,因?yàn)樵谑聞?wù)中默認(rèn)會(huì)使用主庫。這種情況下,可以使用
@Transactional(readOnly = true)
標(biāo)記方法為只讀事務(wù),讓 Spring 在事務(wù)中仍使用從庫。線程安全:因?yàn)閿?shù)據(jù)源切換是基于
ThreadLocal
實(shí)現(xiàn)的,所以多線程環(huán)境下可以安全地設(shè)置和切換數(shù)據(jù)源標(biāo)識(shí)。然而,如果有異步操作,可能會(huì)導(dǎo)致數(shù)據(jù)源信息傳遞失效,需要特別注意。
5.測(cè)試和驗(yàn)證
完成 Spring Boot 項(xiàng)目中多數(shù)據(jù)源的配置之后,需要測(cè)試項(xiàng)目是否能夠正確地實(shí)現(xiàn)讀寫分離。主要步驟包括:檢查 MySQL 主庫和從庫的服務(wù)狀態(tài)、啟動(dòng)項(xiàng)目、編寫并運(yùn)行測(cè)試類,以及驗(yàn)證主從數(shù)據(jù)庫的數(shù)據(jù)同步情況。
5.1 檢查 MySQL 主庫和從庫的服務(wù)狀態(tài)
要確保 Spring Boot 項(xiàng)目能夠連接到主從數(shù)據(jù)庫,首先需要確認(rèn) MySQL 主庫和從庫的服務(wù)狀態(tài)。如果主庫和從庫未啟動(dòng),Spring Boot 應(yīng)用將無法連接數(shù)據(jù)庫。
5.1.1 使用命令行檢查 MySQL 服務(wù)狀態(tài)
在主庫和從庫的服務(wù)器上,可以使用以下命令檢查 MySQL 服務(wù)狀態(tài):
# 檢查 MySQL 服務(wù)是否正在運(yùn)行 sudo systemctl status mysql
如果顯示 active (running)
,則表示 MySQL 服務(wù)正在運(yùn)行。
5.1.2 使用命令行啟動(dòng) MySQL 服務(wù)
如果 MySQL 服務(wù)未運(yùn)行,可以使用以下命令啟動(dòng):
# 啟動(dòng) MySQL 服務(wù) sudo systemctl start mysql
啟動(dòng)服務(wù)后,可以再次使用 status
命令檢查服務(wù)狀態(tài),確保 MySQL 服務(wù)已啟動(dòng)。
注意:如果主庫和從庫在不同的服務(wù)器上,您需要分別在主庫服務(wù)器和從庫服務(wù)器上執(zhí)行上述命令。
5.2 使用 IntelliJ IDEA 啟動(dòng) Spring Boot 項(xiàng)目
在確認(rèn) MySQL 主庫和從庫均已啟動(dòng)后,可以啟動(dòng) Spring Boot 項(xiàng)目。
5.2.1 在 IntelliJ IDEA 中啟動(dòng) Spring Boot 項(xiàng)目
- 打開項(xiàng)目:在 IntelliJ IDEA 中打開您的 Spring Boot 項(xiàng)目。
- 定位主類:在項(xiàng)目的
src/main/java
目錄中,找到主類(通常是帶有@SpringBootApplication
注解的類,比如YourApplication
類)。 - 運(yùn)行項(xiàng)目:右鍵點(diǎn)擊主類文件,選擇“Run ‘YourApplication’”(運(yùn)行
YourApplication
)選項(xiàng)。IDEA 將會(huì)在控制臺(tái)顯示啟動(dòng)日志。
5.2.2 檢查控制臺(tái)輸出
項(xiàng)目啟動(dòng)后,檢查 IDEA 控制臺(tái)的日志輸出,確保沒有報(bào)錯(cuò)信息。如果看到類似以下內(nèi)容,則說明項(xiàng)目啟動(dòng)成功:
INFO 12345 --- [main] com.example.YourApplication : Started YourApplication in 3.456 seconds (JVM running for 4.123)
5.3 編寫測(cè)試類,測(cè)試讀寫操作是否按照預(yù)期走對(duì)應(yīng)的數(shù)據(jù)源
在項(xiàng)目啟動(dòng)后,我們可以編寫測(cè)試類,通過測(cè)試 Spring Boot 項(xiàng)目中是否實(shí)現(xiàn)了主從分離(讀操作走從庫、寫操作走主庫)。
5.3.1 創(chuàng)建測(cè)試類
在項(xiàng)目的 src/test/java
目錄下,創(chuàng)建一個(gè)新的測(cè)試類,例如 UserServiceTest
,用于測(cè)試服務(wù)類中的讀寫方法。以下是測(cè)試類的示例代碼:
import com.example.service.UserService; import com.example.model.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class UserServiceTest { @Autowired private UserService userService; // 測(cè)試讀操作 @Test public void testReadOperation() { // 調(diào)用讀取用戶的方法 System.out.println("Executing read operation (should use slave database)..."); userService.getUsers(); } // 測(cè)試寫操作 @Test public void testWriteOperation() { // 創(chuàng)建一個(gè)新的用戶 User user = new User(); user.setName("Test User"); user.setEmail("testuser@example.com"); // 調(diào)用保存用戶的方法 System.out.println("Executing write operation (should use master database)..."); userService.saveUser(user); } }
代碼說明
- @SpringBootTest:該注解用于在測(cè)試類中啟動(dòng) Spring Boot 上下文。它會(huì)自動(dòng)加載配置文件和應(yīng)用上下文,確保測(cè)試類中可以注入依賴。
- @Autowired:注入
UserService
,以便在測(cè)試方法中調(diào)用getUsers
和saveUser
方法。 - @Test:每個(gè)測(cè)試方法都使用
@Test
注解,表明這是一個(gè)測(cè)試用例,測(cè)試框架會(huì)自動(dòng)運(yùn)行帶有@Test
的方法。
測(cè)試類的測(cè)試方法
testReadOperation:測(cè)試從庫的讀取操作。
- 該方法會(huì)調(diào)用
userService.getUsers()
,這應(yīng)該會(huì)使用從庫來執(zhí)行查詢操作。我們可以在日志中確認(rèn)是否成功走到了從庫。
- 該方法會(huì)調(diào)用
testWriteOperation:測(cè)試主庫的寫入操作。
- 該方法會(huì)創(chuàng)建一個(gè)新用戶并調(diào)用
userService.saveUser(user)
,應(yīng)該會(huì)使用主庫來執(zhí)行插入操作??梢栽谌罩局胁榭词欠癯晒κ褂弥鲙?。
- 該方法會(huì)創(chuàng)建一個(gè)新用戶并調(diào)用
5.3.3 在 IntelliJ IDEA 中運(yùn)行測(cè)試
在 IntelliJ IDEA 中可以通過以下步驟運(yùn)行測(cè)試類:
- 運(yùn)行單個(gè)測(cè)試方法:在
testReadOperation
或testWriteOperation
方法上右鍵點(diǎn)擊,選擇“Run ‘testReadOperation’”或“Run ‘testWriteOperation’”來運(yùn)行特定測(cè)試。 - 運(yùn)行整個(gè)測(cè)試類:在
UserServiceTest
類名上右鍵點(diǎn)擊,選擇“Run ‘UserServiceTest’”,以運(yùn)行所有測(cè)試方法。
5.3.4 檢查測(cè)試輸出
在控制臺(tái)中查看輸出信息。如果您在 DynamicDataSource
類中添加了日志信息,可以看到類似以下的日志:
Switching to data source: slave Executing read operation (should use slave database)... Switching to data source: master Executing write operation (should use master database)...
- 讀操作日志:如果
testReadOperation
的日志顯示Switching to data source: slave
,說明讀操作成功走了從庫。 - 寫操作日志:如果
testWriteOperation
的日志顯示Switching to data source: master
,說明寫操作成功走了主庫。
5.4 檢查主從數(shù)據(jù)庫的數(shù)據(jù)同步
在驗(yàn)證讀寫操作走了正確的數(shù)據(jù)源后,還需要檢查主從數(shù)據(jù)庫的數(shù)據(jù)同步情況,以確認(rèn)主庫的數(shù)據(jù)更改是否成功同步到從庫。
5.4.1 執(zhí)行寫入操作,檢查數(shù)據(jù)同步
運(yùn)行寫入測(cè)試方法:通過
testWriteOperation
或直接調(diào)用服務(wù)中的寫入方法,向主庫插入新數(shù)據(jù)。在主庫中檢查數(shù)據(jù):連接到主庫,執(zhí)行以下查詢,確認(rèn)數(shù)據(jù)是否成功插入:
SELECT * FROM your_database.users WHERE name = 'Test User';
在從庫中檢查數(shù)據(jù)同步:稍等片刻后,連接到從庫,執(zhí)行相同的查詢,檢查數(shù)據(jù)是否已同步:
SELECT * FROM your_database.users WHERE name = 'Test User';
如果從庫中也能查詢到這條記錄,則表明主庫的數(shù)據(jù)成功同步到了從庫。
5.4.2 執(zhí)行更新操作,檢查數(shù)據(jù)同步
運(yùn)行更新操作:在主庫中更新記錄的某個(gè)字段,例如通過
userService.updateUser(user)
方法更新email
字段(假設(shè)該方法存在),或直接在主庫執(zhí)行更新 SQL 語句:UPDATE your_database.users SET email = 'updateduser@example.com' WHERE name = 'Test User';
在從庫中檢查數(shù)據(jù)同步:稍等片刻后,在從庫執(zhí)行相同的查詢,確認(rèn)
email
字段是否已更新為'updateduser@example.com'
。
5.4.3 執(zhí)行刪除操作,檢查數(shù)據(jù)同步
運(yùn)行刪除操作:通過
userService.deleteUserById(userId)
方法(假設(shè)存在)或直接在主庫執(zhí)行刪除語句:DELETE FROM your_database.users WHERE name = 'Test User';
在從庫中檢查數(shù)據(jù)同步:在從庫中執(zhí)行以下查詢,確認(rèn)數(shù)據(jù)是否已同步刪除:
SELECT * FROM your_database.users WHERE name = 'Test User';
如果查詢結(jié)果為空,則表明刪除操作已成功同步到從庫。
到此這篇關(guān)于MySQL主從復(fù)制數(shù)據(jù)同步的實(shí)現(xiàn)步驟的文章就介紹到這了,更多相關(guān)MySQL 主從復(fù)制數(shù)據(jù)同步內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql 5.7.18 zip版安裝配置方法圖文教程(win7)
這篇文章主要為大家詳細(xì)介紹了win7下mysql 5.7.8 zip版安裝配置方法圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08保證MySQL與Redis數(shù)據(jù)一致性的6種實(shí)現(xiàn)方案
這篇文章將聚焦在一個(gè)非常重要且復(fù)雜的問題上:MySQL與Redis數(shù)據(jù)的一致性,當(dāng)我們?cè)趹?yīng)用中同時(shí)使用MySQL和Redis時(shí),如何保證兩者的數(shù)據(jù)一致性呢?下面就來分享幾種實(shí)用的解決方案,需要的朋友可以參考下2024-03-03MySQL?數(shù)據(jù)庫的對(duì)庫的操作及其數(shù)據(jù)類型
這篇文章主要介紹了MySQL?數(shù)據(jù)庫的對(duì)庫的操作及其數(shù)據(jù)類型,下面文字圍繞數(shù)據(jù)庫的對(duì)庫的操作及其數(shù)據(jù)類型的相關(guān)資料展開詳細(xì)介紹,需要的小伙伴可以參考一下,希望對(duì)你有所幫助2021-12-12用命令創(chuàng)建MySQL數(shù)據(jù)庫(de1)的方法
下面小編就為大家?guī)硪黄妹顒?chuàng)建MySQL數(shù)據(jù)庫(de1)的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03pymysql.err.DataError:(1264, ")異常的有效解決方法(最新推薦)
遇到pymysql.err.DataError錯(cuò)誤時(shí),錯(cuò)誤代碼1264通常指的是MySQL數(shù)據(jù)庫中的Out of range value for column錯(cuò)誤,這意味著你嘗試插入或更新的數(shù)據(jù)超過了對(duì)應(yīng)數(shù)據(jù)庫列所允許的范圍,這篇文章主要介紹了pymysql.err.DataError:(1264, ")異常的有效問題,需要的朋友可以參考下2024-05-05解讀mysql主從配置及其原理分析(Master-Slave)
在windows下配置的,后面會(huì)在Linux下配置進(jìn)行測(cè)試,需要配置mysql數(shù)據(jù)庫同步的朋友可以參考下。2011-05-05MySQL獲取當(dāng)前時(shí)間的多種方式總結(jié)
負(fù)責(zé)的項(xiàng)目中使用的是mysql數(shù)據(jù)庫,頁面上要顯示當(dāng)天所注冊(cè)人數(shù)的數(shù)量,獲取當(dāng)前的年月日,下面這篇文章主要給大家總結(jié)介紹了關(guān)于MySQL獲取當(dāng)前時(shí)間的多種方式,需要的朋友可以參考下2023-02-02