天天看點

TDSQL中修複的mysql核心bug

在TDSQL這兩年多的開發工作中,我感覺很自豪的一件事是我修複了不少mysql-5.7.17和mariadb-10.1.9的核心bug,這些bug大多已經報告給了MySQL/MariaDB官方開發團隊,在每個bug描述中我會貼出來bug報告的連接配接。本文将大略介紹這些bug的概況,我在将來會寫更多文章詳細介紹每個bug的具體問題分析以及解決思路。本文列出的所有bug都已經修複,經過驗證可以正确工作并解決相關問題。

這裡先說一下為什麼我要送出代碼給mysql/mariadb官方開發團隊,主要有一下幾個好處:

  1. 官方開發者可以review我送出的patch,幫助完善patch,發現和解決之前可能沒有想到的問題。我自己做mysql核心開發和bug 修複時會跑mtr(mysql自帶的測試包),確定所有測例通過,并且新增測例覆寫新增的功能和代碼。不過即使如此,也可能有未考慮到的點,是以可以借助mysql/mariadb官方的力量。
  2. 将來在新版本合并代碼會很容易,因為這些patch的改動已經在官方釋出的新版本中了,就不需要再次合并了。
  3. 為開源軟體做出貢獻,這樣的一種社群氛圍長期來看對所有mysql使用者都是好事。

下面就是每個bug的簡介,主要介紹其症狀和危害性。具體解決方法和分析過程以及修複代碼patch請看bug連結的頁面,我未來也會寫文章做出更詳細的講解和分析。下文中經常提到的英文術語簡介如下:

XA: 分布式事務處理

DML: 資料操作語言,比如SQL語言中的INSERT,UPDATE,DELETE,SELECT等語句。

Mutex:互斥量,作業系統基礎功能

安裝插件流程中的mutex死鎖

執行install plugin語句時,負責插件狀态互斥的一個mutex與負責全局變量狀态互斥的mutex可能發生死鎖。死鎖一旦發生,mysqld就hang住了,所有連接配接都無法繼續工作。我送出給mysql官方開發團隊的bug報告如下:

Bug?#88693Mutex deadlock causing mysqld to hang and cease to work

https://bugs.mysql.com/bug.php?id=88693

Optimize table與DML語句的沖突

當執行optimize table時由大量的DML語句(比如delete)在執行時,innodb有可能crash在一個assert中,導緻mysqld程序崩潰退出。詳情請見我的bug報告:

Bug #88511 innodb assertion failure in table rename

https://bugs.mysql.com/bug.php?id=88511

percona 備機複制在特定事件序列情況下卡住

對于mysql-5.7的XA事務來說,在一個XA事務的任何位置中斷slave IO線程的連接配接後都會出問題,導緻備機sql線程陷入死循環,備機複制無法繼續下去。我送出給mysql官方開發團隊的bug報告如下:

Bug #87385 Partial external XA transactions are not rolled back correctly

https://bugs.mysql.com/bug.php?id=87385

MYSQL 用戶端的AUTOCOMMIT狀态位設定不準确

當global autocommit是0 時候 新開啟一個mysql連接配接時,用戶端傳回的autocommit狀态位是1,應該是0. mariadb和percona都有這個問題。這個bug對于絕大多數最終使用者而言可能是無感覺的,不過TDSQL的網關子產品依賴于這個狀态位來正确工作,因而我們發現并修複了這個bug。

我送出給mysql官方開發團隊的bug報告如下:

Bug #87813 Incorrect autocommit status bit in server_status at connection startup

https://bugs.mysql.com/bug.php?id=87813

無法在AUTOCOMMIT=0時結束PREPARED狀态的XA事務

如果一個連接配接的autocommit=0,然後執行XA COMMIT/XA ROLLBACK來結束一個事務,那麼Mysql會報錯并拒絕操作。報錯資訊:

ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the NON-EXISTING state

這樣導緻PREPARED狀态的XA事務在非autocommit 設定下無法被送出或者復原,分布式事務處理機制無法正确工作。

Bug #87836 XA COMMIT/ROLLBACK rejected by non-autocommit session with no active transaction

https://bugs.mysql.com/bug.php?id=87836

percona XA事務送出和復原的恢複機制

官方mysql-5.7的實作中,沒有處理XA 事務分支的binlog + engine 恢複,包括XA PREPARE,XA COMMIT ... ONE PHASE以及XA COMMIT/XA ROLLBACK 送出的事務的恢複,mysql-5.7都沒有實作binlog事務恢複機制,因而在各種災難情況下會發生丢失XA事務,

QQ号賣号平台

資料不一緻等緻命問題。這些bug我們在TDSQL中都已經修複了。

這是一個巨大的開發任務,新增代碼幾千行,我送出給mysql官方開發團隊的兩個bug報告如下:

Bug #87560 XA PREPARE log order error in replication and binlog recovery

https://bugs.mysql.com/bug.php?id=87560

Bug #84297 Engine prepare executed after flush stage

https://bugs.mysql.com/bug.php?id=84297

Percona XA COMMIT/ROLLBACK事務的gtid管理

這類事件組(也就是一個xa事務的binlog的後半部分)在備機上面可能無法執行,因為主備切換的特殊時機下目前備曾是主機,已經執行過了。但是官方預設實作未考慮到這一點,就會導緻備機可能無法連接配接到主機因為主機沒有備機需要的gtid。此時的症狀表現就是主備切換後,備機無法連上主機,産生反複連接配接和報錯的循環,TDSQL在強同步模式下就無法運作了。這個bug已經修複,經過驗證可以正确工作并解決相關問題。

