zoukankan      html  css  js  c++  java
  • mysql之 事务prepare 与 commit 阶段分析

    打开binlog选项后,执行事务提交命令时,就会进入两阶段提交模式。两阶段提交分为prepare阶段和commit两个阶段。流程如下 :这里面涉及到两个重要的参数:innodb_flush_log_at_trx_commit和sync_binlog,参数可以设置不同的值,具体可以查看mysql的帮助手册。我这里设置的是双一模式(innodb_flush_log_at_trx_commit=1,sync_binlog=1),不同的模式区别在于,写文件调用write和落盘fsync调用的频率不同,所导致的后果是mysqld 或 os crash后,不严格的设置可能会丢失事务的更新。双一模式是最严格的模式,这种设置情况下,单机在任何情况下不会丢失事务更新。
     
    prepare阶段:
        1.设置undo state=TRX_UNDO_PREPARED; //trx_undo_set_state_at_prepare调用
        2.刷事务更新产生的redo日志;【步骤1产生的redo日志也会刷入】
        
    commit阶段:
       1.将事务产生的binlog写入文件,刷入磁盘;
       2.设置undo页的状态,置为TRX_UNDO_TO_FREE或TRX_UNDO_TO_PURGE;  // trx_undo_set_state_at_finish调用
       3.记录事务对应的binlog偏移,写入系统表空间; //trx_sys_update_mysql_binlog_offset调用
        
        下面这部分是我抽象出来的源码调用部分,大家可以通过单步调试方式,在关键函数中设置断点,来详细了解这个过程。
    ===========
     prepare阶段
    ===========
    MYSQL_BIN_LOG::prepare
        ha_prepare_low
        {
    engine:
    binlog_prepare
    innobase_xa_prepare
    mysql:
    trx_prepare_for_mysql
    {

                    1.trx_undo_set_state_at_prepare    //设置undo段的标记为TRX_UNDO_PREPARED
                    2.设置事务状态为TRX_STATE_PREPARED
                    3.trx_flush_log_if_needed  //将产生的redolog刷入磁盘

                }
         }
         
    ============
    commit阶段
    ============
    MYSQL_BIN_LOG::commit
        ordered_commit
       {
    1.FLUSH_STAGE
            flush_cache_to_file  //  刷binlog
     
    2.SYNC_STAGE
            sync_binlog_file    //Call fsync() to sync the file to disk.
     
    3.COMMIT_STAGE
            ha_commit_low
            {
                binlog_commit
                innobase_commit   
                    trx_commit(trx) 
                    {
                        trx_write_serialisation_history(trx, mtr);  //更新binlog位点,设置undo状态
                        trx_commit_in_memory(trx, lsn); //释放锁资源,清理保存点列表,清理回滚段
                    }        
            } 
        }
     
          mysqld可能在任何情况下crash,os也有可能出现问题,另外若机器掉电,mysqld也会同样挂掉。但是即使这样,mysql仍然能保证数据库的一致性。接下来,我会结合上述流程,分析二阶段提交如何保证这点的。下面给出几种常见的场景,
    1.prepare阶段,redo log落盘前,mysqld crash
    2.prepare阶段,redo log落盘后,binlog落盘前,mysqld crash
    3.commit阶段,binlog落盘后,mysqld crash
          对于第一种情况,由于redo没有落盘,毫无疑问,事务的更新肯定没有写入磁盘,数据库的一致性受影响;对于第二种情况,这时候redo log写入完成,但binlog还未写入,事务处于TRX_STATE_PREPARED状态,这是提交还是回滚呢?对于第三种情况,此时,redo log和binlog都已经落盘,只是undo状态没有更新,这种情况也应该提交,因为redo log和binlog已经一致了,当然这只是我的假设,需要通过源码逻辑来验证。
         下面给出了mysqld异常重启后的执行逻辑以及关键的源代码。对于第三种情况,我们可以搜集到未提交事务的binlog event,所以需要提交,与我们假设相符;而对于第二种情况,由于binlog未写入,需要通过执行回滚操作来保证数据库的一致性。
     
    异常重启后,如何判断事务该提交还是回滚
    1.读binlog日志,获取崩溃时没有提交的event;  //info->commit_list中含有该元素
    2.若存在,则对应的事务要提交;否则需要回滚。
     
    判断事务提交或回滚源码如下:
     
     
         上面讨论了两阶段提交的基本流程,以及服务器异常crash后,mysql如何重启恢复保证binlog和数据的一致性。简而言之,对于异常的xa事务,若binlog已落盘,则事务应该提交;binlog未落盘,则事务就应该回滚。由于这块涉及到的源代码较多,我也没有看完所有源代码,如有不正确的地方,欢迎指正。
     
    //异常重启后,回滚流程
    innobase_rollback_by_xid
        rollback_by_xid
    trx_rollback_resurrected
        trx_rollback_active
            row_undo
            {
                //从回滚页获取undo记录
                //分析undo记录类型
                if (insert)
                    row_undo_ins
                else
                    row_undo_mod
            }
     
    //异常重启后,提交流程
    commit_by_xid
        trx_commit_for_mysql
     
    //写binlog接口
    handler.cc:binlog_log_row
    sql/binlog.cc:commit
    mysys/my_sync:my_sync
    sql/binlog.cc:sync_binlog_file
    handler/ha_innodb.cc:innobase_xa_prepare
     
    转:https://www.cnblogs.com/yuyue2014/p/4738007.html
  • 相关阅读:
    我爱java系列之---【微服务间的认证—Feign拦截器】
    我爱java系列之---【设置权限的三种解决方案】
    581. Shortest Unsorted Continuous Subarray
    129. Sum Root to Leaf Numbers
    513. Find Bottom Left Tree Value
    515. Find Largest Value in Each Tree Row
    155. Min Stack max stack Maxpop O(1) 操作
    painting house
    Minimum Adjustment Cost
    k Sum
  • 原文地址:https://www.cnblogs.com/andy6/p/9850855.html
Copyright © 2011-2022 走看看