天天看點

一次關于 MySQL 主從模式采用 GTID 的實踐記錄

作者:Java靈風

一、背景

為了保證高可用,之前在測試環境部署了一套 MySQL 雙主模式,當一個主庫服務出現異常,可以将流量切到另外一個主庫,兩個主庫之間互相同步資料。

雙主模式

雙主模式的原理圖如下:

一次關于 MySQL 主從模式采用 GTID 的實踐記錄

但是經常出現資料沖突的問題,于是我們又把雙主模式改為了主從讀寫分離模式。主庫作為讀寫庫,再加上一個從庫用來做 I/O 密集型的任務(如大量的資料統計操作)。如下圖所示:

一次關于 MySQL 主從模式采用 GTID 的實踐記錄

另外從庫複制的模式采用位點的方式:指定 binlog 檔案和 binlog 位置,這樣從庫就知道了複制的起始位置。(下文會講解這種方式)

雖然改為了主從模式,但依舊遇到了些問題:

  • 問題 1:從庫 B 複制資料時,出現了主鍵沖突問題,導緻同步失敗,從庫停止複制。猜測因主庫配置的 binlog 日志的格式為 mixed,從庫同步時出現不一緻的情況。
  • 問題 2:從庫 B 停止複制後,導緻很多資料未同步到從庫,出現主從大量資料不一緻的情況。
  • 問題 3:從庫 B 想要恢複複制,必須先解決同步失敗的問題才能恢複。排查難度較大,耗時。
  • 問題 4:從庫 B 恢複時,必須知道同步位點,也就是從哪個 binlog 檔案和 binlog 位置斷開複制的,且即使找到了位點,也不是精确的。
  • 問題 5:從庫 B 因同步異常導緻停止複制到恢複複制這段期間,主庫 A 自動清理了幾天前的 binlog 日志,而這些日志從庫 B 還未來得及同步,進而導緻再次同步失敗。
  • 問題 6:主從存在同步延遲。

這篇我們來探讨下問題 4 和問題 6。

其中問題 4 是一個比較頭疼的問題,我們一般是通過檢視從庫 B 目前的同步狀态拿到同步位點,然後設定同步位點後。但是重新啟動同步的時候又會出現同步異常,比如從庫 B 可能會出現 Duplicate entry ‘id_of_R’ for key ‘PRIMARY’ 錯誤,提示出現了主鍵沖突,然後停止同步。

為了減少位點同步引入的複雜度,我們切換成了 GTID 模式。

對于問題 6,本篇也僅限于探讨如何觀察延遲,對于如何減少延遲不在本篇探讨範圍之内。

接下來我們來展開看下位點同步的痛點。

二、位點同步的痛點

2.1 通過位點同步的原理圖

為了更清晰地了解主從采用位點同步的原理,這裡有一個原理圖:

一次關于 MySQL 主從模式采用 GTID 的實踐記錄

1、主庫會生成多個 binlog 日志檔案。

2、從庫的I/O 線程請求指定檔案和指定位置的 binlog 日志檔案(位點)。

3、主庫 dump 線程擷取指定位點的 binlog 日志。

4、主庫按照從庫發送給來的位點資訊讀取 binlog,然後推送 binlog 給從庫。

5、從庫将得到的 binlog 寫到本地的 relay log (中繼日志) 檔案中。

6、從庫的 SQL 線程讀取和解析 relay log 檔案。

7、從庫的 SQL 線程重放 relay log 中的指令。

當我們使用位點同步的方式時,兩種場景下的操作步驟比較複雜。

2.2 痛點

痛點1:首次開啟主從複制的步驟複雜

  • 第一次開啟主從同步時,要求從庫和主庫是一緻的。
  • 找到主庫的 binlog 位點。
  • 設定從庫的 binlog 位點。
  • 開啟從庫的複制線程。

