欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MySQL主從復(fù)制數(shù)據(jù)同步的實(shí)現(xiàn)步驟

 更新時(shí)間:2024年12月03日 10:11:07   作者:阿乾之銘  
MySQL主從復(fù)制是一種數(shù)據(jù)同步技術(shù),通過(guò)將數(shù)據(jù)從主數(shù)據(jù)庫(kù)服務(wù)器復(fù)制到一個(gè)或多個(gè)從數(shù)據(jù)庫(kù)服務(wù)器來(lái)實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、什么是 MySQL 的主從復(fù)制

MySQL 的主從復(fù)制(Master-Slave Replication)是一種將數(shù)據(jù)從一個(gè)主數(shù)據(jù)庫(kù)服務(wù)器(主庫(kù))復(fù)制到一個(gè)或多個(gè)從數(shù)據(jù)庫(kù)服務(wù)器(從庫(kù))的技術(shù)。主庫(kù)負(fù)責(zé)所有的數(shù)據(jù)寫(xiě)操作,從庫(kù)則通過(guò)讀取主庫(kù)的二進(jìn)制日志來(lái)同步主庫(kù)的數(shù)據(jù)變化。主從復(fù)制主要用于實(shí)現(xiàn)數(shù)據(jù)的備份、負(fù)載分擔(dān)和高可用性。

二、具體流程

1. 主庫(kù)記錄寫(xiě)操作

當(dāng)主庫(kù)執(zhí)行寫(xiě)操作(如 INSERT、UPDATE 或 DELETE)時(shí),這些操作會(huì)被記錄到二進(jìn)制日志(binlog)中。二進(jìn)制日志保存了所有數(shù)據(jù)更改的歷史記錄,這些記錄將成為從庫(kù)同步數(shù)據(jù)的來(lái)源。

  • 步驟:主庫(kù)將所有寫(xiě)操作(數(shù)據(jù)變更)記錄到二進(jìn)制日志中。

2. 從庫(kù)連接主庫(kù)并讀取二進(jìn)制日志

從庫(kù)通過(guò)一個(gè)稱(chēng)為IO 線程的進(jìn)程與主庫(kù)建立連接,開(kāi)始讀取主庫(kù)的二進(jìn)制日志。主庫(kù)會(huì)將日志內(nèi)容發(fā)送給從庫(kù)的 IO 線程。

  • 步驟:從庫(kù)的 IO 線程連接主庫(kù),持續(xù)接收主庫(kù)二進(jìn)制日志的最新內(nèi)容。

3. 從庫(kù)將二進(jìn)制日志寫(xiě)入中繼日志

從庫(kù)的 IO 線程將接收到的主庫(kù)二進(jìn)制日志復(fù)制到從庫(kù)的**中繼日志(relay log)**中。中繼日志在從庫(kù)本地保存了一份主庫(kù)數(shù)據(jù)變更的副本,供從庫(kù)執(zhí)行使用。

  • 步驟:從庫(kù)的 IO 線程將接收的日志寫(xiě)入中繼日志,為數(shù)據(jù)變更做好準(zhǔn)備。

4. 從庫(kù)應(yīng)用中繼日志,執(zhí)行數(shù)據(jù)同步

從庫(kù)的另一個(gè)線程(稱(chēng)為SQL 線程)負(fù)責(zé)讀取中繼日志中的內(nèi)容,并逐條在從庫(kù)上執(zhí)行,以實(shí)現(xiàn)數(shù)據(jù)的同步。每當(dāng)主庫(kù)有新的數(shù)據(jù)變更,從庫(kù)都會(huì)從中繼日志中獲取并執(zhí)行這些變更,從而保持與主庫(kù)的數(shù)據(jù)一致。

  • 步驟:從庫(kù)的 SQL 線程從中繼日志中讀取并執(zhí)行記錄的操作,逐步更新從庫(kù)數(shù)據(jù)。

5. 持續(xù)復(fù)制和同步

整個(gè)主從復(fù)制過(guò)程是一個(gè)持續(xù)的流程,只要主庫(kù)有新的數(shù)據(jù)變更,從庫(kù)就會(huì)自動(dòng)獲取并執(zhí)行對(duì)應(yīng)的更改,從而保持與主庫(kù)數(shù)據(jù)的一致性。主從復(fù)制會(huì)持續(xù)保持同步,以確保從庫(kù)能夠?qū)崟r(shí)或接近實(shí)時(shí)地反映主庫(kù)的最新數(shù)據(jù)。

  • 步驟:主庫(kù)的所有寫(xiě)操作都會(huì)記錄到日志中,實(shí)時(shí)同步到從庫(kù);從庫(kù)持續(xù)讀取并執(zhí)行日志內(nèi)容,更新自身數(shù)據(jù)。

三、作用和好處

  • 讀寫(xiě)分離,提高性能

    • 減輕主庫(kù)壓力:將大量的讀操作分散到從庫(kù),主庫(kù)主要負(fù)責(zé)寫(xiě)操作,大大降低了主庫(kù)的負(fù)載壓力。
    • 提升并發(fā)處理能力:增加從庫(kù)的數(shù)量可以提升系統(tǒng)的讀并發(fā)能力,處理更多用戶的并發(fā)請(qǐng)求,提升系統(tǒng)整體性能。
  • 高可用性和容錯(cuò)性

    • 數(shù)據(jù)冗余與容災(zāi):主從復(fù)制提供了數(shù)據(jù)的實(shí)時(shí)備份,從庫(kù)可以在主庫(kù)發(fā)生故障時(shí)快速接管服務(wù),提高系統(tǒng)的可用性。
    • 故障切換:當(dāng)主庫(kù)出現(xiàn)故障時(shí),可以臨時(shí)將從庫(kù)升級(jí)為主庫(kù),保障服務(wù)持續(xù)運(yùn)行。
  • 負(fù)載均衡

    • 分擔(dān)查詢(xún)壓力:多個(gè)從庫(kù)可以共同承擔(dān)讀取請(qǐng)求,通過(guò)負(fù)載均衡算法(如輪詢(xún)、隨機(jī)等)分配請(qǐng)求,提升系統(tǒng)的穩(wěn)定性。
    • 避免單點(diǎn)瓶頸:讀操作分散到不同的從庫(kù)上執(zhí)行,避免了單一數(shù)據(jù)庫(kù)因訪問(wèn)量過(guò)大而成為系統(tǒng)瓶頸。
  • 擴(kuò)展性

    • 水平擴(kuò)展:根據(jù)業(yè)務(wù)增長(zhǎng),靈活增加從庫(kù)數(shù)量,滿足性能需求,而無(wú)需對(duì)主庫(kù)進(jìn)行大幅度改造。
    • 彈性擴(kuò)展:可以根據(jù)實(shí)際流量,增加或減少?gòu)膸?kù),做到靈活應(yīng)對(duì)負(fù)載變化。
  • 數(shù)據(jù)備份和安全性

    • 數(shù)據(jù)保護(hù):從庫(kù)可以用于數(shù)據(jù)備份和容災(zāi),防止主庫(kù)故障導(dǎo)致的數(shù)據(jù)丟失。
    • 快速恢復(fù):在數(shù)據(jù)意外丟失或損壞時(shí),可以通過(guò)從庫(kù)恢復(fù)主庫(kù)的數(shù)據(jù)。

