http://www.mysqlops.com/2012/05/23/mysql-innodb-group-commit-1.html
MySQL数据库InnoDB存储引擎的Group Commit(一)
五月 23, 2012 by admin · 3 Comments
文章转载自:www.orczhou.com
这个问题由来已久,Kristian Nielsen连续写了四篇文章《Fixing MySQL group commit》(part 1 |part 2 | part 3 | part 4 )深入细致的分析了“故事”的前因后果。本文完全没有任何新意,仅做一个小的总结。这里会先介绍一下什么是“Group Commit”,MySQL/InnoDB存储引擎里面的Group Commit为什么引起如此大的关注,现在是怎么解决问题的。
简单的,InnoDB在每次提交事务时,为了保证数据已经持久化到磁盘(Durable),需要调用一次fsync(或者是fdatasync、或者使用O_DIRECT选项等)来告知文件系统将可能在缓存中的数据刷新到磁盘(更多关于fsync)。 而fsync操作本身是非常“昂贵”的(关于“昂贵”:消耗较多的IO资源,响应较慢):传统硬盘(10K转/分钟)大约每秒支撑150个fsync操 作,SSD(Intel X25-M)大约每秒支撑1200个fsync操作。所以,如果每次事务提交都单独做fsync操作,那么这里将是系统TPS的一个瓶颈。所以就有了 Group Commit的概念。
当多个事务并发时,我们让多个都在等待fsync的事务一起合并为仅调用一次fsync操作。这样的一个简单的优化将大大提高系统的吞吐量,Yoshinori Matsunobu的实验表明,这将带来五到六倍的性能提升。
MySQL5.0开始,InnoDB就不再支持Group Commit了。在2005年,Peter Zaitsev就将此作为一个Bug提交,直到2009年在InnoDB Plugin1.0.4才将此问题修复。
究其更本原因发现,5.0之后MySQL数据库开始支持“分布式事务和两阶段提交协议”(distributed transactions and Two Phase Commit /2PC),在代码实现时为了保证Binlog中的事务顺序和InnoDB Log(Transaction Log或者叫redo log)事务顺序一致,被动放弃了Group Commit。如果Binlog顺序不一致,那么备库就无法确保和主库有一致的数据。参考上面的Part 2给出代码相关的解释:
InnoDB本身的代码实现是可以支持Group Commit的,我们来看下面的代码:
innobase_commit_low(trx);
trx->flush_log_later = FALSE;
上面的第二个阶段数据持久化是比较慢的,所以当多线程并发完成第一阶段后,可以一起只调用一次fsync来持久化全部数据(fsync应该是以文件描述符为单位的)。
如果打开了二进制日志,那么代码就有些不一样了。MySQL数据库使用了“两阶段提交”(XA/2-phase commit )来保证存储引擎和二进制日志的一致,在上面的过程之前有多了一步:
write() and fsync() binary log
innobase_commit()
在innobase_xa_prepare函数中需要获取排他锁prepare_commit_mutex,直到上面的innodb_commit结束锁才释放,所以上面的binlog write and fsync也就无法Group操作了(当binlog_sync=1时)。当设置Binlog_sync=1时,InnoDB就无法做Group_commit了,如果prepare_commit_mutex提前释放了,则可能导致InnoDB内部的事务顺序和Binlog内部事务的顺序不一致(这里值得商榷,参考下面的参考文献6)。
Facebook作为一个SNS(Social network service)网站,在互联网界已经创造了奇迹。在技术领域Facebook也为开源社区(包括MySQL PHP Linux等)做了很多的杰出的贡献,希望我们公司能以此为目标。
开源社区为此也展开了很多讨论,Mark Callaghan(Facebook)提出的”Ticket System“目前看到的最简洁而有效的实现。
Facebook尝试两种方式实现,一,通过一个InnoDB的UT_LIST链表来确保InnoDB和Binlog内部的顺序一致;二,实现一个 简单的“Ticket System”,当多个线程都进入innobase_xa_prepare()阶段时,就分配一个”ticket”值,并且分配的ticket值是单调增 加的。当线程的binlog准备commit时,必须保证小于当前ticket的binlog都提交了,如果这时有多个事务的binlog则可以同时合并 提交。
这里可以看到相关的性能测试情况:Laying the foundatation for group commit
在InnoDB Plugin 1.0.4声称解决了这个问题。下面是Plugin手册上的一段原话:
Beginning with InnoDB Plugin 1.0.4, the group commit functionality inside InnoDB works with the Two Phase Commit protocol in MySQL. Re-enabling of the group commit functionality fully ensures that the ordering of commit in the MySQL binlog and the InnoDB logfile is the same as it was before.
相关的性能测试数据。(sync_binlog = 0时)
很遗憾,我还不知道具体的实现细节,也还没有去检查源代码看看有没有相关实现说明。
Update 2011-07-25: InnoDB Plugin关于Group commit代码在storage/innobase/trx/trx0trx.c中实现,可以尝试阅读。
Update(2011-11-06):当sync_binlog = 0,MySQL并不能Group commit。
Update(2011-12-20):当打开二进制日志时,MySQL如何提交事务(这是一个两阶段提交):
binlog_prepare (do nothing)
innodb_prepare
…
binlog_commit
innobase_commit
1. Binary Log Group Commit – An Implementation Proposal
2. Ease of Switching to the InnoDB Plugin and the Numerous Benefits
3. Eventually consistent Group Commit
4. Innodb plugin 1.0.4 released – great job Innobase
5. The followings are just my current understanding about broken group commit
6. Fixing MySQL group commit part 1 | part 2 |part 3 | part 4