遭遇bugInnoDB:Failingassertion:page_get_n_recs(page)>1


昨晚遇到了一个MySQL 的bug  : 

170112 22:26:06  InnoDB: Assertion failure in thread 139900811020032 in file /export/home/pb2/build/sb_0-2859905-1295553452.13/mysql
-5.5.9/storage/innobase/ibuf/ibuf0ibuf.c line 4147
InnoDB: Failing assertion: page_get_n_recs(page) > 1
InnoDB: We intentionally generate a memory trap.
InnoDB: Submit a detailed bug report to http://bugs.mysql.com.
InnoDB: If you get repeated assertion failures or crashes, even
InnoDB: immediately after the mysqld startup, there may be
InnoDB: corruption in the InnoDB tablespace. Please refer to
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/forcing-innodb-recovery.html
InnoDB: about forcing recovery.
170112 22:26:06 - mysqld got signal 6 ;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.
We will try our best to scrape up some info that will hopefully help diagnose
the problem, but since we have already crashed, something is definitely wrong
and this may fail.



数据库自动重启 做recovery 的时候陷入死循环. 无法启动数据库. 


关于这个bug  搜索了一下网络 : 

一下数据转自云栖社区 


摘要: 我们知道,在MySQL5.5里,insert buffer换了个说法,叫change buffer,能够缓存对二级索引的操作,直到将实际的page读入bp时才进行合并,这在IO-bound并且表上有很多二级索引时,可以有效提升性能。 但存在一个蛋疼的bug,在5.5.31版本才被彻底f...
我们知道,在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,再做悲观删除
http://bazaar.launchpad.net/~mysql/mysql-server/5.5/revision/3979




官方第二次fix,在MySQL5.5.31里,修复ibuf_merge_or_delete_for_page中存在的问题
http://bazaar.launchpad.net/~mysql/mysql-server/5.5/revision/4177


网页标题:遭遇bugInnoDB:Failingassertion:page_get_n_recs(page)>1
网站链接:http://scyanting.com/article/goieoo.html