四、在實(shí)際應(yīng)用中的場(chǎng)景與示例

1. 電商平臺(tái)

在電商平臺(tái)中,用戶的瀏覽和查詢(xún)操作會(huì)產(chǎn)生大量的讀請(qǐng)求,而訂單創(chuàng)建、支付等操作會(huì)產(chǎn)生寫(xiě)請(qǐng)求。通過(guò)主從復(fù)制的讀寫(xiě)分離:

  • 查詢(xún)庫(kù)存、商品詳情等高頻率的讀操作可以由從庫(kù)承擔(dān),減輕主庫(kù)壓力。
  • 用戶下單、支付等寫(xiě)操作由主庫(kù)負(fù)責(zé),確保數(shù)據(jù)的完整性和一致性。

2. 社交媒體或內(nèi)容網(wǎng)站

在社交媒體應(yīng)用中,大量的內(nèi)容瀏覽和搜索會(huì)產(chǎn)生大量的讀操作,而發(fā)布、點(diǎn)贊等則是寫(xiě)操作。通過(guò)主從復(fù)制,平臺(tái)可以:

  • 使用從庫(kù)來(lái)承擔(dān)瀏覽、查詢(xún)等操作,確保高并發(fā)下的快速響應(yīng)。
  • 將寫(xiě)操作指向主庫(kù),確保用戶發(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ù)庫(kù)系統(tǒng)上,完成主從數(shù)據(jù)庫(kù)的配置管理,實(shí)現(xiàn)自動(dòng)的讀寫(xiě)分離。具體來(lái)說(shuō),就是通過(guò)多數(shù)據(jù)源配置,讓 Spring Boot 自動(dòng)識(shí)別是寫(xiě)請(qǐng)求還是讀請(qǐng)求,并將寫(xiě)請(qǐng)求發(fā)送到主庫(kù),讀請(qǐng)求發(fā)送到從庫(kù)。

集成的關(guān)鍵要素:

  • 多數(shù)據(jù)源配置:在 Spring Boot 中配置主數(shù)據(jù)庫(kù)和從數(shù)據(jù)庫(kù)的連接。
  • 動(dòng)態(tài)數(shù)據(jù)源路由:在應(yīng)用層面實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)選擇,讀操作使用從庫(kù),寫(xiě)操作使用主庫(kù)。
  • 讀寫(xiě)分離注解或切面:利用自定義注解或 AOP 切面控制數(shù)據(jù)源的切換。

1.配置 MySQL 的主從復(fù)制環(huán)境

配置 MySQL 的主從復(fù)制環(huán)境是實(shí)現(xiàn) MySQL 主從復(fù)制數(shù)據(jù)同步的第一步。主要步驟包括設(shè)置主庫(kù)(Master)和從庫(kù)(Slave),并驗(yàn)證主從復(fù)制的成功。

1.1 設(shè)置主庫(kù)(Master)

配置主庫(kù)是主從復(fù)制的第一步。主庫(kù)負(fù)責(zé)記錄數(shù)據(jù)變更并將其傳遞給從庫(kù)。

1.1.1 修改主庫(kù)的配置文件

在主庫(kù)的 MySQL 配置文件中(通常位于 /etc/my.cnf 或 /etc/mysql/my.cnf),需要啟用二進(jìn)制日志并為主庫(kù)設(shè)置一個(gè)唯一的 server-id。在 [mysqld] 部分添加如下配置:

[mysqld]
server-id=1                 # 主庫(kù)的唯一 ID
log-bin=mysql-bin           # 啟用二進(jìn)制日志,主從復(fù)制依賴(lài)于此
binlog-do-db=your_database  # 需要同步的數(shù)據(jù)庫(kù)名稱(chēng),多個(gè)數(shù)據(jù)庫(kù)可添加多行
  • server-id:用于標(biāo)識(shí)每個(gè) MySQL 實(shí)例的唯一標(biāo)識(shí)符,主庫(kù)的 server-id 一般設(shè)置為 1。
  • log-bin:?jiǎn)⒂枚M(jìn)制日志,這是主從復(fù)制的基礎(chǔ)。
  • binlog-do-db:指定需要同步的數(shù)據(jù)庫(kù)(多個(gè)數(shù)據(jù)庫(kù)可以多行設(shè)置)。

注意:如果需要同步多個(gè)數(shù)據(jù)庫(kù),可以多次添加 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ù)制的用戶

在主庫(kù)中創(chuàng)建一個(gè)用于復(fù)制的用戶,并授予 REPLICATION SLAVE 權(quán)限。這個(gè)用戶用于從庫(kù)連接主庫(kù)并進(jìn)行數(shù)據(jù)同步。

在主庫(kù)的 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 訪問(wèn)。如果只允許特定從庫(kù)連接,可以用從庫(kù)的 IP 地址代替 %。

1.1.4 獲取主庫(kù)的二進(jìn)制日志信息

為了讓從庫(kù)知道從何處開(kāi)始復(fù)制數(shù)據(jù),主庫(kù)需要提供當(dāng)前的二進(jìn)制日志位置??梢酝ㄟ^(guò)以下命令查看:

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)制日志的偏移量,從該位置開(kāi)始讀取數(shù)據(jù)。

建議:在生產(chǎn)環(huán)境中,執(zhí)行 SHOW MASTER STATUS; 之前,可以先執(zhí)行 FLUSH TABLES WITH READ LOCK; 來(lái)鎖定表,防止數(shù)據(jù)寫(xiě)入導(dǎo)致的數(shù)據(jù)不一致。獲取信息后再執(zhí)行 UNLOCK TABLES; 解鎖。

