既然是主從,是讀寫分離,那就不可避免會產生延遲,因為資料從主機同步到從機,總是需要時間的。
一般來說這個時間不會太久,可能就是 1ms 左右。
不過,如果你的系統資料量比較大,亦或者業務對資料實時性要求比較高,那麼我們還是需要想辦法去處理這個主從延遲。
一般來說有如下幾種思路,鬆哥來和大家一一說明。
一 強制讀主庫
第一種方案就是強制讀主庫。
這種方案看著有點笨重,但卻是我司用的最多的一種方案。
簡單來說,就是將查詢請求進行分類:一類是對資料實時性要求不高的請求,這種請求直接去讀從庫;另一類則是對實時性要求比較高的請求,這種就強制讀主庫。
舉個簡單例子:讀取系統配置、讀取使用者基本資訊等等,都算是對資料實時性要求不高的請求,這種直接讀取從庫就可以了;但是像使用者下單獲取訂單狀態的話,這種就需要讀主庫了,確保資料的一致性。
強制讀主庫我們可以在程式碼裏邊透過 AOP 的方式實現,也可以透過一些資料庫中間如 ShardingJDBC 去配置。
二 sleep 方案
這種方案就是剛剛插入完成之後,此時如果去讀取從機的話,先 sleep 一會再讀,這樣就能儘量保證從機的資料已經同步過來了。
不過這個方案顯然不夠優雅,發請求先 sleep,怎麼想都覺得彆扭。
三 判斷主從是否延遲
第三種方案就是我們去判斷一下主從是否延遲,如果發生延遲了,就等一會,如果資料已經同步了,那就直接查詢就行了。
判斷是否發生主從延遲,一般來說可以透過兩種方式。
3.1 seconds_behind_master
seconds_behind_master
引數是一個只讀變數,用於表示從伺服器(slave)相對於主伺服器(master)的複製延遲時間。
這個引數反映了從伺服器在複製過程中落後於主伺服器的時間長度(以秒為單位)。
這個引數的取值如下:
正值:表示從伺服器正在追趕主伺服器的複製進度。具體的數值表示從屬伺服器的複製程序落後於主伺服器的時間長度。例如,如果此值為 60 秒,那麼意味著從伺服器的複製操作比主伺服器晚了 60 秒。
0:表示從屬伺服器與主伺服器的複製同步是實時的,沒有延遲。這意味著從屬伺服器已經完成了所有可用的複製事件,且沒有新的事件等待應用。
NULL:
如果從伺服器剛剛啟動,還沒有開始複製過程,那麼此值可能是 NULL。
如果從伺服器與主伺服器之間的連線斷開,或者從屬伺服器正在處理非複製任務(例如,正在進行表修復),也可能顯示為 NULL。
如果從伺服器已經追上了主伺服器,並且沒有新的事件需要複製,也會顯示為 NULL。
要檢視 seconds_behind_master
的值,我們可以使用以下 SQL 命令:
SHOW SLAVE STATUS\G;
輸出中會有一行顯示 Seconds_Behind_Master
,這就是你要找的資訊。
利用 seconds_behind_master
引數,我們可以監控複製延遲,管理員可以據此瞭解從伺服器的複製進度,並確定是否存在複製延遲問題。
在 MySQL8.0 之後的版本中,
seconds_behind_master
被替換為replication_lag
,但這兩個引數的功能是一樣的。
3.2 GTID
GTID 是 MySQL5.6 引入的一個特性,用於跟蹤事務在主伺服器上的執行情況,並確保這些事務按順序在從伺服器上重現。使用 GTID 進行主從複製可以簡化管理和監控,特別是在有多個從伺服器或複雜的複製拓撲中。
下面鬆哥給大家簡單演示下如何利用 GTID 判斷 MySQL 主從複製是否發生延遲。
步驟 1:確認主伺服器和從伺服器都啟用了 GTID
確保主伺服器和從伺服器都配置了 GTID。需要在 MySQL 的配置檔案(如 my.cnf
或 my.ini
)中設定 server-id
和 gtid_mode
。
[mysqld] server-id = 1 # 主伺服器的 server-id gtid_mode = ON # 啟用 GTID
[mysqld] server-id = 2 # 從伺服器的 server-id gtid_mode = ON # 啟用 GTID
步驟 2:檢查 GTID 執行狀態
可以使用 SHOW MASTER STATUS
和 SHOW SLAVE STATUS
命令來檢查主伺服器和從伺服器的 GTID 狀態。
在主伺服器上
SHOW MASTER STATUS;
這裏多說一句,從 MySQL8.4 開始,不再使用
SHOW MASTER STATUS;
,取而代之的是SHOW BINARY LOG STATUS
。
輸出將包括當前的 GTID 執行位置,如下所示:
File: mysql-bin.000001 Position: 107 Binlog_Do_DB: Binlog_Ignore_DB: Executed_Gtid_Set: 11111111-1111-1111-1111-111111111111:1-100
這裏 Executed_Gtid_Set
顯示了主伺服器已經執行的所有 GTID 的集合。
在從伺服器上
SHOW SLAVE STATUS\G;
輸出將包括從伺服器的 GTID 執行位置,如下所示:
... Master_Host: master.example.com Master_User: replication Master_Port: 3306 Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 107 Relay_Master_Log_File: mysql-bin.000001 ... Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 107 Auto_Position: 1 ...
其中 Auto_Position
的值為 1 表示從伺服器正在使用 GTID 進行復制。
步驟 3:比較 GTID 集合
比較主伺服器和從伺服器的 Executed_Gtid_Set
。如果兩者相同,則表示複製沒有延遲;如果有差異,則表示存在延遲。
步驟 4:分析 GTID 集合差異
如果發現 GTID 集合之間存在差異,可以透過以下命令檢視具體的 GTID:
SELECT @@gtid_executed;
透過比較主從上兩個命令執行的結果,就可以知道是否發生了延遲。如果發生了延遲,我們就停一會再去讀。
鬆哥這邊的專案第一種方案是使用比較多的,另外兩種使用相對比較少。小夥伴們有無遇到類似問題,都是怎麼解決的?歡迎留言討論。