InnoDB:Failingassertion:page_get_n_recs(page)>1

zhaiwx_yinfeng發表於2016-05-10
我們知道,在MySQL5.5裡,insert buffer換了個說法,叫change buffer,能夠快取對二級索引的操作,直到將實際的page讀入bp時才進行合併,這在IO-bound並且表上有很多二級索引時,可以有效提升效能。

但存在一個蛋疼的bug,在5.5.31版本才被徹底fix掉.如果你不幸碰到如下斷言錯誤crash,那麼恭喜你中招了:
                InnoDB: Failing assertion: page_get_n_recs(page) > 1


這個問題最初可以追溯到2012年1月份的中旬,春節前三四天,當時一個線上庫不幸觸發該bug,導致crash,並且無法重啟。 當時的處理方式是用innodb froce recovery起來,同時關掉innodb purge thread(另外一個bug,設定一個較大的innodb force recovery無法啟動mysqld),然後dump資料,重建庫..

比較早的關於該bug的討論見:http://bugs.mysql.com/bug.php?id=61104, 但bug#61104並沒用完全修復該bug,只是將斷言移除了而已,這樣使用者可以把例項起來,執行一次DDL來重建表的二級索引

後來Percona的Alexey Kopytov 在buglist上提出了該bug的導致的根本原因(http://bugs.mysql.com/bug.php?id=66819)在於刪除ibuf記錄和應用Ibuf記錄並不是原子的,也就是不在一個mtr中,那麼在不恰當的時間點掛掉,就可能導致無法crash recovery,實際上,即使我們將innodb_change_buffer設定inserts也不是安全的。。。。

再後來,這個bug被fix掉了,但我看了diff後,發現fix的不完整,DELETE的場景依然存在問題。於是俺在Facebook上吐槽了下,Percona的Valeriy Kravchuk很熱心的幫忙確認了..


主要涉及兩個函式

>ibuf_merge_or_delete_for_page,是對一個block上記錄進行change buffer合併的主要函式;

對於Purge操作,即IBUF_OP_DELETE型別:
先執行ibuf_delete後,會直接進行ibuf_btr_pcur_commit_specify_mtr,提交redo,然後再去刪除ibuf記錄(ibuf_delete_rec)

>ibuf_delete_rec,每完成一次change buffer記錄的合併,都會呼叫該函式去從Ibuf tree中將其刪除

在ibuf_delete_rec函式中,當進行btr_cur_optimistic_delete失敗後,會先去commit mitr(呼叫ibuf_btr_pcur_commit_specify_mtr),再去開啟新的mtr做btr_cur_pessimistic_delete


我們知道,Innodb是通過mtr寫入的redo日誌來做crash recovery的,如果我們在merge資料成功和刪除ibuf記錄之間crash掉,那麼就可能資料記錄被更新了,但ibuf記錄還沒被刪除,從而觸發前面提到的斷言失敗(在做ibuf_delete時,想刪除記錄,卻發現page上的記錄已經刪光了…)

也就是說,實際上對於所有的change buffer操作型別都可能存在問題,只是對於purge操作,概率更高點,因為它有兩個風險點


官方第一次fix,在MySQL5.5.29裡,修復了ibuf_delete_rec中存在的問題,方式是先標記刪除ibuf entry,再做悲觀刪除

官方第二次fix,在MySQL5.5.31裡,修復ibuf_merge_or_delete_for_page中存在的問題


相關文章