1.2 設(shè)置從庫(kù)(Slave)

配置從庫(kù)是主從復(fù)制的第二步。配置從庫(kù)連接到主庫(kù)并開(kāi)始同步數(shù)據(jù)。

1.2.1 修改從庫(kù)的配置文件

在從庫(kù)的 MySQL 配置文件中(通常是 /etc/my.cnf 或 /etc/mysql/my.cnf),設(shè)置一個(gè)唯一的 server-id 并配置中繼日志和只讀模式。 在 [mysqld] 部分添加以下配置:

[mysqld]
server-id=2                 # 從庫(kù)的唯一 ID,不能與主庫(kù)或其他從庫(kù)相同
relay-log=relay-log         # 設(shè)置中繼日志文件名前綴
read-only=1                 # 設(shè)置只讀模式,防止誤操作
  • server-id:從庫(kù)的唯一標(biāo)識(shí)符,每個(gè)從庫(kù)的 server-id 也需要是唯一的,通常從 2 開(kāi)始。
  • relay-log:指定中繼日志的前綴,用于存儲(chǔ)從主庫(kù)同步的數(shù)據(jù)。
  • read-only:開(kāi)啟只讀模式,防止意外寫(xiě)入。此模式下,擁有 SUPER 權(quán)限的用戶仍可以寫(xiě)入數(shù)據(jù)。

1.2.2 重啟 MySQL 服務(wù)

修改配置文件后,重啟從庫(kù)的 MySQL 服務(wù)以應(yīng)用配置:

# Linux 系統(tǒng)
sudo systemctl restart mysqld

# Windows 系統(tǒng)
net stop mysql
net start mysql

1.2.3 配置從庫(kù)連接到主庫(kù)

在從庫(kù)的 MySQL 中,配置主庫(kù)信息以便開(kāi)始復(fù)制。需要用到在主庫(kù)上記錄的 File 和 Position 值。

CHANGE MASTER TO
    MASTER_HOST='主庫(kù)的 IP 地址',
    MASTER_USER='replica_user',
    MASTER_PASSWORD='password',
    MASTER_LOG_FILE='mysql-bin.000001',  -- 主庫(kù)的 File 值
    MASTER_LOG_POS=154;                  -- 主庫(kù)的 Position 值
  • MASTER_HOST:主庫(kù)的 IP 地址。
  • MASTER_USER:在主庫(kù)上創(chuàng)建的用于復(fù)制的用戶(如 replica_user)。
  • MASTER_PASSWORD:復(fù)制用戶的密碼。
  • MASTER_LOG_FILE:主庫(kù)的二進(jìn)制日志文件名。
  • MASTER_LOG_POS:二進(jìn)制日志的位置偏移量。

1.2.4 啟動(dòng)復(fù)制進(jìn)程

配置完成后,啟動(dòng)從庫(kù)的復(fù)制進(jìn)程以開(kāi)始從主庫(kù)復(fù)制數(shù)據(jù):

START SLAVE;

1.2.5 查看從庫(kù)的復(fù)制狀態(tài)

運(yùn)行以下命令檢查從庫(kù)的狀態(tài),確認(rèn)從庫(kù)已成功連接到主庫(kù)并開(kāi)始復(fù)制數(shù)據(jù):

SHOW SLAVE STATUS\G
  • Slave_IO_Running:應(yīng)為 Yes,表示從庫(kù)的 IO 線程正在讀取主庫(kù)的日志。
  • Slave_SQL_Running:應(yīng)為 Yes,表示從庫(kù)的 SQL 線程正在執(zhí)行主庫(kù)傳遞的日志。
  • 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ò)誤提示排查問(wèn)題。

1.3 驗(yàn)證主從復(fù)制是否成功

在配置完成后,驗(yàn)證主從復(fù)制的成功性。確保主庫(kù)的寫(xiě)操作能夠被從庫(kù)正確同步。

1.3.1 在主庫(kù)上創(chuàng)建測(cè)試數(shù)據(jù)

