技術分享 | slave_relay_log_info 表認知的一些展開

作者:胡呈清

slave_relay_log_info 表是這樣的:

mysql> select * from mysql.slave_relay_log_info\G

 *************************** 1. row ***************************
  Number_of_lines: 7
   Relay_log_name: ./mysql-relay.000015
    Relay_log_pos: 621
  Master_log_name: mysql-bin.000001
   Master_log_pos: 2407
        Sql_delay: 0
Number_of_workers: 16
               Id: 1
     Channel_name:
slave_relay_log_info 表存儲 slave sql thread 的工作位置。

在從庫啓動的時候時,讀取 slave_relay_log_info 表中存儲的位置,並把值傳給 "show slave status" 中的 Relay_Log_File、Relay_Log_Pos,下次 "start slave" 是從這個位置開始繼續回放 relay log。
slave_relay_log_info 表存儲的是持久化的狀態、show slave status 輸出的是內存中的狀態:

  • 兩者輸出的位置可能不一樣
  • stop slave 或者正常關閉 mysqld,都會將內存中的狀態持久化到磁盤上(slave_relay_log_info表中)
  • 啓動 mysqld 時會讀取磁盤狀態,初始化給內存狀態
  • start slave 時生效的是內存狀態

slave io thread 按照 Master_Log_File、Read_Master_Log_Pos 位置讀取主庫的 binlog,並寫入到本地 relay log(注意這兩個位點信息保存在 slave_master_info 表中);slave sql thread 按照 Relay_Log_Name、Relay_Log_Pos 位置進行 realy log 的回放。

但是同一個事務在從庫 relay log 中的 position 和主庫 binlog 中的 position 是不相等的,slave_relay_log_info 表通過 Master_log_name、Master_log_pos 這兩個字段記錄了 relay log 中事務對應在主庫 binlog 中的 position。
我們得知道如果 slave io thread 重複、遺漏的讀取主庫 binlog 寫入到 relay log 中,sql thread 也會重複、遺漏地回放這些 relay log。也就是說從庫的數據是否正確,io thread 的位置是否正確也非常重要。
在 MySQL 5.6 以前,複製位點信息只能存儲在數據目錄的 master.info 文件中,在回放事務後更新到文件中(默認每次回放10000個事務更新,受參數 sync_relay_log_info 控制)。即使每個事務都更新文件,意外宕機時也沒法保證持久性一致性。
MySQL 5.6 開始,可以設置 --relay-log-info-repository=TABLE,將 slave sql thread 的工作位置存儲在 mysql.slave_relay_log_info 表中,如果這個表是 InnoDB 這樣的支持事務的引擎,則從庫每回放一個事務時都會在這個事務裏同時更新 mysql.slave_relay_log_info 表,使得 sql thread 的位置與數據保持一致。事實上在 5.6.0-5.6.5 的版本,slave_relay_log_info 表默認使用的是 MyISAM 引擎,之後的版本才改爲 InnoDB,不過再考慮到 MySQL 5.6.10 才 GA,這個坑踩過的人應該不多。

更新機制

引用手冊:

sync_relay_log_info = 0
  If relay_log_info_repository is set to FILE, the MySQL server performs no synchronization of the relay-  log.info file to disk; instead, the server relies on the operating system to flush its contents periodically as with any other file.
  If relay_log_info_repository is set to TABLE, and the storage engine for that table is transactional, the table is updated after each transaction. (Thesync_relay_log_info setting is effectively ignored in this case.)
  If relay_log_info_repository is set to TABLE, and the storage engine for that table is not transactional, the table is never updated.

sync_relay_log_info = N > 0
  If relay_log_info_repository is set to FILE, the slave synchronizes its relay-log.info file to disk (using fdatasync()) after every N transactions.
  If relay_log_info_repository is set to TABLE, and the storage engine for that table is transactional, the table is updated after each transaction. (Thesync_relay_log_info setting is effectively ignored in this case.)
  If relay_log_info_repository is set to TABLE, and the storage engine for that table is not transactional, the table is updated after every N events.

一般的運維規範都會要求 relay_log_info_repository=TABLE,默認值 sync_relay_log_info=10000 此時會失效,變成每回放一個事務都會在這個事務裏同時更新 mysql.slave_relay_log_info 表,保證持久性,以最終保證複製的數據一致。當然 InooDB 的持久性需要 innodb_flush_log_at_trx_commit=1 來保證。