痛點2:恢複主從複制的步驟複雜

  • 找到從庫複制線程停止時的位點。
  • 解決複制異常的事務。無法解決時就需要手動跳過指定類型的錯誤,比如通過設定slave_skip_errors=1032,1062。當然這個前提條件是跳過這類錯誤是無損的。(1062 錯誤是插入資料時唯一鍵沖突;1032 錯誤是删除資料時找不到行)
不論是首次開啟同步時需要找位點和設定位點,還是恢複主從複制時,設定位點和忽略錯誤,這些步驟都顯得過于複雜,而且容易出錯。是以 MySQL 5.6 版本引入了 GTID,徹底解決了這個困難。

三、GTID 方案

3.1 GTID 是什麼?

GTID 的全稱是 Global Transaction Identifier,全局事務 ID,當一個事務送出時,就會生成一個 GTID,相當于事務的唯一辨別。

GTID 長這樣:

c5d74746-d7ec-11ec-bf8f-0242ac110002:1
           

結構:

GTID=server_uuid:gno
           

server_uuid 是一個執行個體第一次啟動時自動生成的,是一個全局唯一的值;

gno 是一個整數,初始值是 1,每次送出事務的時候配置設定給這個事務,并加 1。

每個 MySQL 執行個體都維護了一個 GTID 集合,用來對應“這個執行個體執行過的所有事務”。

3.2 GTID 的優勢

  • 1、更簡單的實作 failover,不用以前那樣在需要找位點(log_file 和 log_pos)。
  • 2、更簡單的搭建主從複制。
  • 3、比傳統的複制更加安全。
  • 4、GTID是連續的沒有空洞的,保證資料的一緻性,零丢失。

3.3 如何啟用 GTID

修改主庫和從庫的配置檔案:

#GTID:
gtid_mode=on
enforce_gtid_consistency=on
           

從庫配置同步的參數:

CHANGE MASTER TO 
MASTER_HOST=$host_name 
MASTER_PORT=$port 
MASTER_USER=$user_name 
MASTER_PASSWORD=$password 
master_auto_position=1 
           

其中 master_auto_position 辨別主從關系使用的 GTID 協定。

相比之前的配置,MASTER_LOG_FILE 和 MASTER_LOG_POS 參數已經不需要了。

3.4 GTID 同步方案

GTID 同步的原理圖。

GTID 方案:主庫計算主庫 GTID 集合和從庫 GTID 的集合的差集,主庫推送差集 binlog 給從庫。

當從庫設定完同步參數後,主庫 A 的GTID 集合記為集合 x,從庫 B 的 GTID 集合記為 y。從庫同步的邏輯如下:

一次關于 MySQL 主從模式采用 GTID 的實踐記錄
  • 從庫 B 指定主庫 A,基于主備協定履歷連接配接。
  • 從庫 B 把集合 y 發給主庫 A。
  • 主庫 A 計算出集合 x 和集合 y 的差集,也就是集合 x 中存在,集合 y 中不存在的 GTID 集合。比如集合 x 是 1~100,集合 y 是 1~90,那麼這個差集就是 91~100。這裡會判斷集合 x 是不是包含有集合 y 的所有 GTID,如果不是則說明主庫 A 删除了從庫 B 需要的 binlog,主庫 A 直接傳回錯誤。
  • 主庫 A 從自己的 binlog 檔案裡面,找到第一個不在集合 y 中的事務 GTID,也就是找到了 91。
  • 主庫 A 從 GTID = 91 的事務開始,往後讀 binlog 檔案,按順序取 binlog,然後發給 B。
  • 從庫 B 的 I/O 線程讀取 binlog 檔案生成 relay log,SQL 線程解析 relay log,然後執行 SQL 語句。

GTID 同步方案和位點同步的方案差別是:

  • 位點同步方案是通過人工在從庫上指定哪個位點,主庫就發哪個位點,不做日志的完整性判斷。
  • 而 GTID 方案是通過主庫來自動計算位點的,不需要人工去設定位點,對運維人員友好。