在主庫(kù)上選擇用于復(fù)制的數(shù)據(jù)庫(kù),并創(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 在從庫(kù)上檢查數(shù)據(jù)同步情況

在從庫(kù)上選擇相同的數(shù)據(jù)庫(kù),查詢(xún) test_table 表,確認(rèn)是否同步了主庫(kù)的數(shù)據(jù):

USE your_database;
SELECT * FROM test_table;
  • 如果可以看到 Test Data,則表示主從復(fù)制配置成功并且工作正常。

1.3.3 驗(yàn)證實(shí)時(shí)同步效果

在主庫(kù)上繼續(xù)插入或更新數(shù)據(jù),再次在從庫(kù)上查詢(xún),驗(yàn)證數(shù)據(jù)是否能夠及時(shí)同步。主從復(fù)制一般情況下會(huì)立即同步,延遲較小。

1.4 故障排查

在配置主從復(fù)制的過(guò)程中,可能會(huì)遇到以下常見(jiàn)問(wèn)題:

1 從庫(kù)無(wú)法連接到主庫(kù)

  • 檢查網(wǎng)絡(luò)連接:確保主庫(kù)的防火墻允許從庫(kù)的 IP 地址訪問(wèn) MySQL 的 3306 端口。

2 權(quán)限問(wèn)題

  • 用戶權(quán)限:確保在主庫(kù)上創(chuàng)建的用戶擁有 REPLICATION SLAVE 權(quán)限。

3 主從版本兼容性問(wèn)題

  • 版本兼容性:確保主庫(kù)和從庫(kù)的 MySQL 版本相互兼容,推薦使用相同的版本。

4 日志位置不正確

  • 文件和位置錯(cuò)誤:如果配置的 File 和 Position 不正確,可以重新設(shè)置主從復(fù)制位置。

2.在 Spring Boot 項(xiàng)目中配置多數(shù)據(jù)源

配置多數(shù)據(jù)源是實(shí)現(xiàn)主從復(fù)制的重要步驟。通過(guò)多數(shù)據(jù)源配置,Spring Boot 應(yīng)用可以自動(dòng)區(qū)分并選擇主庫(kù)或從庫(kù),從而實(shí)現(xiàn)讀寫(xiě)分離。這一步的具體操作包括添加依賴(lài)、配置數(shù)據(jù)源信息,以及配置數(shù)據(jù)源路由以實(shí)現(xiàn)自動(dòng)選擇主庫(kù)或從庫(kù)。以下是詳細(xì)的分步講解。

2.1 添加必要的依賴(lài)

在使用 Spring Data JPA 和 MySQL 多數(shù)據(jù)源時(shí),需要添加以下依賴(lài)項(xiàng):

  • MySQL 驅(qū)動(dòng):支持連接 MySQL 數(shù)據(jù)庫(kù)。
  • Spring Data JPA:提供 JPA 支持,簡(jiǎn)化數(shù)據(jù)庫(kù)操作。
  • HikariCP 連接池:Spring Boot 默認(rèn)的連接池,適合高并發(fā)環(huán)境且支持多數(shù)據(jù)源配置。

在項(xiàng)目的 pom.xml 中添加以下依賴(lài):

<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 在配置文件中定義主庫(kù)和從庫(kù)的數(shù)據(jù)源信息

接下來(lái),需要在 application.properties 中定義主庫(kù)和從庫(kù)的連接信息。主庫(kù)通常用于寫(xiě)操作,從庫(kù)用于讀操作。

application.properties 文件內(nèi)容

# 主庫(kù)配置
spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.master.url=jdbc:mysql://主庫(kù)IP地址:3306/your_database?useSSL=false&characterEncoding=utf8
spring.datasource.master.username=主庫(kù)用戶名
spring.datasource.master.password=主庫(kù)密碼

# 從庫(kù)配置
spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slave.url=jdbc:mysql://從庫(kù)IP地址:3306/your_database?useSSL=false&characterEncoding=utf8
spring.datasource.slave.username=從庫(kù)用戶名
spring.datasource.slave.password=從庫(kù)密碼

說(shuō)明

  • spring.datasource.master:主庫(kù)連接信息,用于寫(xiě)操作。
  • spring.datasource.slave:從庫(kù)連接信息,用于讀操作。
  • driver-class-name:MySQL 驅(qū)動(dòng)類(lèi)名。
  • url:數(shù)據(jù)庫(kù)連接 URL,其中包含數(shù)據(jù)庫(kù)的 IP 地址和數(shù)據(jù)庫(kù)名稱(chēng)。
  • username 和 password:數(shù)據(jù)庫(kù)的用戶名和密碼。

2.3 創(chuàng)建數(shù)據(jù)源配置類(lèi),配置主從數(shù)據(jù)源

在項(xiàng)目中添加一個(gè)配置類(lèi),用于定義主庫(kù)和從庫(kù)數(shù)據(jù)源,并通過(guò)一個(gè)動(dòng)態(tài)數(shù)據(jù)源實(shí)現(xiàn)自動(dòng)選擇主庫(kù)或從庫(kù)。

2.3.1 配置主庫(kù)和從庫(kù)的數(shù)據(jù)源

首先,我們需要在配置類(lèi)中定義兩個(gè)數(shù)據(jù)源,即主庫(kù)和從庫(kù)。主庫(kù)主要用于寫(xiě)操作,從庫(kù)用于讀操作。

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 {

    // 定義主庫(kù)數(shù)據(jù)源
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    // 定義從庫(kù)數(shù)據(jù)源
    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }
}

說(shuō)明

  • @ConfigurationProperties:這個(gè)注解將 application.properties 中 spring.datasource.master 和 spring.datasource.slave 配置的信息綁定到 masterDataSource 和 slaveDataSource 上。這樣,Spring Boot 會(huì)自動(dòng)讀取配置文件中的主從庫(kù)信息,并將它們分別注入到兩個(gè)數(shù)據(jù)源對(duì)象中。

  • DataSourceBuilder:這是 Spring 提供的一個(gè)工具類(lèi),通過(guò)它可以根據(jù)配置文件構(gòu)建 DataSource 對(duì)象。DataSource 是與數(shù)據(jù)庫(kù)連接的核心組件,主要用于管理數(shù)據(jù)庫(kù)連接、連接池等信息。

2.3.2 動(dòng)態(tài)數(shù)據(jù)源路由

為了實(shí)現(xiàn)主從分離,我們需要根據(jù)請(qǐng)求自動(dòng)選擇主庫(kù)或從庫(kù)的數(shù)據(jù)源。AbstractRoutingDataSource 是 Spring 提供的一個(gè)抽象類(lèi),可以根據(jù)用戶定義的路由規(guī)則動(dòng)態(tài)選擇數(shù)據(jù)源。

創(chuàng)建 DynamicDataSource 類(lèi):該類(lèi)繼承自 AbstractRoutingDataSource,通過(guò)設(shè)置鍵值映射來(lái)實(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ù)源(主庫(kù)或從庫(kù))
    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ù)源,通過(guò)傳入 master 或 slave 來(lái)指定主庫(kù)或從庫(kù)。

  • clearDataSource:用于清除當(dāng)前線程的數(shù)據(jù)源,避免數(shù)據(jù)源信息在線程之間混淆。

  • determineCurrentLookupKey:這是 AbstractRoutingDataSource 提供的方法,它會(huì)在每次數(shù)據(jù)庫(kù)操作時(shí)調(diào)用。根據(jù) contextHolder 中的數(shù)據(jù)源標(biāo)識(shí),選擇主庫(kù)或從庫(kù)。

2.3.3 將主從數(shù)據(jù)源注入到動(dòng)態(tài)數(shù)據(jù)源

我們需要將主庫(kù)和從庫(kù)的數(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);      // 將主庫(kù)和從庫(kù)的數(shù)據(jù)源加入路由

    return dynamicDataSource;
}

代碼詳解

  • @Qualifier:指定 masterDataSource 和 slaveDataSource,通過(guò)依賴(lài)注入的方式將主庫(kù)和從庫(kù)的數(shù)據(jù)源傳入 dynamicDataSource

  • targetDataSources:將 master 和 slave 作為鍵,分別映射到 masterDataSource 和 slaveDataSourceDynamicDataSource 會(huì)通過(guò) determineCurrentLookupKey 方法自動(dòng)選擇對(duì)應(yīng)的數(shù)據(jù)源。

  • setDefaultTargetDataSource:設(shè)置默認(rèn)的數(shù)據(jù)源。在沒(méi)有指定數(shù)據(jù)源的情況下,系統(tǒng)會(huì)使用默認(rèn)數(shù)據(jù)源(一般為主庫(kù))。

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í)體類(lèi)包名
    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í)體類(lèi)所在的包路徑,JPA 會(huì)掃描該包中的實(shí)體類(lèi),進(jìn)行數(shù)據(jù)庫(kù)操作的映射。

  • HibernateJpaVendorAdapter:設(shè)置 JPA 的供應(yīng)商適配器。這里我們使用 Hibernate 作為 JPA 的實(shí)現(xiàn),它提供了 Hibernate 特有的優(yōu)化和配置支持。

