專案02(Mysql gtid複製故障處理01)

請給我的愛人一杯mojito發表於2020-11-11

背景

Mysql主從基於gtid複製被廣泛使用,DBA可能遇到從庫上的複製故障及處理方法整理

本系列按照做專案的處理方式,每篇包含兩個故障問題

寫在前面

參考知數堂師兄徐晨亮的微信公眾號,連結如下(20201111測試連結可訪問),推薦關注他噢( mysql code tracer)。

link

01

在這裡插入圖片描述

Last_IO_Errno: 1236

#Last_IO_Error:
Got fatal error 1236 from master when reading data from binary log: 
'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, 
but the master has purged binary logs containing GTIDs that the slave requires.

#疑問(請根據圖片中語句的執行順序和報錯資訊閱讀以下分析資訊)
為什麼master上的GTID大於slave上的GTID但是還是會提示找不到binlog呢?

原因
從庫異常停止了io_thread,之後主庫更新了gtid_purged值(清理了binlog檔案),最後導致重新開啟io_thread時,報錯。PURGE BINARY LOGS TO 'mysql-bin.000007'; Query OK

原來slave去master上拉取binlog時,會判斷Executed_Gtid_Set值與主庫上的gtid_purged值,
而在t4時刻執行了purge binary logs,那麼即會更新master上的gtid_purged值。
此時slave上去拉取binlog時,發現本地Executed_Gtid_Set要小於master上的gtid_purged值。

我使用的方法

#如果涉及的庫表資料量不大並且主庫上的binlog包含所需的資料(重新從主庫拉取binlog)
1.先備份整個庫 
mysqldump --single-transaction --master-data=2 -R -E --triggers --all-databases > test.sql
2.刪除庫
3.重新設定主從
 reset slave all;
 reset master;
 change master to master_host='192.168.66.133',master_user='msandbox',master_port=17261,
 master_password='123456',master_auto_position=1;

原作者的解決方案

方法一
更改複製為位置點複製,建立連線後再次改為基於GTID的複製方式(個人理解:跳過錯誤的事務)
(跳過事務是要保證跳過的是無用的也就是跳過之後涉及的庫表在主從上都是一致的)
show slave status\G

#記錄
Master_Log_File: mysql-bin.000008[保證需要資料在這個檔案中,主庫沒有flush logs]
Exec_Master_Log_Pos: 349595 [看看執行到什麼位置了pos]

stop slave;
master_host='192.168.66.133',master_user='msandbox',master_port=17261,
master_password='123456',master_log_file='mysql-bin.000008',master_log_pos=349595,
master_auto_position=0;
[使用哪個二進位制檔案和位置,要在主庫上檢視使用show binary logs;和mysqlbinlog,需要自己確定喲,
不一定是上面記錄中的值]
CHANGE MASTER TO MASTER_AUTO_POSITION=1;
start slave;

#在主庫解析binlog,得到事務的gtid
mysqlbinlog  mysql-bin.000008  --database test  --base64-output=decode-rows -vv  
--skip-gtids=true |grep  -C 1 -i "at 349595" > 11.log

理論上不推薦跳過事務這種操作,除非跳過的是一個空事務。(熟練了再使用,不熟練就重建主從關係)

02

參考博文在以下連結 https://www.fordba.com/mysql-gtid-maintain-two-case.html

link

(原部落格中事務執行流程圖)
在這裡插入圖片描述
報錯資訊
Relay_Log_File: mysql-relay.000011(之後要使用mysqlbinlog解析)
Relay_Log_Pos: 929 (從庫上找語句)
在這裡插入圖片描述

#主庫檢視事務
select @@GLOBAL.gtid_executed;
從庫讓 SQL THREAD 跳過一個GTID事務的時候,想讓從庫重新執行這個跳過的GTID,卻始終報錯。

原因

也就是我們在從庫 手動跳過了 刪除表的事務,之後又建立同名表

在這裡插入圖片描述
在這裡插入圖片描述

原作者的解決方案

STOP SLAVE;
CHANGE MASTER TO RELAY_LOG_FILE='mysql-relay.000011' ,RELAY_LOG_POS=749 
,MASTER_AUTO_POSITION=0;CHANGE MASTER TO MASTER_AUTO_POSITION=1;
START SLAVE;
SHOW SLAVE STATUS\G

原作者的第二個案例,涉及的知識點如下

relay_log_recover=0
slave會掃描最後一個relay log檔案,Retrieved_Gtid_Set顯示的是當前掃描所得的GTID;
io執行緒會通過掃描所得的最後一個GTID+1(如果Retrieved_Gtid_Set>=Executed_Gtid_Set)為依據來拉取。

