MySQL · TokuDB · Savepoint漫谈

最后更新于:2022-04-01 10:35:45

## 问题描述 某TokuDB实例备库发生复制中断,报错信息甚是诡异: ~~~ Error executing row event: "Can't lock file (errno: 22 - Invalid argument)" ~~~ 经过gdb core后,大体知道了发生错误的原因: TokuDB在创建子事务的时候,由于嵌套事务过多,FT-index直接返回了错误(TokuDB的嵌套事务最多允许256个,嵌套事务数目的类型为uint8_t)导致。 比较诡异的是主库一切正常,无任何错误。 经过沟通,发现用户使用了大量的savepoint,这是一个突破点。 ## Savepoint机制 在TokuDB里savepoint的机制是个啥样子呢? 这里我们分两种情况来处理,先来看第一种,SQL1: ~~~ set autocommit=0; savepoint s0; insert into tb1 values(0); release savepoint s0; savepoint s1; insert into tb1 values(1); release savepoint s1; ... commit; ~~~ TokuDB将会这样处理: ~~~ a) savepoint s0: 创建s0子事务 b) release savepoint s0的时候: commit当前s0事务 c) savepoint s1: 创建s1子事务(此时s0子事务已不存在,已提交) d) release savepoint s1的时候: commit当前s1事务 ~~~ savepoint 间的事务并非嵌套,而是用完就释放,然后再重新创建,这样不会导致事务栈溢出。 再来看另外一种情况,SQL2: ~~~ set autocommit=0; savepoint s0; insert into tb1 values(0); savepoint s1; insert into tb1 values(1); savepoint s2; insert into tb1 values(2); ... commit; ~~~ 这种SQL TokuDB的处理方式是: ~~~ a) savepoint的时候: s1是s0的子事务,s2是s1的子事务,就这样嵌套下去直到事务栈溢出... b) commit的时候: 会先提交s2,然后提交s1,然后提交s0,最后整个事务提交 ~~~ ## 问题解决 针对开始时的问题,TokuDB内核君进行了大胆猜测: ~~~ 在主库执行的是SQL1,一切正常; 在备库执行的是SQL2,事务栈溢出了; ~~~ 通过翻看 binlog 代码印证了我们的猜测,在 sql/binlog.cc 里只实现了 `binlog_savepoint_set` 方法,只记录savepoint event到binlog,而未记录release savepoint event! 由于savepoint实现机制不同,对InnoDB引擎来说是没有问题的,但对 TokuDB 来说就是灾难了。解决办法就是实现`binlog_savepoint_release`方法,记录release savepoint event到binlog。 ApsaraDB MySQL 5.6 最新版已修复这个问题,对savepoint有需求的TokuDB用户(InnoDB不受影响)做下升级即可。 本篇小文从一个用户引发的问题出发,讨论了TokuDB的savepoint机制,当然TokuDB内核组的同学每天都会处理很多类似的问题,从发现问题到解决问题,以及持续的优化,为的就是给大家提供一种“飞”的感觉。有大数据量存储的同学不妨试试我们的 TokuDB 引擎,存储成本大大降低,顺便体验一把下一代存储引擎带来的“优越感”(注意,这不是广告)。 最后广告还是来了: 我们的PB级云数据库 [PetaData已开始公测](https://www.aliyun.com/product/petadata),底层采用TokuDB引擎,存储介质是普通的SATA盘,有兴趣的同学可以去围观下。
';