通過(guò)配置 EntityManagerFactory 使用 dynamicDataSource,我們可以讓 JPA 在操作數(shù)據(jù)庫(kù)時(shí)自動(dòng)根據(jù)業(yè)務(wù)需要切換到主庫(kù)或從庫(kù),從而實(shí)現(xiàn)主從分離和讀寫(xiě)分離。

2.3.5 配置事務(wù)管理器

Spring Data JPA 默認(rèn)需要一個(gè)事務(wù)管理器來(lái)管理事務(wù)。我們需要為動(dòng)態(tài)數(shù)據(jù)源配置一個(gè) JpaTransactionManager,以確保事務(wù)操作可以正確地應(yīng)用于當(dāng)前選擇的數(shù)據(jù)源(主庫(kù)或從庫(kù))。

@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ù)庫(kù)操作的 ACID 特性(原子性、一致性、隔離性、持久性)。

  • entityManagerFactory.getObject():將 entityManagerFactory(配置了動(dòng)態(tài)數(shù)據(jù)源的實(shí)體管理器工廠)傳遞給 JpaTransactionManager。這樣,事務(wù)管理器會(huì)根據(jù)動(dòng)態(tài)數(shù)據(jù)源路由來(lái)管理事務(wù),確保事務(wù)一致性。

3.實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)切換

為了實(shí)現(xiàn)數(shù)據(jù)庫(kù)的主從切換,使得 Spring Boot 項(xiàng)目可以根據(jù)操作類(lèi)型自動(dòng)選擇主庫(kù)或從庫(kù),我們需要實(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í)在特定方法或類(lèi)上指定的數(shù)據(jù)源類(lèi)型(如主庫(kù) master 或從庫(kù) slave)。有了這個(gè)注解之后,我們可以在代碼中靈活地指定哪些操作使用主庫(kù),哪些操作使用從庫(kù)。

package com.example.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE}) // 可以用在方法或類(lèi)上
@Retention(RetentionPolicy.RUNTIME)             // 在運(yùn)行時(shí)保留,便于通過(guò)反射獲取
@Documented
public @interface DataSource {
    String value() default "master"; // 默認(rèn)使用主庫(kù)
}

注解參數(shù)說(shuō)明

  • @Target:定義注解的使用位置,ElementType.METHOD 表示可以作用于方法,ElementType.TYPE 表示可以作用于類(lèi)。

  • @Retention:指定注解的保留策略為 RUNTIME,即該注解會(huì)保留到運(yùn)行時(shí),并且可以通過(guò)反射獲取,這樣切面類(lèi)可以在運(yùn)行時(shí)識(shí)別注解。

  • value:注解的屬性,用來(lái)指定數(shù)據(jù)源類(lèi)型,默認(rèn)為 "master",表示主庫(kù)。我們可以在使用注解時(shí),通過(guò)設(shè)置 @DataSource("slave") 來(lái)指定從庫(kù)。

3.2 創(chuàng)建 DataSourceAspect 切面類(lèi)(根據(jù)注解動(dòng)態(tài)切換數(shù)據(jù)源)

AOP(面向切面編程)可以在方法調(diào)用前后動(dòng)態(tài)地切入代碼邏輯。在這里,我們編寫(xiě)一個(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 注解的方法或類(lèi)
    @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)使用主庫(kù)
        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)) {
            // 如果方法上沒(méi)有注解,則讀取類(lèi)上的 @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();
    }
}

切面類(lèi)代碼詳解

  • @Pointcut:定義切點(diǎn) dataSourcePointCut,用于匹配所有帶有 @DataSource 注解的方法或類(lèi)。這意味著我們可以在方法上、類(lèi)上使用 @DataSource 來(lái)控制數(shù)據(jù)源選擇。

  • @Before:在目標(biāo)方法執(zhí)行之前觸發(fā) before 方法。

    • MethodSignature:通過(guò) MethodSignature 可以獲取方法的注解。

    • dataSource:默認(rèn)值為 "master"(主庫(kù))。首先檢查方法上的 @DataSource 注解,如果沒(méi)有找到,再檢查類(lèi)上的 @DataSource 注解。

    • DynamicDataSource.setDataSource(dataSource):根據(jù)注解值設(shè)置當(dāng)前線程使用的數(shù)據(jù)源(主庫(kù)或從庫(kù))。這使得后續(xù)的數(shù)據(jù)庫(kù)操作將根據(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ù)代碼中,可以在需要使用主庫(kù)或從庫(kù)的業(yè)務(wù)方法上添加 @DataSource 注解。默認(rèn)情況下,@DataSource 注解的 value 屬性是 "master",表示主庫(kù)。我們可以在查詢(xún)類(lèi)方法上標(biāo)記 @DataSource("slave") 以使用從庫(kù),從而實(shí)現(xiàn)讀寫(xiě)分離。

4.1.1.在服務(wù)層使用 @DataSource 注解

假設(shè)我們有一個(gè) UserService 服務(wù)類(lèi),該類(lèi)提供了查詢(xún)用戶列表和保存用戶的功能。我們可以通過(guò) @DataSource 注解指定使用的數(shù)據(jù)庫(kù)。

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;

    // 使用從庫(kù)(slave)進(jìn)行讀取操作
    @DataSource("slave")
    public List<User> getUsers() {
        // 數(shù)據(jù)庫(kù)查詢(xún)操作,這里會(huì)通過(guò)切面選擇從庫(kù)
        return userRepository.findAll();
    }

    // 使用主庫(kù)(master)進(jìn)行寫(xiě)入操作
    @DataSource("master")
    public void saveUser(User user) {
        // 數(shù)據(jù)庫(kù)寫(xiě)入操作,這里會(huì)通過(guò)切面選擇主庫(kù)
        userRepository.save(user);
    }
}

