<b>bug描述</b>
這是5.6中和gtid相關的一個bug,當 mysqld 收到 sighup 信号 (比如 kill -1) 的時候,會 flush binlog,但是新生成binlog開頭沒寫 previous_gtids_log_event,這會導緻下面 2 個問題:
這個時候 mysqld 重新開機的話,會發現再也起不來了,error log 裡有這樣的錯
the binary log file 'mysql/mysql-bin.000020' is logically corrupted: the first global transaction identifier was read, but no other information regarding identifiers existing on the previous log files was found.
這個時候主庫繼續更新,然後從庫來拉取 binlog 的時候,io 線程會停下來
last_io_error: got fatal error 1236 from master when reading data from binary log: 'error reading header of binary log while looking for the oldest binary log that contains any gtid that is not in the given gtid set'
<b>bug 分析</b>
mysqld 在收到 sighup 信号後,signal_hand 線程會調用 reload_acl_and_cache 函數 (sql_reload.cc),最終會調用 mysql_bin_log::open_binlog,open_binlog 有這段邏輯:
signal_hand 沒有調用 store_globals 設定 thr_thd 這個key,是以這個時候 current_thd 得到的值是空的,是以prev_gtids_event 也就不會寫進新binlog中的。
<b>2個問題的分析</b>
mysqld 重新開機不起來的原因:
mysqld 在啟動的時候會通過 mysql_bin_log.init_gtid_sets 來初始化 gtid_executed 和 gtid_purged 2個set,初使化 gtid_executed 時,會讀最新的binlog,将檔案開頭 previous_gtids_log_event 的 gtid set 和檔案裡所有的 gtid_event 加起來,放進 gtid_executed,在讀檔案過程中,如果發現沒有 previous_gtids_log_event ,就報錯,程式退出。
備庫的錯誤資訊解釋:
在gtid協定下,主庫向備庫發 binlog 是用 com_binlog_dump_gtid 函數,這個函數會調到 mysql_bin_log::find_first_log_not_in_gtid_set(),這個函數的作用是找到備庫需要的第一個 binlog 檔案,邏輯是這樣的,從編号最大的binlog 往前找,對每個binlog,讀取 previous_gtids_log_event,如果發現這個集合是備庫的發來的 gtid_set 的子集,就停止,目前這個binlog檔案就是備庫需要的第一個binlog檔案。找的過程中,如果發現沒有 previous_gtids_log_event,就把錯誤資訊 er_master_fatal_error_reading_binlog 發給備庫。
<b>問題的解決方法</b>
對server 起不來的,隻能手動删所有 binlog 檔案了,同時還要清空 binlog.index 檔案,有備庫的話要重搭備庫。
對于主備場景下,備庫停掉的,purge 主庫的binlog,如果主備不緻的話,比如主庫sighup後又有新的更新,這時候需要重做備庫,因為binlog已經沒了,隻能拿主庫的資料來重新做一個。
<b>bug 修複</b>
修複方法類似reload_acl_and_cache 中 refresh_grant 的邏輯,生成一個臨時的 thd 作為 current_thd,在flush logs 完後釋放掉。