MySQL事务提交和崩溃恢复

MySQL中的XA协议

有一个名叫X/Open的组织提出了一个名为XA的规范。这个XA规范提出了2个角色:

  • 一个全局事务由多个小的事务组成,所以我们得在某个地方找一个总揽全局的角色用于和各个小事务进行沟通,指导它们是提交还是回滚。这个角色被称作事务协调器(Transaction Coordinator)。
  • 管理一个小事务的角色被称作事务管理器(Transaction Manager)。

要提交一个全局事务,那么属于该全局事务的若干个小事务就应该全部提交,只要有任何一个小事务无法提交,那么整个全局事务就应该全部回滚。XA规范中指出,要提交一个全局事务,必须分为2步:

  • Prepare阶段:当协调器准备提交一个全局事务时,会依次通知各个管理器把在事务执行过程中所产生的数据都刷盘。
  • Commit阶段:如果在Prepare阶段各个管理器都完成了数据的刷盘,那么协调器就要真正通知各个管理器去提交事务了,否则就需要让这些管理器回滚事务了。

XA规范把上述全局事务提交时所经历的两个阶段称作两阶段提交。在单个MySQL实例中,将server层作为事务协调器,存储引擎作为事务管理器,故本文将binlog作为事务协调器。

sql执行流程

sql提交到MySQL时需要进行词法语法分析、优化(如果没有命中索引,就会扫全表),才会执行:

图片[1]-MySQL事务提交和崩溃恢复-不念博客
sql执行流程

事务执行流程

假设我们要更新一条数据,语句如下:

update T set c=c+1 where ID=2;
  1. Server层的执行器先调用引擎取出ID=2这一行。ID是主键,引擎直接用树搜索找到这一行。如果 ID=2这一行所在的数据页本来就在内存中,就直接返回给执行器;否则需要先从磁盘读入内存,然后再返回。
  2. 执行器拿到数据把这个值+1(分配trx_id,开始记录事务),得到新的数据,再调用存储引擎接口写入这行新数据。此处会先记录undolog,并将undolog对应的变化信息redolog保存到log buffer中,然后再去修改buffer pool,并且把buffer pool对应的变化信息redolog记录到log buffer中,详情见上文。
  3. InnoDB做完上述操作后,就准备提交事务了。此时处在Prepare阶段,执行器调用binlog_prepare接口,就会将上文提到的undo segment的状态置为TRX_UNDO_PREPARED,并将本次提交事务的XID也写入其中,同时生成对应的redolog。此时根据redolog的刷盘策略,本次事务对应的log buffer可能会被刷盘,而只要log buffer刷盘成功,那么即使之后系统崩溃,在重启恢复的时候也可以将处于Prepare状态的事务完全恢复(恢复buffer pool和undolog),然后回滚或者再次提交事务。
  4. 而到Commit阶段,执行器继续调用binlog_commit接口提交事务,此时会先将事务执行过程中产生的binlog(包括XID)按照binlog的刷盘策略刷入磁盘,再根据不同的操作类型把undo segment的状态转换成TRX_UNDO_CACHED、TRX_UNDO_TO_FREE或者TRX_UNDO_TO_PURGE(这几个状态是InnoDB的事务结束的标志),表示满足一定条件后可以清理这些undolog,并将对应的redolog刷盘。至此这个事务就算是提交完了,注意事务提交需要三次刷盘(写redolog,写binlog,写commit,InnoDB新版本通过组提交进行了优化)。而脏页并不一定随着事务提交而刷盘,需依赖于buffer pool持久化策略。
  5. 对于处于Prepare状态的事务,存储引擎既可以提交,也可以回滚,这取决于目前该事务对应的binlog是否已经写入硬盘。这时就会读取最后一个binlog日志文件,从日志文件中找一下有没有该Prepare事务对应的XID记录,如果有的话,就将该事务提交,否则就回滚好了。

如果没有两阶段提交

redolog未写入,binlog未写入:此时MySQL异常重启无法恢复数据,认为sql就没执行。

redolog写入,binlog未写入:此时MySQL异常重启能根据redolog恢复事务提交时的数据,但binlog没有记录,后续使用binlog恢复临时库会出现数据丟失,导致状态不一致。

binlog写入,redolog未写入:此时MySQL异常重启临时库能根据binlog重放事务提交时的数据,但redolog没有记录,如果主库有一些脏页已经刷盘,本应先回滚再通过binlog重放,但现在无法回滚,会导致状态不一致。

结论

所谓两阶段提交,就是指同时将redolog和binlog都写成功,这样既能保证通过binlog恢复临时库时和主库无差异,又能保证通过redolog恢复主库时和临时库无差异。

© 版权声明
THE END