示例說(shuō)明

  • getUsers 方法:由于標(biāo)注了 @DataSource("slave") 注解,因此在執(zhí)行 getUsers 方法時(shí)會(huì)選擇從庫(kù)作為當(dāng)前數(shù)據(jù)源,從而使查詢(xún)操作通過(guò)從庫(kù)完成,減輕主庫(kù)壓力。

  • saveUser 方法:標(biāo)注了 @DataSource("master") 注解,因此在執(zhí)行 saveUser 方法時(shí)會(huì)選擇主庫(kù)作為當(dāng)前數(shù)據(jù)源,從而保證數(shù)據(jù)寫(xiě)入操作在主庫(kù)上進(jìn)行,確保數(shù)據(jù)的一致性。

4.1.2.在 DAO 層(數(shù)據(jù)持久層)使用 @DataSource 注解

假設(shè)我們將數(shù)據(jù)源控制進(jìn)一步細(xì)化到 DAO 層。例如,在 UserRepository 中的查詢(xún)和保存方法上分別指定數(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> {

    // 使用從庫(kù)(slave)進(jìn)行讀取操作
    @DataSource("slave")
    List<User> findByName(String name);

    // 使用主庫(kù)(master)進(jìn)行寫(xiě)入操作
    @DataSource("master")
    User save(User user);
}

示例說(shuō)明

  • findByName 方法:該方法標(biāo)注了 @DataSource("slave"),因此在調(diào)用 findByName 時(shí),數(shù)據(jù)查詢(xún)操作將通過(guò)從庫(kù)完成。

  • save 方法:該方法標(biāo)注了 @DataSource("master"),確保數(shù)據(jù)寫(xiě)入操作始終通過(guò)主庫(kù)進(jìn)行,保證了數(shù)據(jù)的完整性。

注意:將 @DataSource 注解放在服務(wù)層和 DAO 層都會(huì)生效。實(shí)際應(yīng)用中可以根據(jù)業(yè)務(wù)邏輯的復(fù)雜程度來(lái)決定在哪一層實(shí)現(xiàn)數(shù)據(jù)源的切換。

4.2 確保讀操作走從庫(kù),寫(xiě)操作走主庫(kù)

通過(guò)在業(yè)務(wù)方法中使用 @DataSource 注解,可以實(shí)現(xiàn)以下的邏輯:

1.讀操作走從庫(kù):在查詢(xún)數(shù)據(jù)的方法上添加 @DataSource("slave"),使這些方法通過(guò)從庫(kù)執(zhí)行。

  • 從庫(kù)通常用于處理讀操作。這樣可以分擔(dān)主庫(kù)的負(fù)載,提升系統(tǒng)的讀取性能。
  • 因?yàn)閺膸?kù)的數(shù)據(jù)來(lái)自主庫(kù)的復(fù)制,存在一定延遲,所以一般不用于強(qiáng)一致性要求的場(chǎng)景,而適合對(duì)實(shí)時(shí)性要求較低的查詢(xún)場(chǎng)景。

2.寫(xiě)操作走主庫(kù):在插入、更新、刪除數(shù)據(jù)的方法上添加 @DataSource("master"),確保這些操作通過(guò)主庫(kù)執(zhí)行。

  • 主庫(kù)是數(shù)據(jù)的源頭,負(fù)責(zé)處理寫(xiě)入、更新等操作,以確保數(shù)據(jù)的準(zhǔn)確性和一致性。
  • 這樣可以避免因?yàn)閺?fù)制延遲導(dǎo)致的數(shù)據(jù)不一致問(wèn)題。

示例:在 ProductService 中實(shí)現(xiàn)讀寫(xiě)分離

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;

    // 使用從庫(kù)讀取產(chǎn)品列表
    @DataSource("slave")
    public List<Product> getAllProducts() {
        // 從庫(kù)執(zhí)行查詢(xún)
        return productRepository.findAll();
    }

    // 使用主庫(kù)保存產(chǎn)品信息
    @DataSource("master")
    public void addProduct(Product product) {
        // 主庫(kù)執(zhí)行插入操作
        productRepository.save(product);
    }

    // 更新產(chǎn)品信息,使用主庫(kù)
    @DataSource("master")
    public void updateProduct(Product product) {
        // 主庫(kù)執(zhí)行更新操作
        productRepository.save(product);
    }

    // 刪除產(chǎn)品信息,使用主庫(kù)
    @DataSource("master")
    public void deleteProductById(Long productId) {
        // 主庫(kù)執(zhí)行刪除操作
        productRepository.deleteById(productId);
    }
}

代碼解釋?zhuān)?/strong>

  • getAllProducts() 方法

    • 使用 @DataSource("slave") 注解,從庫(kù)將執(zhí)行該方法中的查詢(xún)操作。
    • 適用于讀取類(lèi)操作,減輕主庫(kù)壓力。
  • addProduct()、updateProduct() 和 deleteProductById() 方法

    • 這些方法使用 @DataSource("master") 注解,因此會(huì)走主庫(kù)。
    • 適用于寫(xiě)操作(新增、修改、刪除),確保數(shù)據(jù)的強(qiáng)一致性和實(shí)時(shí)性。

4.3 注意事項(xiàng)

在實(shí)現(xiàn)讀寫(xiě)分離時(shí),以下幾點(diǎn)需要注意:

  • 主從數(shù)據(jù)一致性:從庫(kù)的數(shù)據(jù)源于主庫(kù)的復(fù)制,所以存在一定的延遲。對(duì)于不允許數(shù)據(jù)延遲的操作,建議強(qiáng)制使用主庫(kù)。比如訂單支付或庫(kù)存更新等操作,通常對(duì)實(shí)時(shí)性要求較高,應(yīng)直接走主庫(kù)。

  • 事務(wù)管理:在事務(wù)中進(jìn)行讀操作,可能會(huì)導(dǎo)致數(shù)據(jù)源切換失效,因?yàn)樵谑聞?wù)中默認(rèn)會(huì)使用主庫(kù)。這種情況下,可以使用 @Transactional(readOnly = true) 標(biāo)記方法為只讀事務(wù),讓 Spring 在事務(wù)中仍使用從庫(kù)。

  • 線程安全:因?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)讀寫(xiě)分離。主要步驟包括:檢查 MySQL 主庫(kù)和從庫(kù)的服務(wù)狀態(tài)、啟動(dòng)項(xiàng)目、編寫(xiě)并運(yùn)行測(cè)試類(lèi),以及驗(yàn)證主從數(shù)據(jù)庫(kù)的數(shù)據(jù)同步情況。