四、如何判斷主從庫是否有延遲

上面提到的問題 6 是主從讀寫分離後,從庫複制存在延遲,接下來我們來探讨下如何觀察主從延遲多少的問題。

方案一:判斷從庫的同步狀态參數 seconds_behind_master 是否為 0。(不準确)

方案二:對比位點確定主備無延遲。

方案三:對比 GTID 集合確定主備無延遲。

方案一:檢視 seconds_behind_master

可以在從庫上執行 slow slave status 指令來看執行結果裡面的 seconds_behind_master 參數的值,如下圖所示,Seconds_Behind_Master 等于 0

一次關于 MySQL 主從模式采用 GTID 的實踐記錄

Seconds_Behind_Master 的機關是秒,是以精度不準确。

是以為了保證查詢的資料是和主庫一緻的,就需要先判斷 seconds_behind_master 是否已經等于 0,如果不等于 0,就必須等到這個參數變為 0 才能執行查詢請求。

方案二:對比位點

可以通過檢視從庫目前的同步位點來确認從庫同步是否有延遲。下圖是在從庫上執行 show slave status \G指令後的結果:

一次關于 MySQL 主從模式采用 GTID 的實踐記錄

Master_Log_File 和 Read_Master_Log_Pos 這兩個參數合起來表示的是讀到的主庫的最新位點,第一參數是代表讀取到了哪個檔案,第二個是讀取到的檔案的位置。

Relay_Master_Log_File 和 Exec_Master_Log_Pos,這兩個參數合起來表示的是從庫執行的最新位點。

如果紅色框起來的兩個參數:Master_Log_File 和 Relay_Master_Log_File 相等,則說明從庫讀到的最新檔案和主庫上生成的檔案相同,這裡都是 mysql-bin.000934。

如果藍色框起來的兩個參數 Read_Master_Log_Pos 和 Exec_Master_Log_Pos 相等,則說明從庫讀到的日志檔案的位置和從庫上執行日志檔案的位置相同,這裡都是 59521082。

當上面兩組參數都相等時,則說明沒有延遲。

方案三:對比 GTID 集合

方案三是對比 GTID 集合。首先我們在從庫上執行 show slave status \G來檢視 GTID 集合。

如下圖所示:

一次關于 MySQL 主從模式采用 GTID 的實踐記錄

Master_UUID 表示目前連接配接的主庫的 ID。

Auto_Position: 1 表示主備使用了 GTID 協定。

Retrieved_Gtid_Set 表示從庫收到的所有日志的 GTID 集合。

Executed_Gtid_Set 表示從庫已經執行完成的 GTID 集合。

如果 Executed_Gtid_Set 集合是包含 Retrieved_Gtid_Set,則表示從庫接收到的日志已經同步完成。

比如上圖中 Retrieved_Gtid_Set 值為

c5d74746-d7ec-11ec-bf8f-0242ac110002:1-87323
           

前面一段是主庫 id,後面一段 1-87383 是 GTID 範圍。而Executed_Gtid_Set 的值有兩個集合

7083ae1f-d7ef-11ec-a329-0242ac110002:1-2,
c5d74746-d7ec-11ec-bf8f-0242ac110002:1-87323
           

Executed_Gtid_Set 的第二個集合和第一個集合完全一緻,第一個集合 id 和 集合範圍是上次同步另外一個主庫的記錄。這裡說明從庫已經和目前主庫同步完成了。

方案二對比位點和方案三的 GTID 比對都要比方案一的seconds_behind_master 更準确。但是還是沒有達到精确的程度,需要配合半同步複制(semi-sync replication)才能達到。

小結:本篇通過 GTID 的方式更好地實作了主從節點的同步,以及如何觀察主從同步的延遲。

Original reprint:https://mp.weixin.qq.com/s/N6jpJ3DkwWfzmCiefEKZRw

繼續閱讀