在看innodb_support_xa之前我们要先看下什么是XA,什么是两阶段提交。
XA
一个协调分布式事务的标准接口,为了遵从ACID原则,允许多个DB参与事务。为了查看更多,请 Section 13.3.7, “XA Transactions”。
通常情况下,默认都启用了XA分布式事务支持。如果你没有用这个特性,你可以配置innodb_support_xa,避免为每个事务执行fsync带来的性能损耗。
在MySQL 5.7.10版本不启用innodb_support_xa,因为它使主备复制不安全和降低了与二进制日志group commit相关的性能。
在MySQL 8.0中这个innodb_support_xa选项被移除掉了。
两阶段提交
简言之,两阶段提交是在XA规范下,分布式事务里面的一部分操作(有时也被称为2PC)。当事务里面有多个DB操作时,要么都提交,要么都回滚。下面的图更容易理解些。
下面的一段引用沃趣科技 MySQL数据库专家 @pickup112,找不到原文请见谅
第一阶段:
首先,协调者在自身节点的日志中写入一条的日志记录,然后所有参与者发送消息prepare T,询问这些参与者(包括自身),是否能够提交这个事务;
参与者在接受到这个prepare T 消息以后,会根据自身的情况,进行事务的预处理,如果参与者能够提交该事务,则会将日志写入磁盘,并返回给协调者一个ready T信息,同时自身进入预提交状态状态;如果不能提交该事务,则记录日志,并返回一个not commit T信息给协调者,同时撤销在自身上所做的数据库改;
参与者能够推迟发送响应的时间,但最终还是需要发送的。
第二阶段:
协调者会收集所有参与者的意见,如果收到参与者发来的not commit T信息,则标识着该事务不能提交,协调者会将Abort T 记录到日志中,并向所有参与者发送一个Abort T 信息,让所有参与者撤销在自身上所有的预操作;
如果协调者收到所有参与者发来prepare T信息,那么协调者会将Commit T日志写入磁盘,并向所有参与者发送一个Commit T信息,提交该事务。若协调者迟迟未收到某个参与者发来的信息,则认为该参与者发送了一个VOTE_ABORT信息,从而取消该事务的执行。
参与者接收到协调者发来的Abort T信息以后,参与者会终止提交,并将Abort T 记录到日志中;如果参与者收到的是Commit T信息,则会将事务进行提交,并写入记录。
一般情况下,两阶段提交机制都能较好的运行,当在事务进行过程中,有参与者宕机时,他重启以后,可以通过询问其他参与者或者协调者,从而知道这个事务到底提交了没有。当然,这一切的前提都是各个参与者在进行每一步操作时,都会事先写入日志。
引用结束
innodb_support_xa
innodb_support_xa的作用?
innodb_support_xa可以开关InnoDB的xa两段式事务提交。
如何开启?
innodb_support_xa=true,支持xa两段式事务提交。
如何运转?
innodb_support_xa=true,支持xa两段式事务提交。此时MySQL首先要求innodb prepare,对应的redolog 将写入log buffer;如果有其他的引擎,其他引擎也需要做事务提交的prepare,然后MySQL server将binlog将写入;并通知各事务引擎真正commit;InnoDB将commit标志写入,完成真正的提交,响应应用程序为提交成功。这个过程中任何出错将导致事务回滚,响应应用程序为提交失败。也就是说,在这种情况下,基本不会出错。
为什么启用innodb_support_xa?
如果不启用innodb_support_xa,事务再写到binlog文件时可能会和现在的db在commit的时候顺序不一样,当这些binlog应用到恢复数据库或者在备库执行时,可能会产生不一样的数据
它可以保证InnoDB两阶段提交(prepare,commit).这不仅仅在用户发起的XA,在内部的XA协调InnoDB事务日志和MySQL的binlog日志,保证数据一致。数据一致是一个非常非常重要。内部的XA就是MySQL Server层,即以binlog为准。
开启innodb_support_xa带来的影响?
如果在XA事务里面启用了InnoDB支持两阶段提交,在事务准备阶段将会带来额外的刷盘操作,性能影响会达到5%,所有为了提高性能,有些DBA会设置innodb_support_xa=false。这样的话,redolog和binlog将无法同步,可能存在事务在主库提交,但是没有记录到binlog的情况。这样也有可能造成事务数据的丢失。
为什么会影响性能?
我们来看下两阶段提交的过程:
Prepare InnoDB [ha_prepare]:
1.1 Write prepare record to log buffer
1.2 fsync() log file to disk (this can currently do group commit)
1.3 Take prepare_commit_mutex
Log transaction to binary log [TC_LOG_BINLOG::log_xid]:
2.1 Lock binary log
2.2 Write transaction data to binary log
2.3 Sync binary log based on sync_binlog. This forces the binlog to always fsync() (no group commit) due to prepare_commit_mutex
2.4 Unlock binary log
Commit InnoDB:
3.1 Release prepare_commit_mutex
3.2 Write commit record to log buffer
3.3 Sync log buffer to disk (this can currently do group commit)
3.4 InnoDB locks are released
从上面的步骤里面,我们可以看到会调用会刷redolog和binlog,那会势必会影响写入的性能。那么这个可以控制刷盘的速度吗?可以。
redolog的控制参数是innodb_flush_log_at_trx_commit
值 | 描述 |
---|---|
0 | 一秒InnoDB log buffer刷新一次到硬盘。在事务commit时不刷新log buffer到log file.由于调度问题,不保证一秒一刷新的操作一定能发生。mysqld crash后导致丢失最后一秒的事务。 |
1 | 出于完全遵从ACID原则的目的,默认值是1.在每个事务commit时,InnoDB log buffer里面的内容都要写入log file,并且log file刷到磁盘。With this value, the contents of the InnoDB log buffer are written out to the log file at each transaction commit and the log file is flushed to disk. |
2 | 每个事务提交后,InnoDB log buffer里面的内容都要写入log file,log file大约每秒刷一次盘。the contents of the InnoDB log buffer are written to the log file after each transaction commit and the log file is flushed to disk approximately once per second.由于进程调度问题,每秒刷一次盘不保证100%的都能发生。Once-per-second flushing is not 100% guaranteed to happen every second, due to process scheduling issues. 因为innodb_flush_log_at_timeout.默认是1秒,操作系统crash或电源故障导致丢失最后一秒的事务。 |
InnoDB log 的刷新频率是由 innodb_flush_log_at_timeout 控制的, 这个值默认是1单位秒.
以下两种情况不受innodb_flush_log_at_trx_commit的限制:
- DDL和其他InnoDB内部活动如果要刷新InnoDB日志的话
- InnoDB crash recovery 工作, 事务要么整个被应用要么整个被回滚
当然这里还有另外一个比较重要的参数innodb_flush_method,打开连接就可以了,在这里就不再说了。
binlog的控制参数是sync_binlog
默认参数是0。如果这个值比0大,在sync_binlog把commit groups提交到二进制日志,MySQL Server就会指定 通过调用fdatasync()的方式把binlog同步到磁盘。
值 | 描述 |
---|---|
0 | 不用同步到磁盘。在这种情况下,MySQL Server就依赖操作系统像刷新其他文件一样,不时地刷新binlog日志。 |
1 | 最安全的选择,当MySQL crash后,在binlog里面最多丢失一个commit group的数据。然而,这也是最慢的选择(除非磁盘有个电池cache,可以让同步更快)。 |
对于InnoDB事务最安全的做法是:
sync_binlog=1
innodb_flush_log_at_trx_commit=1
下面的情况可以关闭innodb_support_xa,即innodb_support_xa=false:
- 可以接受在服务器上只有一个线程修改数据,对于InnoDB表为了提高性能可以关闭这个选项
- 当只有一个SQL线程修改数据的时候,可以在slave上关闭innodb_support_xa
- 不需要保证binlog或者主备复制的数据安全,不需要一个外部的XA事务管理者时,你可以关闭这个选项
参考
https://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html#sysvar_innodb_support_xa
https://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_xa
https://dev.mysql.com/doc/refman/5.6/en/replication-options-binary-log.html#sysvar_sync_binlog
https://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit
http://mysqlmusings.blogspot.com/2010/04/binary-log-group-commit-implementation.html
https://linux.cn/article-1157-2.html