5.1 檢查 MySQL 主庫(kù)和從庫(kù)的服務(wù)狀態(tài)

要確保 Spring Boot 項(xiàng)目能夠連接到主從數(shù)據(jù)庫(kù),首先需要確認(rèn) MySQL 主庫(kù)和從庫(kù)的服務(wù)狀態(tài)。如果主庫(kù)和從庫(kù)未啟動(dòng),Spring Boot 應(yīng)用將無(wú)法連接數(shù)據(jù)庫(kù)。

5.1.1 使用命令行檢查 MySQL 服務(wù)狀態(tài)

在主庫(kù)和從庫(kù)的服務(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)。

注意:如果主庫(kù)和從庫(kù)在不同的服務(wù)器上,您需要分別在主庫(kù)服務(wù)器和從庫(kù)服務(wù)器上執(zhí)行上述命令。

5.2 使用 IntelliJ IDEA 啟動(dòng) Spring Boot 項(xiàng)目

在確認(rèn) MySQL 主庫(kù)和從庫(kù)均已啟動(dòng)后,可以啟動(dòng) Spring Boot 項(xiàng)目。

5.2.1 在 IntelliJ IDEA 中啟動(dòng) Spring Boot 項(xiàng)目

  • 打開(kāi)項(xiàng)目:在 IntelliJ IDEA 中打開(kāi)您的 Spring Boot 項(xiàng)目。
  • 定位主類(lèi):在項(xiàng)目的 src/main/java 目錄中,找到主類(lèi)(通常是帶有 @SpringBootApplication 注解的類(lèi),比如 YourApplication 類(lèi))。
  • 運(yùn)行項(xiàng)目:右鍵點(diǎn)擊主類(lèi)文件,選擇“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)的日志輸出,確保沒(méi)有報(bào)錯(cuò)信息。如果看到類(lèi)似以下內(nèi)容,則說(shuō)明項(xiàng)目啟動(dòng)成功:

INFO 12345 --- [main] com.example.YourApplication       : Started YourApplication in 3.456 seconds (JVM running for 4.123)

5.3 編寫(xiě)測(cè)試類(lèi),測(cè)試讀寫(xiě)操作是否按照預(yù)期走對(duì)應(yīng)的數(shù)據(jù)源

在項(xiàng)目啟動(dòng)后,我們可以編寫(xiě)測(cè)試類(lèi),通過(guò)測(cè)試 Spring Boot 項(xiàng)目中是否實(shí)現(xiàn)了主從分離(讀操作走從庫(kù)、寫(xiě)操作走主庫(kù))。

5.3.1 創(chuàng)建測(cè)試類(lèi)

在項(xiàng)目的 src/test/java 目錄下,創(chuàng)建一個(gè)新的測(cè)試類(lèi),例如 UserServiceTest,用于測(cè)試服務(wù)類(lèi)中的讀寫(xiě)方法。以下是測(cè)試類(lèi)的示例代碼:

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è)試寫(xiě)操作
    @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);
    }
}

代碼說(shuō)明

  • @SpringBootTest:該注解用于在測(cè)試類(lèi)中啟動(dòng) Spring Boot 上下文。它會(huì)自動(dòng)加載配置文件和應(yīng)用上下文,確保測(cè)試類(lèi)中可以注入依賴(lài)。
  • @Autowired:注入 UserService,以便在測(cè)試方法中調(diào)用 getUsers 和 saveUser 方法。
  • @Test:每個(gè)測(cè)試方法都使用 @Test 注解,表明這是一個(gè)測(cè)試用例,測(cè)試框架會(huì)自動(dòng)運(yùn)行帶有 @Test 的方法。

測(cè)試類(lèi)的測(cè)試方法

  • testReadOperation:測(cè)試從庫(kù)的讀取操作。

    • 該方法會(huì)調(diào)用 userService.getUsers(),這應(yīng)該會(huì)使用從庫(kù)來(lái)執(zhí)行查詢(xún)操作。我們可以在日志中確認(rèn)是否成功走到了從庫(kù)。
  • testWriteOperation:測(cè)試主庫(kù)的寫(xiě)入操作。

    • 該方法會(huì)創(chuàng)建一個(gè)新用戶并調(diào)用 userService.saveUser(user),應(yīng)該會(huì)使用主庫(kù)來(lái)執(zhí)行插入操作??梢栽谌罩局胁榭词欠癯晒κ褂弥鲙?kù)。

5.3.3 在 IntelliJ IDEA 中運(yùn)行測(cè)試

在 IntelliJ IDEA 中可以通過(guò)以下步驟運(yùn)行測(cè)試類(lèi):

  • 運(yùn)行單個(gè)測(cè)試方法:在 testReadOperation 或 testWriteOperation 方法上右鍵點(diǎn)擊,選擇“Run ‘testReadOperation’”或“Run ‘testWriteOperation’”來(lái)運(yùn)行特定測(cè)試。
  • 運(yùn)行整個(gè)測(cè)試類(lèi):在 UserServiceTest 類(lèi)名上右鍵點(diǎn)擊,選擇“Run ‘UserServiceTest’”,以運(yùn)行所有測(cè)試方法。

5.3.4 檢查測(cè)試輸出

在控制臺(tái)中查看輸出信息。如果您在 DynamicDataSource 類(lèi)中添加了日志信息,可以看到類(lèi)似以下的日志:

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,說(shuō)明讀操作成功走了從庫(kù)。
  • 寫(xiě)操作日志:如果 testWriteOperation 的日志顯示 Switching to data source: master,說(shuō)明寫(xiě)操作成功走了主庫(kù)。

5.4 檢查主從數(shù)據(jù)庫(kù)的數(shù)據(jù)同步

在驗(yàn)證讀寫(xiě)操作走了正確的數(shù)據(jù)源后,還需要檢查主從數(shù)據(jù)庫(kù)的數(shù)據(jù)同步情況,以確認(rèn)主庫(kù)的數(shù)據(jù)更改是否成功同步到從庫(kù)。