前面有一句話“也就是說從庫的數據是否正確,io thread 的位置是否正確也非常重要”。簡單來說 io thread 位置保存在 slave_master_info 表中,其實設置和 relay_log_info_repository 類似,不同的是它的持久化保障通常與性能衝突很大:

  • 必須設置 master_info_repository = TABLE 和 sync_master_info=1,刷盤的單位是 binlog event 而不是事務,寫放大很嚴重,性能損耗大

所以通常 sync_master_info 使用默認值 10000, io thread 的位置無法保證持久化,也就沒法保證正確。MySQL 有另一個參數 relay_log_recovery 提供一種機制來保證 mysqld crash 後 io thread 位置的準確性,稍後進行介紹。

master_auto_position

master_auto_position 的作用是根據從庫的 Executed_Gtid_Set 自動尋找主庫上對應 binlog 位置,這是在 GTID 出現後的一個功能。

這裏思考一個問題:開啓 master_auto_position 後,slave io thread 能直接根據從庫的 Executed_Gtid_Set 定位主庫上 binlog 的位置嗎?還需要 slave_relay_log_info、slave_master_info 表中記錄的位點信息嗎?

其實 slave_relay_log_info、slave_master_info 表依然發揮作用:

  • 當第一次或者 reset slave 後,執行 start slave,io thread 將從庫的 Executed_Gtid_Set 發往主庫,獲取到對應的 File、Position,之後更新到從庫的 slave_relay_log_info、slave_master_info 表中
  • 當 slave_relay_log_info、slave_master_info 表中存在位置信息後,此後無論是重啓複製還是重啓 mysqld,都是直接從這兩個地方獲取 File、Position,並從這裏開始讀取 binlog 和回放 relay log

注意:執行 "reset slave" 會刪除從庫上的 relay log,並且重置 slave_relay_log_info 表,即重置複製位置。如果 master_auto_position=0,下次啓動複製時會從新開始獲取並回放主庫的 binlog,造成錯誤。

relay_log_recovery

當啓用 relay_log_recovery,mysqld 啓動時,recovery 過程會生成一個新的 relay log,並初始化 slave sql thread 的位置,表現爲:

  • slave_relay_log_info 表的 Relay_Log_Name 值更新爲最新的日誌名, Relay_Log_Pos 值更新爲一個固定值 4(應該是頭部固定信息佔 4個偏移量)
  • 內存狀態即 show slave status 輸出中的 Relay_Log_File、Relay_Log_Pos,也更新成上面一樣的

並且 slave io thread 的位置也會初始化,表現爲:

  • slave_master_info 表中的 Master_log_name、Master_log_pos 不會改變
  • 內存狀態即 show slave status 輸出中的 Master_Log_File、Read_Master_Log_Pos,會更新爲 slave_relay_log_info 表中 Master_Log_Name、Master_Log_Pos

io thread 這個位置的初始化思路就是:既然以前記錄的位置不確定是否準確,那就直接不要了。sql thread 回放到哪,我就從哪開始重新拿主庫的 binlog,這樣準沒錯。一個事務在 relay log 中的 position 對應到主庫 binlog 的 position 是這樣來確定的:

  • slave_relay_log_info 表中的 Relay_log_name 與 Master_log_name,Relay_Log_Pos 與 Master_Log_Pos 始終一一對應,代表同一個事務的位置。

所以,即使 sync_master_info 表的持久化無法保證,relay_log_recovery 也會將 io thread 重置到已經回放的那個位置。
relay_log_recovery 的另一個作用是防止 relay log 的損壞,因爲默認 relay log 是不保證持久化的(也不推薦設置 sync_relay_log=1),當操作系統或者 mysqld crash 後,sql thread 可能會因爲 relay log 的損壞、丟失導致錯誤。

一些結論

  • 當開啓 GTID 和 master_auto_position,並設置 relay_log_recovery=1,即使 relay_log_info_repository 設置爲 file,操作系統或者 mysqld crash 後,mysqld 下次重啓啓動複製都能保證數據與主庫一致。即使 slave_relay_log_info 表中記錄的位置不是最新的,sql thread 可能會重複回放一部分事務,但是從庫已經存在這些事務的 GTID,這部分重複的事務會被跳過。
  • 當未開啓 GTID 和 master_auto_position,必須要設置 relay_log_info_repository=table、relay_log_recovery=1,操作系統或者 mysqld crash 後,mysqld 下次重啓啓動複製才能保證數據與主庫一致。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章