InnoDB: Failing assertion: trx->isolation_level == TRX_ISO_READ_UNCOMMITTED

简介:

最近再次碰到之前遇到的断言失败的bug,错误信息如下:

InnoDB: Failing assertion: trx->isolation_level == TRX_ISO_READ_UNCOMMITTED

 

这是一个已知的bug(bug#62037),MySQL5.5.22版本中被fix掉,在lauchpad上可以看到具体是如何修复的

 

如何重现

重现case,使用gdb的non-stop模式很容易重现:

启动gdb,使用non-stop

set target-async 1

set pagination off

set non-stop on



断点:

row0upd.c:2033   (Percona Server5.5.18)



case:



session 1:

 CREATE TABLE `t1` (   `a` int(11) DEFAULT NULL,   `b` text,   `c` text ) ENGINE=InnoDB DEFAULT CHARSET=gbk;

insert into t1 values (1,repeat(‘b’, 7000), repeat(‘c’, 100));

update t1 set c = concat(c, repeat(‘c’, 2000));

这时候会因为更新列c而导致b列的数据被外部存储(b的列长最大),停在断点

session 2:执行查询

select * from t1 where a = 1;

该bug主要包含两个问题:


问题一:运行时断言失败
主要原因是:
1.更新记录时,undo中只记录了那些被更新的列,而由于其他列更新,导致某个列需要外部存储时,这个列的值不会写入undo。
也就是说,在从函数btr_cur_pessimistic_update中返回后,记录上某个未被更新的列,可能存储的是无效的指针
例如上述场景b列被选出来外部存储,因为它的长度最大,因此b列的数据被修改成一个尚未生效的值全为0的指针;
在悲观更新返回后,由于先mtr commit,再更新外部存储数据,这导致block及索引上的排他锁都被释放掉;这时候其他连接的查询是可以看到这条记录的。
2.虽然MVCC保证查询可以看到修改过的列,但1提到的未修改但被选作外部存储的列没有做undo,因此查询看到的是指针,导致触发断言失败,实例crash
解决:
官方的解决办法是在悲观更新和更新完外部存储列后,才进行mtr commit,这可以保证中间不会有其他查询看到未完成的更新(被阻塞住)


问题二:断言失败crash后,无法crash recovery
原因:
如果在完成悲观更新和写入外部存储列的过程中crash,记录更新可能完成了,但外部存储列是失败的,在crash recovery后,记录依然维持在不一致的状态。这样只要一访问该记录,就会报和问题一一样的断言crash错误
解决:

将上述逻辑修改为:

1.对原记录进行修改,mtr不提交(用btr_mtr代表该mtr)
2.扩展新的外部存储页(不能使用btr_mtr之前释放掉的page,暂时没搞明白为什么…),对新的外部存储页的写入(包括初始化)使用另外一个mtr(称为blob_mtr),对记录上指针的更新使用btr_mtr
3.commit blob_mtr
4.commit btr_mtr


这样在crash recovery的时候,就会先恢复blob页,再恢复记录上的操作;最差的情况就是丢失更新,ibd中存在一个有部分数据的无效blob页,但记录本身仍然能保持一致的状态

 

另外在插入记录时也可能触发该bug,例如,当一条记录被标记删除,但未被purge掉时,如果再次插入该聚集索引记录,被标记删除的记录就会被更新成现在的记录,这时候触发bug的逻辑就和上述Update的逻辑类似了。


相关文章
|
JavaScript 关系型数据库 MySQL
InnoDB: Failing assertion: format != 0
InnoDB: Failing assertion: format != 0
1628 0
|
2月前
|
关系型数据库 MySQL 数据库
MySQL谈谈InnoDB怎么解决幻读的
MySQL谈谈InnoDB怎么解决幻读的
21 2
|
2月前
|
存储 关系型数据库 MySQL
MySQL InnoDB数据存储结构
MySQL InnoDB数据存储结构
|
2月前
|
存储 缓存 关系型数据库
MySQL的varchar水真的太深了——InnoDB记录存储结构
varchar(M) 能存多少个字符,为什么提示最大16383?innodb怎么知道varchar真正有多长?记录为NULL,innodb如何处理?某个列数据占用的字节数非常多怎么办?影响每行实际可用空间的因素有哪些?本篇围绕innodb默认行格式dynamic来说说原理。
839 6
MySQL的varchar水真的太深了——InnoDB记录存储结构
|
3月前
|
存储 缓存 关系型数据库
MySQL - 存储引擎MyISAM和Innodb
MySQL - 存储引擎MyISAM和Innodb
|
27天前
|
存储 关系型数据库 MySQL
MySQL引擎对决:深入解析MyISAM和InnoDB的区别
MySQL引擎对决:深入解析MyISAM和InnoDB的区别
36 0
|
1天前
|
存储 SQL 关系型数据库
mysql中MyISAM和InnoDB的区别是什么
mysql中MyISAM和InnoDB的区别是什么
8 0
|
6天前
|
存储 关系型数据库 MySQL
【MySQL系列笔记】InnoDB引擎-数据存储结构
InnoDB 存储引擎是MySQL的默认存储引擎,是事务安全的MySQL存储引擎。该存储引擎是第一个完整ACID事务的MySQL存储引擎,其特点是行锁设计、支持MVCC、支持外键、提供一致性非锁定读,同时被设计用来最有效地利用以及使用内存和 CPU。因此很有必要学习下InnoDB存储引擎,它的很多架构设计思路都可以应用到我们的应用系统设计中。
28 4
|
11天前
|
存储 监控 关系型数据库
【MySQL】InnoDB 什么情况下会产生死锁
【MySQL】InnoDB 什么情况下会产生死锁