5.4.1 執(zhí)行寫(xiě)入操作,檢查數(shù)據(jù)同步

  • 運(yùn)行寫(xiě)入測(cè)試方法:通過(guò) testWriteOperation 或直接調(diào)用服務(wù)中的寫(xiě)入方法,向主庫(kù)插入新數(shù)據(jù)。

  • 在主庫(kù)中檢查數(shù)據(jù):連接到主庫(kù),執(zhí)行以下查詢(xún),確認(rèn)數(shù)據(jù)是否成功插入:

    SELECT * FROM your_database.users WHERE name = 'Test User';
    
  • 在從庫(kù)中檢查數(shù)據(jù)同步:稍等片刻后,連接到從庫(kù),執(zhí)行相同的查詢(xún),檢查數(shù)據(jù)是否已同步:

    SELECT * FROM your_database.users WHERE name = 'Test User';
    

    如果從庫(kù)中也能查詢(xún)到這條記錄,則表明主庫(kù)的數(shù)據(jù)成功同步到了從庫(kù)。

5.4.2 執(zhí)行更新操作,檢查數(shù)據(jù)同步

  • 運(yùn)行更新操作:在主庫(kù)中更新記錄的某個(gè)字段,例如通過(guò) userService.updateUser(user) 方法更新 email 字段(假設(shè)該方法存在),或直接在主庫(kù)執(zhí)行更新 SQL 語(yǔ)句:

    UPDATE your_database.users SET email = 'updateduser@example.com' WHERE name = 'Test User';
    
  • 在從庫(kù)中檢查數(shù)據(jù)同步:稍等片刻后,在從庫(kù)執(zhí)行相同的查詢(xún),確認(rèn) email 字段是否已更新為 'updateduser@example.com'。

5.4.3 執(zhí)行刪除操作,檢查數(shù)據(jù)同步

  • 運(yùn)行刪除操作:通過(guò) userService.deleteUserById(userId) 方法(假設(shè)存在)或直接在主庫(kù)執(zhí)行刪除語(yǔ)句:

    DELETE FROM your_database.users WHERE name = 'Test User';
    
  • 在從庫(kù)中檢查數(shù)據(jù)同步:在從庫(kù)中執(zhí)行以下查詢(xún),確認(rèn)數(shù)據(jù)是否已同步刪除:

    SELECT * FROM your_database.users WHERE name = 'Test User';
    

    如果查詢(xún)結(jié)果為空,則表明刪除操作已成功同步到從庫(kù)。

到此這篇關(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)

    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)方案

    保證MySQL與Redis數(shù)據(jù)一致性的6種實(shí)現(xiàn)方案

    這篇文章將聚焦在一個(gè)非常重要且復(fù)雜的問(wèn)題上:MySQL與Redis數(shù)據(jù)的一致性,當(dāng)我們?cè)趹?yīng)用中同時(shí)使用MySQL和Redis時(shí),如何保證兩者的數(shù)據(jù)一致性呢?下面就來(lái)分享幾種實(shí)用的解決方案,需要的朋友可以參考下
    2024-03-03
  • MySQL?數(shù)據(jù)庫(kù)的對(duì)庫(kù)的操作及其數(shù)據(jù)類(lèi)型

    MySQL?數(shù)據(jù)庫(kù)的對(duì)庫(kù)的操作及其數(shù)據(jù)類(lèi)型

    這篇文章主要介紹了MySQL?數(shù)據(jù)庫(kù)的對(duì)庫(kù)的操作及其數(shù)據(jù)類(lèi)型,下面文字圍繞數(shù)據(jù)庫(kù)的對(duì)庫(kù)的操作及其數(shù)據(jù)類(lèi)型的相關(guān)資料展開(kāi)詳細(xì)介紹,需要的小伙伴可以參考一下,希望對(duì)你有所幫助
    2021-12-12
  • MAC下MySQL初始密碼忘記怎么辦

    MAC下MySQL初始密碼忘記怎么辦

    MySQL初始密碼忘記如何解決,這篇文章主要介紹了MAC下MySQL忘記初始密碼的解決辦法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • 用命令創(chuàng)建MySQL數(shù)據(jù)庫(kù)(de1)的方法

    用命令創(chuàng)建MySQL數(shù)據(jù)庫(kù)(de1)的方法

    下面小編就為大家?guī)?lái)一篇用命令創(chuàng)建MySQL數(shù)據(jù)庫(kù)(de1)的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-03-03
  • pymysql.err.DataError:(1264, ")異常的有效解決方法(最新推薦)

    pymysql.err.DataError:(1264, ")異常的有效解決方法(最新推薦)

    遇到pymysql.err.DataError錯(cuò)誤時(shí),錯(cuò)誤代碼1264通常指的是MySQL數(shù)據(jù)庫(kù)中的Out of range value for column錯(cuò)誤,這意味著你嘗試插入或更新的數(shù)據(jù)超過(guò)了對(duì)應(yīng)數(shù)據(jù)庫(kù)列所允許的范圍,這篇文章主要介紹了pymysql.err.DataError:(1264, ")異常的有效問(wèn)題,需要的朋友可以參考下
    2024-05-05
  • MySQL如何創(chuàng)建視圖

    MySQL如何創(chuàng)建視圖

    這篇文章主要介紹了MySQL如何創(chuàng)建視圖,幫助大家更好的理解和學(xué)習(xí)MySQL,感興趣的朋友可以了解下
    2020-08-08
  • 解讀mysql主從配置及其原理分析(Master-Slave)

    解讀mysql主從配置及其原理分析(Master-Slave)

    在windows下配置的,后面會(huì)在Linux下配置進(jìn)行測(cè)試,需要配置mysql數(shù)據(jù)庫(kù)同步的朋友可以參考下。
    2011-05-05
  • 最新mysql-5.7.21安裝和配置方法

    最新mysql-5.7.21安裝和配置方法

    這篇文章主要介紹了最新mysql-5.7.21安裝和配置方法,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-09-09
  • MySQL獲取當(dāng)前時(shí)間的多種方式總結(jié)

    MySQL獲取當(dāng)前時(shí)間的多種方式總結(jié)

    負(fù)責(zé)的項(xiàng)目中使用的是mysql數(shù)據(jù)庫(kù),頁(yè)面上要顯示當(dāng)天所注冊(cè)人數(shù)的數(shù)量,獲取當(dāng)前的年月日,下面這篇文章主要給大家總結(jié)介紹了關(guān)于MySQL獲取當(dāng)前時(shí)間的多種方式,需要的朋友可以參考下
    2023-02-02

最新評(píng)論