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