我們如何處理 MySQL 主從延遲?
既然是主從,是讀寫分離,那就不可避免會產生延遲,因為數據從主機同步到從機,總是需要時間的。
一般來說這個時間不會太久,可能就是 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;
通過比較主從上兩個命令執行的結果,就可以知道是否發生了延遲。如果發生了延遲,我們就停一會再去讀。