经过gdb core后,大体知道了发生错误的原因:

TokuDB在创建子事务的时候,由于嵌套事务过多,FT-index直接返回了错误(TokuDB的嵌套事务最多允许256个,嵌套事务数目的类型为uint8_t)导致。

比较诡异的是主库一切正常,无任何错误。

经过沟通,发现用户使用了大量的savepoint,这是一个突破点。

这里我们分两种情况来处理,先来看第一种,SQL1

  1. savepoint s0;
  2. insert into tb1 values(0);
  3. release savepoint s0;
  4. insert into tb1 values(1);
  5. release savepoint s1;
  6. commit;

TokuDB将会这样处理:

savepoint 间的事务并非嵌套,而是用完就释放,然后再重新创建,这样不会导致事务栈溢出。

再来看另外一种情况,SQL2

  1. set autocommit=0;
  2. savepoint s0;
  3. savepoint s1;
  4. insert into tb1 values(1);
  5. insert into tb1 values(2);
  6. ...
  7. commit;

针对开始时的问题,TokuDB内核君进行了大胆猜测:

  1. 在备库执行的是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盘,有兴趣的同学可以去围观下。