我們?nèi)绾翁幚?MySQL 主從延遲?
既然是主從,是讀寫分離,那就不可避免會(huì)產(chǎn)生延遲,因?yàn)閿?shù)據(jù)從主機(jī)同步到從機(jī),總是需要時(shí)間的。
一般來說這個(gè)時(shí)間不會(huì)太久,可能就是 1ms 左右。
不過,如果你的系統(tǒng)數(shù)據(jù)量比較大,亦或者業(yè)務(wù)對(duì)數(shù)據(jù)實(shí)時(shí)性要求比較高,那么我們還是需要想辦法去處理這個(gè)主從延遲。
一般來說有如下幾種思路,松哥來和大家一一說明。
一 強(qiáng)制讀主庫(kù)
第一種方案就是強(qiáng)制讀主庫(kù)。
這種方案看著有點(diǎn)笨重,但卻是我司用的最多的一種方案。
簡(jiǎn)單來說,就是將查詢請(qǐng)求進(jìn)行分類:一類是對(duì)數(shù)據(jù)實(shí)時(shí)性要求不高的請(qǐng)求,這種請(qǐng)求直接去讀從庫(kù);另一類則是對(duì)實(shí)時(shí)性要求比較高的請(qǐng)求,這種就強(qiáng)制讀主庫(kù)。
舉個(gè)簡(jiǎn)單例子:讀取系統(tǒng)配置、讀取用戶基本信息等等,都算是對(duì)數(shù)據(jù)實(shí)時(shí)性要求不高的請(qǐng)求,這種直接讀取從庫(kù)就可以了;但是像用戶下單獲取訂單狀態(tài)的話,這種就需要讀主庫(kù)了,確保數(shù)據(jù)的一致性。
強(qiáng)制讀主庫(kù)我們可以在代碼里邊通過 AOP 的方式實(shí)現(xiàn),也可以通過一些數(shù)據(jù)庫(kù)中間如 ShardingJDBC 去配置。
二 sleep 方案
這種方案就是剛剛插入完成之后,此時(shí)如果去讀取從機(jī)的話,先 sleep 一會(huì)再讀,這樣就能盡量保證從機(jī)的數(shù)據(jù)已經(jīng)同步過來了。
不過這個(gè)方案顯然不夠優(yōu)雅,發(fā)請(qǐng)求先 sleep,怎么想都覺得別扭。
三 判斷主從是否延遲
第三種方案就是我們?nèi)ヅ袛嘁幌轮鲝氖欠裱舆t,如果發(fā)生延遲了,就等一會(huì),如果數(shù)據(jù)已經(jīng)同步了,那就直接查詢就行了。
判斷是否發(fā)生主從延遲,一般來說可以通過兩種方式。
3.1 seconds_behind_master
seconds_behind_master 參數(shù)是一個(gè)只讀變量,用于表示從服務(wù)器(slave)相對(duì)于主服務(wù)器(master)的復(fù)制延遲時(shí)間。
這個(gè)參數(shù)反映了從服務(wù)器在復(fù)制過程中落后于主服務(wù)器的時(shí)間長(zhǎng)度(以秒為單位)。
這個(gè)參數(shù)的取值如下:
- 正值:表示從服務(wù)器正在追趕主服務(wù)器的復(fù)制進(jìn)度。具體的數(shù)值表示從屬服務(wù)器的復(fù)制進(jìn)程落后于主服務(wù)器的時(shí)間長(zhǎng)度。例如,如果此值為 60 秒,那么意味著從服務(wù)器的復(fù)制操作比主服務(wù)器晚了 60 秒。
- 0:表示從屬服務(wù)器與主服務(wù)器的復(fù)制同步是實(shí)時(shí)的,沒有延遲。這意味著從屬服務(wù)器已經(jīng)完成了所有可用的復(fù)制事件,且沒有新的事件等待應(yīng)用。
- NULL:
如果從服務(wù)器剛剛啟動(dòng),還沒有開始復(fù)制過程,那么此值可能是 NULL。
如果從服務(wù)器與主服務(wù)器之間的連接斷開,或者從屬服務(wù)器正在處理非復(fù)制任務(wù)(例如,正在進(jìn)行表修復(fù)),也可能顯示為 NULL。
- 如果從服務(wù)器已經(jīng)追上了主服務(wù)器,并且沒有新的事件需要復(fù)制,也會(huì)顯示為 NULL。
要查看 seconds_behind_master 的值,我們可以使用以下 SQL 命令:
SHOW SLAVE STATUS\G;
輸出中會(huì)有一行顯示 Seconds_Behind_Master,這就是你要找的信息。
利用 seconds_behind_master 參數(shù),我們可以監(jiān)控復(fù)制延遲,管理員可以據(jù)此了解從服務(wù)器的復(fù)制進(jìn)度,并確定是否存在復(fù)制延遲問題。
?
在 MySQL8.0 之后的版本中,seconds_behind_master 被替換為 replication_lag,但這兩個(gè)參數(shù)的功能是一樣的。
3.2 GTID
GTID 是 MySQL5.6 引入的一個(gè)特性,用于跟蹤事務(wù)在主服務(wù)器上的執(zhí)行情況,并確保這些事務(wù)按順序在從服務(wù)器上重現(xiàn)。使用 GTID 進(jìn)行主從復(fù)制可以簡(jiǎn)化管理和監(jiān)控,特別是在有多個(gè)從服務(wù)器或復(fù)雜的復(fù)制拓?fù)渲小?/p>
下面松哥給大家簡(jiǎn)單演示下如何利用 GTID 判斷 MySQL 主從復(fù)制是否發(fā)生延遲。
步驟 1:確認(rèn)主服務(wù)器和從服務(wù)器都啟用了 GTID
確保主服務(wù)器和從服務(wù)器都配置了 GTID。需要在 MySQL 的配置文件(如 my.cnf 或 my.ini)中設(shè)置 server-id 和 gtid_mode。
[mysqld]
server-id = 1 # 主服務(wù)器的 server-id
gtid_mode = ON # 啟用 GTID
[mysqld]
server-id = 2 # 從服務(wù)器的 server-id
gtid_mode = ON # 啟用 GTID
步驟 2:檢查 GTID 執(zhí)行狀態(tài)
可以使用 SHOW MASTER STATUS 和 SHOW SLAVE STATUS 命令來檢查主服務(wù)器和從服務(wù)器的 GTID 狀態(tài)。
在主服務(wù)器上
SHOW MASTER STATUS;
這里多說一句,從 MySQL8.4 開始,不再使用 SHOW MASTER STATUS;,取而代之的是 SHOW BINARY LOG STATUS。
輸出將包括當(dāng)前的 GTID 執(zhí)行位置,如下所示:
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 顯示了主服務(wù)器已經(jīng)執(zhí)行的所有 GTID 的集合。
在從服務(wù)器上
SHOW SLAVE STATUS\G;
輸出將包括從服務(wù)器的 GTID 執(zhí)行位置,如下所示:
...
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 表示從服務(wù)器正在使用 GTID 進(jìn)行復(fù)制。
步驟 3:比較 GTID 集合
比較主服務(wù)器和從服務(wù)器的 Executed_Gtid_Set。如果兩者相同,則表示復(fù)制沒有延遲;如果有差異,則表示存在延遲。
步驟 4:分析 GTID 集合差異
如果發(fā)現(xiàn) GTID 集合之間存在差異,可以通過以下命令查看具體的 GTID:
SELECT @@gtid_executed;
通過比較主從上兩個(gè)命令執(zhí)行的結(jié)果,就可以知道是否發(fā)生了延遲。如果發(fā)生了延遲,我們就停一會(huì)再去讀。






