由于MySQL的事务日志包含二进制日志和存储引擎日志,当发生崩溃恢复时,MySQL主节点通过redo log进行恢复,而在主从复制的环境下,slaver节点是依据于主节点的binlog进行同步数据的;这样的架构于是对mysql的二进制日志和redo log,就有两个基本要求:第一,保证binlog里面存在的事务一定在redo log里面存在,也就是binlog里不会比redo log多事务(可以少,因为redo log里面记录的事务可能有部分没有commit,这些事务最终可能会被rollback)。 2、顺序一致,这也是很重要的一点,假设两者记录的事务顺序不一致,那么会出现类似于主库事务执行的顺序是ta, tb, tc,td,但是binlog里面记录的是ta,tc, tb, td,binlog复制到从库后导致主从的数据不一致。为了达到上面说的两点,mysql是怎么来实现的呢?没错,答案是内部xa事务(核心是2pc)
一、二阶段事务提交流程: (1)先调用binglog_hton和innobase_hton的prepare方法完成第一阶段,binlog_hton的papare方法实际上什么也没做,innodb的prepare将事务状态设为TRX_PREPARED,并将redo log刷磁盘
(2)如果事务涉及的所有存储引擎的prepare都执行成功,则调用TC_LOG_BINLOG::log_xid将SQL语句写到binlog,此时,事务已经铁定要提交了。否则,调用ha_rollback_trans回滚事务,而SQL语句实际上也不会写到binlog。
(3)最后,调用引擎的commit完成事务的提交。实际上binlog_hton->commit什么也不会做(因为(2)已经将binlog写入磁盘),innobase_hton->commit则清除undo信息,刷redo日志,将事务设为TRX_NOT_STARTED状态
二、事务恢复流程:
Innodb在恢复的时候,不同状态的事务,会进行不同的处理(见trx_rollback_or_clean_all_without_sess函数):
<1>对于TRX_COMMITTED_IN_MEMORY的事务,清除回滚段,然后将事务设为TRX_NOT_STARTED;
<2>对于TRX_NOT_STARTED的事务,表示事务已经提交,跳过;
<3>对于TRX_PREPARED的事务,要根据binlog来决定事务的命运,暂时跳过;
<4>对于TRX_ACTIVE的事务,回滚。
简单来讲,当发生崩溃恢复时,数据库根据redo log进行数据恢复,逐个查看每条redo的事务状态,如果根据上图流程,已进行到TRX_NOT_STARTED阶段,也就是存储引擎commit阶段,那么说明redo log和bin log是一致的,正常根据redo进行恢复即可;事务状态为TRX_ACTIVE,不用说了,都没写到binlog中,直接回滚;但如果事务状态为TRX_PREPARED,就要分两种情况了,要先检查binlog是否已写入成功?如果没写入成功,那么就算是TRX_PREPARED状态,也要回滚。如果写入成功了,那么就进行最后一步,调用存储引擎commit,更改事务状态为TRX_NOT_STARTED,也就是真正提交状态,可以用作数据恢复。
从以上分析可以看出,写binlog是一定已经提交的数据,只要写了binlog,事务是铁定要提交成功的。因为主从复制环境下,写了binlog就可能直接传输到从节点应用了,所以两阶段提交很好的保持数据一致性和顺序性
三、相关参数介绍:
1、innodb_support_xa
用于控制innodb是否支持XA事务的2PC,默认是TRUE。如果关闭,则innodb在prepare阶段就什么也不做;这可能会导致binlog的顺序与innodb提交的顺序不一致(比如A事务比B事务先写binlog,但是在innodb内部却可能A事务比B事务后提交),这会导致在恢复或者slave产生不同的数据。
2、sync_binlog
Mysql在提交事务时调用MYSQL_LOG::write完成写binlog,并根据sync_binlog决定是否进行刷盘。默认值是0,即不刷盘,从而把控制权让给OS。如果设为1,则每次提交事务,就会进行一次刷盘;这对性能有影响(5.6已经支持binlog group),所以很多人将其设置为100。
3、innodb_flush_log_at_trx_commit
innodb_flush_log_at_trx_commit有0、1、2三个值分别代表不同的使redo log落地策略。0表示每秒进行一次flush,但是每次事务commit不进行任何操作(每秒调用fsync使数据落地到磁盘,不过这里需要注意如果底层存储有cache,比如raid cache,那么这时也不会真正落地,但是由于一般raid卡都带有备用电源,所以一般都认为此时数据是安全的)。1代表每次事务提交都会进行flush,这是最安全的模式。2表示每秒flush,每次事务提交时不flush,而是调用write将redo log buffer里面的redo log刷到os page cache。
那现在来比较三种策略的优劣势:1由于每次事务commit都会是redo log落地所以是最安全的,但是由于fsync的次数增多导致性能下降比较厉害。0表示每秒flush,每次事务提交不进行任何操作,所以mysql crash或者os crash时会丢失一秒的事务。2相对于0来说了多了每次事务commit时会有一次write操作,此时数据虽然没有落地到磁盘但是只要没有 os crash,即使mysql crash,那么事务是不会丢失的。2相对于0来说会稍微安全一点点。
上面3个参数不同的值会带来不同的效果。三者都设置为1(TRUE),数据才能真正安全。sync_binlog非1,可能导致binlog丢失(OS挂掉),从而与innodb层面的数据不一致。innodb_flush_log_at_trx_commit非1,可能会导致innodb层面的数据丢失(OS挂掉),从而与binlog不一致
文章题目:【MYSQL】两阶段提交及相关参数介绍
文章转载:
http://kswjz.com/article/gehohh.html