mysql超大表处理方式是_第29问:MySQL 的复制心跳说它不想跳了
問題
最近年底,大家的數據庫經常跑批量大事務,會發現復制突然斷開,報錯“心跳與本地信息不兼容”:
會是什么原因?
實驗
我們先來復現一下,再進行分析。
寬油,做一對主從數據庫:
我們先造一個 500M 的空文件,下一步有用:
再制造一張大表,這里用到了之前的造表法,不同的是使用了一個 longblob 字段,讓少數的幾行記錄就能占用很大的 binlog 空間,方便我們后面做實驗。
這里的 longblob 字段,用到了上一步我們做的空文件,
這樣我們獲得了一個行數較少,但體積很大的表。
現在起兩個會話,一個事務造表 t2,一個事務造表 t3,并同時提交操作,以下舉例其中一個事務:
這樣就獲得了一個超大的 binlog,一共 32G,前 16G 是一個事務,后 16G 是另一個事務。
小貼士
一個事務超過 binlog 的限制大小(最大 1G),就會在事務后直接切換到新的 binlog。
在同一個 binlog 中,我們想讓一個超大事務后再記錄一個事務,所以讓兩個事務同時提交,放在同一個提交組中。
查看一下 master 上的 GTID,最后兩個事務分別是 25 和 26:
下面登錄到 slave上,開始表演:
我們先重置 GTID 和復制狀態,然后騙 slave 說它已經接到了 1-25 事務,要從 26 號事務開始傳輸,也就是從 32G binlog 的中間位置開始傳輸。
然后開始復制的 IO 線程,過十幾秒,就可以看到復制報錯:
查看 Error log:
和我們想要復現的報錯一樣。
下面我們來看一下原理:
這個復現中有幾個要素:
我們用 tcpdump 抓個包:
用 wireshark 解開抓包,找到有問題的包(這里怎么找,我們分析后會有方法):
我們來分析一下包結構,這里我們將包的內容謄寫下來,方便大家閱讀:
首先閱讀,https://dev.mysql.com/doc/internals/en/mysql-packet.html,了解 MySQL 的客戶端網絡包頭結構:
將我們的包對應上去:
其后的一位 00,是 MySQL 的 command type(https://dev.mysql.com/doc/internals/en/command-phase.html),在此沒有意義,我們將其忽略,
再繼續閱讀,https://dev.mysql.com/doc/internals/en/event-header-fields.html,了解 binlog event 的頭結構如下:
將我們的包對應上去:
接下來是個字符串,明顯是一個 binlog 的名字,最后四個字節(下圖中用黃色標注)是 checksum,
至此我們完成了一個心跳包的解析,并沒有看出嚴重的問題,不妨往前再找一個心跳包看看規律:
我將重點在圖中標注,就是 next_position 的位置,在這個包中為 0xfa000557,而其下一個包中為 0x19400583,明顯后面的 next_position 比前面的 next_position 小,這個不符合常理。
而 MySQL 的報錯 heartbeat is not compatible with local info,也是在報這個問題:心跳包中的 position 不應比當前的 position 小。
那是什么導致了這個問題,我們注意到 next_position 的字段長只有 4 字節:
也就是說,該字段最大值為 2 的 31 次方,也就是 4G,當前 binlog 的位置大于 4G 時,該字段就會溢出。也就是說,之前我們看到的位置 0x19400583,實際丟掉了最高的一位,應當是 0x119400583。
這也就導致了 binlog event 傳輸時,next_position 突然會變小,心跳機制會檢查到這個變化,產生報錯。
那我們怎么解決這個問題?
目前可能的方法有以下兩種:
復盤
因為文章比較長,我們對邏輯進行一下復盤:
slave 收到心跳包,進行檢測時發現 next_position 比實際的小,進行報錯。
以上只是一種容易復現問題的場景。實際使用中,master 在一段時間不發送數據包后,或者特殊觸發條件,都會發送心跳包。
對于一主多從的環境,每條復制鏈路的心跳是單獨發送的,也就會導致多個 slave 的表現會有所不同,有的 slave 會觸發報錯,有的 slave 由于 master 沒發送心跳包而不會觸發報錯。
最后送上幾個小貼士:
1)我們如何快速找到有問題的包?
報錯信息里已經標志了出錯的 log position 是 423626115,轉換成 16 進制為:0x19400583,找到由此數據的包即可。
2)一位一位讀包太麻煩了,怎么辦?
好辦,先找到 server_id 的十六進制形式,以此為基準往后推定位數就可以。
比如我們的 server_id 是 19327,很容易找到基準位置。
3)報錯里有一段亂碼是啥?
最后這四位,是 MySQL 程序有缺陷,將包中的 checksum 作為文件名輸出了,對程序邏輯沒有影響。
0x11 是 17,對應 ASCII 碼 "device control 1 character",鍵盤表達形式是 "ctrl + Q",打印形式就是 "^Q"。
本文相關的 MySQL 的 bug 列表:
https://bugs.mysql.com/bug.php?id=101948
https://bugs.mysql.com/bug.php?id=101955
關于 MySQL 的技術內容,你們還有什么想知道的嗎?趕緊****留言告訴小編吧!
總結
以上是生活随笔為你收集整理的mysql超大表处理方式是_第29问:MySQL 的复制心跳说它不想跳了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云域名代备案怎么弄(阿里云域名代备案
- 下一篇: es修改type名称_ElasticSe