Percona備機SQL線程執行XA_PREPARE_EVENT時,複制位置沒有持久化

Percona備機SQL線程執行XA_PREPARE_EVENT時,複制位置沒有持久化,于是備機crash然後重新開機之後,開始複制的位置就是錯誤的(會從更早的位置開始複制),就會導緻複制卡住(比如要删除一個已經删除的行,或者更新時間找不到目标行,或者插入發生主鍵沖突)和資料不一緻等嚴重問題。

Bug?#87389Replication position not persisted correctly for XA transactions

https://bugs.mysql.com/bug.php?id=87389

XA COMMIT事件沒有被作為事務邊界

MySQL-5.7的replication實作,依賴于一個事務狀态分析對象,它根據掃描到的事件,維持一個目前事務狀态,這個狀态會被備機的複制機制使用。由于XA COMMIT這個事件沒有被作為事務邊界,就會導緻很多XA相關的複制問題和bug。

Bug #87130 XA COMMIT not taken as transaction boundary

https://bugs.mysql.com/bug.php?id=87130

XA PREPARE與xtrabackup不能協同工作

主要原因是mysql執行XA PREPARE語句期間沒有擷取COMMIT全局意向鎖,導緻它不能被xtrabackup阻塞住,就可能發生xtrabackup備份的資料丢失了prepared事務的嚴重問題,導緻資料不一緻。

Bug #84442 XA PREPARE inconsistent with XTRABACKUP

https://bugs.mysql.com/bug.php?id=84442

XA COMMIT在其他連接配接中執行時與xtrabackup不能協同工作

如果XA COMMIT語句不是在目前XA事務所在的連接配接中執行的,而是目前連接配接斷開後重新連接配接mysqld然後執行XA COMMIT,那麼此時XA COMMIT不會擷取全局COMMIT意向鎖,也就不能被FTWRL阻擋住,于是就不能與xtrabackup正确地協同工作,導緻資料不一緻的嚴重問題。

Bug #84323 XA-COMMIT may not be blocked by FTWRL

https://bugs.mysql.com/bug.php?id=84323

mysqld 被mysqld.sock.lock阻擋而無法啟動

mysql實作了一個防止重複啟動mysqld的機制,就是在防止mysqld.sock的路徑下面增加了一個mysql.sock.lock,這個檔案裡面記錄着mysqld的程序号。當mysqld啟動時刻,會檢查這個檔案記憶體儲的程序号,向該程序發送一個信号0,如果發送成功,就認為這個程序是已經在運作的mysqld程序,于是目前正在啟動的mysqld程序就退出了。

我們在做分布式事務的容災測試期間,需要無數次反複kill掉和重新開機mysqld,偶爾會遇到mysqld無法啟動的情況,報錯就是 Another process with pid xxx is using unix socket file.

不過這個方法有個明顯的漏洞,就是新的mysqld程序号向mysql.sock.lock裡面存儲的程序号XXX發送信号時,可能剛好有個程序的pid是這個數字XXX。這種事情發生的幾率極低(因為linux 配置設定程序号的方式,并且我們的mysqld重新啟動的間隔很短),但是不是不可能。是以我的改進方案就是在發送信号0 成功後,确認該程序的binary檔案名是'mysqld'。如果檢查仍然通過,那麼還有可能這是另一個端口下的mysqld程序,盡管幾率極低。是以我會做進一步的檢查: 檢查這個mysqld程序的啟動參數中的--port=NUM 的NUM是目前正在啟動中的mysqld程序的端口号。隻有這些檢查都通過了,才認為目前mysqld程序号确實是重複啟動了,應該退出。

mariadb sleep時長不精确

這個是TDSQL的自研功能的bug修複。tdsql-mariadb-10.1.9 中,我們為了使sleep()函數避免出現因為機器時間被調整而導緻sleep()無法按時傳回的問題,重新實作了sleep()函數.

修改後,tdsql-mariadb-10.1.9提供了兩種sleep()函數的實作,第一種是我的新的實作方案,可以避免機器時間被調整而導緻sleep()無法按時傳回的問題,并且精度與官方實作相同,預設使用該方案;另外,可以通過動态設定全局變量use_tdsql_sleep_func=0,來使用mariadb官方的預設sleep()函數實作。

MariaDB InnoDB表空間檔案頭标志字段bug

這是我在MariaDB官網上面送出的bug報告:

https://jira.MariaDB.org/browse/MDEV-9581

這個bug的症狀是MariaDB-10.1.9無法使用MariaDB-10.0.10産生的資料目錄:當指定datadir為這樣一個目錄時,mysqld程序在啟動過程中發現錯誤而退出了。也就是說,如果你之前在使用MariaDB-10.0.x,有一個資料目錄,裡面有你珍貴的資料,然後現在你打算使用MariaDB-10.1.x,你會痛苦地發現,MariaDB-10.1.x無法用原來的資料目錄啟動,也就無法使用mysql_upgrade來更新資料目錄。雖然可以使用dump/load的方法來規避問題,但是在資料量很大的情況下,這樣做非常耗時,效率很低。這個bug已經修複,經過驗證可以正确工作并解決相關問題。

繼續閱讀