relay_log_recover=1
slave中把所有relay log清除,io執行緒通過Executed_Gtid_Set後的+1個事務
開始拉取並生成新的relay log檔案;SQL執行緒在清除relay log時把Relay_Log_File、
Relay_Log_Pos設為空,所以SQL執行緒從新的relay log檔案的第一個事務開始應用

從庫:
RESET MASTER; #清空從庫的gtid_executed和binlog資訊(清空並重建從庫上的mysql-bin.000001)
SET GLOBAL GTID_PURGED='1111:1-850'
#手動讓從庫認為1111:845已經執行

在這裡插入圖片描述

flush logs;(新增binlog檔案和relaylog檔案)
-- reset slave; 
#(之後需要重新change master to)清空從庫的gtid_Retrieved和relaylog資訊
(清空並重建mysql-relay.000001)
從伺服器連線到主伺服器之後,把自己執行過的GTID(Executed_Gtid_Set)<SQL執行緒> 、
獲取到的GTID(Retrieved_Gtid_Set)<IO執行緒>發給主伺服器,
主伺服器把從伺服器缺少的GTID及對應的transactions發過去補全即可。

取自以下

原博主寫的太好了,作為學習者,以下內容直接引用了。
link

gtid_executed(global):MySQL資料庫已經執行過的Gtid事務,處於記憶體中。
show master status/show slave status中的Executed_Gtid_Set也取自這裡。

gtid_purged(global):由於binlog檔案的刪除(purge binary logs或者超過expire_logs_days設定)
已經丟失的Gtid事務,它是gtid_executed的子集
引數 binlog_gtid_simple_recovery

資料庫服務啟動時,gtid_executed和gtid_purged按下面方式初始化
binlog_gtid_simple_recovery=FALSE
• gtid_executed:從mysql-bin.index的末行往首行所對應的binlog查詢,
直到首個被找到包含Previous_gtids_log_event的binlog。
然後讀取這個binlog的Previous_gtids_log_event和Gtid_log_events中的所有Gtid集合儲存到內部變數
gtids_in_binlog。然後使用gtids_in_binlog和mysql.gtid_executed表的並集初始化gtid_executed變數
如果你有大量非GTID的binlog(比如gtid_mode=off的情況下建立),
初始化gtid_executed的過程會消耗較長的時間。

• gtid_purged:從mysql-bin.index的首行往末行所對應的binlog查詢,
直到首個被找到包含非空Previous_gtids_log_event或者Gtid_log_event的binlog。
然後讀取這個binlog的Previous_gtids_log_event,
將gtids_in_binlog - Previous_gtids_log_event得到的集合儲存到內部變數
gtids_in_binlog_not_purged。
最後使用gtid_executed - gtids_in_binlog_not_purged初始化gtid_purged變數
#預設值
binlog_gtid_simple_recovery=TRUE(MySQL5.7.7及以上預設)
只迭代mysql-bin.index的首行和末行所對應的binlog,
gtid_executed和gtid_purged的值就是取這兩個binlog中的
Previous_gtids_log_event/Gtid_log_event計算,
當然gtid_executed變數的值還要結合mysql.gtid_executed
引數 mysql.gtid_executed

如果沒有開啟log_bin或者沒有開啟log_slave_updates,從庫在應用relay-log中的
每個事務會執行一個insert mysql.gtid_executed操作。這隻針對從庫而言~
#一般情況
如果開啟log_bin,在binlog發生rotate(flush binary logs/達到max_binlog_size)或者關閉服務時,
會把所有寫入到binlog中的Gtid資訊寫入到mysql.gtid_executed表。這適用於主庫和從庫~

log_bin=on,MySQL 8.0.17 起每個事務提交時會更新mysql.gtid_executed表

例子:從庫log_bin=on,log_slave_updates=off,那麼在應用relay-log時會實時寫入
mysql.gtid_executed,而在從庫直接寫入資料,需要等到發生rotate或者關閉服務才寫入~
mysql.gtid_executed壓縮:log-bin=off,每gtid_executed_compression_period壓縮一次;
log-bin=on,日誌切換時壓縮~

如果發生異常crash,當前binlog中的Gtids資訊沒能寫入到mysql.gtid_executed表,
在恢復過程通過讀取binlog中的Previous_gtids_log_event/Gtid_log_event資訊
把這些Gtids新增到mysql.gtid_executed表和gtid_executed系統變數

#Rotate(主從切換)

# 解析mysql-bin.000208
mysqlbinlog -vv --base64-output=decode-rows mysql-bin.000208 |more

本文說明,主要技術內容來自網際網路技術大佬的分享,還有一些自我的加工(僅僅起到註釋說明的作用)。如有相關疑問,請留言,將確認之後,執行侵權必刪

相關文章