最近QA在对InnoSQL-5.5.30-v5版本做回归测试时发现数据不一致的问题,简单来说这个测试用例为在主机上进行全备后恢复,会发现数据不一致的情形。因为我们使用全INSERT操作来进行测试,所以可以非常简单的发现不一致的情形。但是这些测试在我们之前的v2版本中都是没有问题的。由于InnoSQL非常强调数据的一致性,因此这个问题在组内得到了极大的关注。小伙伴们测试了多个版本,发现官方MySQL 5.5、InnoSQL 5.5.20-v2版本都没有问题,但是官方MySQL 5.6和InnoSQL 5.5.30-v5版本都有这个问题。经过多方面的定位,最终确定了这个问题的源头——MySQL组提交的优化。
大家或许已经知道MySQL 5.6中修复了组提交的bug,这个修复最早是由MariaDB开发团队的大牛Kristian完成的,MySQL 5.6也借鉴了这样的实现,即一次fsync可以刷新多个事务的提交。在MySQL数据库中,组提交存在于上层binlog也存在于下层的InnoDB引擎层。来看一下组提交在MySQL 5.6中的实现:
从上图可以看到,在InnoDB存储引擎层每次每个事务进行prepare操作,都需要触发一次fsync操作,以此确保事务的prepare日志一定写入到redo日志( 当然,InnoDB引擎本身支持组提交,可能一次fsync刷新多个事务的prepare日志,但是这个完全是无法控制的行为 )。然后,一个组提交中的事务依次向MySQL的二进制日志写日志,这个写操作是缓存写,最后写完进行一次fsync操作。即一个fsync将多个事务的日志写入到了二进制日志,也就是通常所说的组提交。最后完成InnoDB引擎层面的提交操作,这个操作主要是将undo日志放入到History链表,释放锁等相关资源。 但是与MySQL 5.5不同是,最后InnoDB引擎层提交操作不再需要fsync 。
最后这样优化的优化也是Kristian提出并实现的,原因很简单,因为即使InnoDB引擎层的redo日志丢失,但是由于binlog日志已经写入(fsync确保),恢复时只要确定写入binlog的日志在InnoDB引擎层全部提交即可。可以看出,这个优化进一步减少了数据库的fsync次数,从而整体上提升了数据库的性能。InnoSQL 5.5修复了组提交的bug,同时也引入了这个优化。
但是,任何优化可能都会存在一些潜在的影响。这就是为什么MySQL 5.6和InnoSQL 5.5备份会导致数据不一致的问题。我们来看Xtrabackup的内部过程,这里进考虑InnoDB表的备份:
但是在MySQL 5.6中xtrackup会遇到问题,因为事务最后提交不再需要fsync操作,这意味这第6步骤拷贝的日志可能会不包括那些已经在步骤4中写入binlog的那些事务,而Xtrabackup对InnoDB的恢复仅通过redo日志回放。 虽然,Xtrabackup备份出来的InnoDB数据文件还是一致的,但是这些数据与binlog的位置是不一致的 。那么当通过备份重新建立一个slave服务器时,就可能会导致出错。Xtrackup 2.2.3版本解决了这个问题,即在执行步骤6时,先执行一步FLUSH ENGINE LOGS操作,确保重做日志通过fsync刷新到磁盘,从而避免binlog与备份文件的不一致。
各位在使用MySQL 5.6、MariaDB 10.0、InnoSQL版本时,务必确保Xtrabackup已经升级到了2.2.3版本。 最后,感谢QA组的同事们,每次在InnoSQL新版本发布前后,总能帮助我们发现很多“奇奇怪怪”的问题 。