经过gdb core后,大体知道了发生错误的原因:
TokuDB在创建子事务的时候,由于嵌套事务过多,FT-index直接返回了错误(TokuDB的嵌套事务最多允许256个,嵌套事务数目的类型为uint8_t)导致。
比较诡异的是主库一切正常,无任何错误。
经过沟通,发现用户使用了大量的savepoint,这是一个突破点。
这里我们分两种情况来处理,先来看第一种,SQL1:
savepoint s0;
insert into tb1 values(0);
release savepoint s0;
insert into tb1 values(1);
release savepoint s1;
commit;
TokuDB将会这样处理:
savepoint 间的事务并非嵌套,而是用完就释放,然后再重新创建,这样不会导致事务栈溢出。
再来看另外一种情况,SQL2:
set autocommit=0;
savepoint s0;
savepoint s1;
insert into tb1 values(1);
insert into tb1 values(2);
...
commit;
针对开始时的问题,TokuDB内核君进行了大胆猜测:
在备库执行的是SQL2,事务栈溢出了;
通过翻看 binlog 代码印证了我们的猜测,在 sql/binlog.cc 里只实现了 binlog_savepoint_set
方法,只记录savepoint event到binlog,而未记录release savepoint event!
由于savepoint实现机制不同,对InnoDB引擎来说是没有问题的,但对 TokuDB 来说就是灾难了。解决办法就是实现方法,记录release savepoint event到binlog。
ApsaraDB MySQL 5.6 最新版已修复这个问题,对savepoint有需求的TokuDB用户(InnoDB不受影响)做下升级即可。
最后广告还是来了: 我们的PB级云数据库 ,底层采用TokuDB引擎,存储介质是普通的SATA盘,有兴趣的同学可以去围观下。