• MySQL源码之两阶段提交


    在双1的情况下,两阶段提交的过程

    环境准备:mysql 5.5.18, innodb 1.1 version
    配置:
      sync_binlog=1
      innodb_flush_log_at_trx_commit=1
      autocommit=0

    设置断点:

    sql_parse.cc::dispatch_command --命令跳转入口
    sql_parse.cc::mysql_parse
    sql_parse.cc::mysql_execute_command
    sql_parse.cc::trans_commit_stmt --语句commit入口
    handler.cc::ha_commit_trans --commit入口

    log.cc::binlog_prepare --binlog prepare入口
    Ha_innodb.cc::innobase_xa_prepare --innodb prepare入口

    log.cc::binlog_commit --binlog commit入口
    Ha_innodb.cc::innobase_commit --innodb commit入口


     

    实验步骤

    步骤1:

    1 use test; 
    2 create table xpchild(id int auto_increment primary key, name varchar(100));
    3 insert into xpchild(1,'xpchild');

    步骤1过程:

    1. dispatch_command:
      command = COM_QUERY
      inc_thread_running():增加thread running
      statistic_increment:增加计数
      switch (command):跳转命令
    2. mysql_execute_command:
      解析命令为:SQLCOM_INSERT
    3. mysql_insert:
      跳转到真正的insert
      bool log_on= (thd->variables.option_bits & OPTION_BIN_LOG):判断是否打开了binlog
      open_and_lock_tables(thd, table_list, TRUE, 0):打开table并锁表
    4. trans_commit_stmt:
      语句级别的提交
      if (thd->transaction.stmt.ha_list)
        res= ha_commit_trans(thd, FALSE);
        针对语句所有参与的引擎进行提交, 但这里all的参数是false,说明是语句的提交动作,而非真正的事务commit。
    5. ha_commit_trans:
      这里trans=all ? &thd->transaction.all : &thd->transaction.stmt,说明是stmt的transaction。
      ha_check_and_coalesce_trx_read_only:判断事务是否需要两阶段提交
      for (Ha_trx_info *hi= ha_info; hi; hi= hi->next())
        一共有两个引擎参与:binlog&&innodb


    进入prepare阶段:

    5.1. binlog_prepare:

         直接返回什么也没有做
    5.2. innobase_xa_prepare:
      all参数是false,innodb认为是语句级别的提交,就只做如下的事情:
      row_unlock_table_autoinc_for_mysql(trx);释放语句hold的auto_increment锁
      trx_mark_sql_stat_end(trx);记录本语句的undo信息,以便语句级的回滚


    进入提交阶段:

    commit_one_phase_low(thd, all, trans, is_real_trans);


    5.3. binlog_commit
      只是cache了statement的binlog,没有做flush操作
    5.4. innobase_commit
      row_unlock_table_autoinc_for_mysql(trx);释放了自增锁
      trx_mark_sql_stat_end(trx);记录本语句的undo信息  
      这个地方做的事情和prepare阶段一样,多做虽然没有坏处,但也没有看到有什么意义
      srv_active_wake_master_thread:给主线程发信号唤醒,就结束
    6. close_thread_tables(thd)
      在mysql_execute_command中close table,然后这次命令就结束了。

    步骤2:

      

    commit

    1. 前面的步骤都相似:
      mysql_execute_command函数中跳转到trans_commit进行真正的提交。

    2. ha_commit_trans
      进入commit函数,传入参数all=true,为真正的提交动作。
      检查rw_ha_count= ha_check_and_coalesce_trx_read_only(thd, ha_info, all)
      rw_ha_count=2 代表binlog引擎和innodb引擎
      is_real_trans=true


    进入两阶段提交过程:

    3. binlog_prepare:
      直接return 0;
    4. innobase_xa_prepare:
      trx_prepare_off_kernel:
      mutex_enter(&(rseg->mutex));锁住undo segment的mutex
      trx_undo_set_state_at_prepare;设置这个insert语句所对应的undo的状态从TRX_UNDO_ACTIVE-》TRX_UNDO_PREPARED。
      mutex_exit(&(rseg->mutex));释放mutex
      mtr_commit(&mtr): 对于本次的内存更改,因为非原子操作,所以也对应一个提交动作
      lsn = mtr.end_lsn:或者最后的lsn后,flush的时候要保证大于或者等于lsn。
      if flush_log_at_trx_commit=1
        log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); 参数flush_to_disk=true表示flush log到disk中。

    5. tc_log->log_and_order:这里做了大部分的commit的工作,包括:
      binlog_commit_flush_trx_cache:刷新binlog到disk
        TC_LOG::run_commit_ordered:
          对binlog进行group commit操作

      innodb::innobase_commit_ordered:
      trx_commit_off_kernel:
        标记事务为TRX_COMMITTED_IN_MEMORY,
        如果是insert undo,直接purge掉。
        trx->flush_log_later=true,所以这里并不进行flush,只是记录了commit的lsn,flush的动作放在了提交阶段。
        trx_roll_free_all_savepoints:释放所有的save_point.
        事务的标记最终为: trx->conc_state = TRX_NOT_STARTED

    进入最终提交阶段:
    commit_one_phase_low(thd, all, trans, is_real_trans):
      binlog_commit:这里貌似什么都没有做,直接进入
      if (cache_mngr->trx_cache.empty())
        cache_mngr->reset_cache(&cache_mngr->trx_cache)
        所以binlog commit并不是真正flush log的地方,而是在ha_commit_trans函数中,完成prepare过程后,commit提交前做了:
        cookie= tc_log->log_and_order(thd, xid, all, need_commit_ordered);
        所以,其实binlog已经提交了,只不过位置不在这里,不过,不妨碍一致性,因为都是在一阶段完成后。

    innobase_commit:
      trx_commit_complete_for_mysql:
        log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE);刷新log到disk中,这里的lsn是commit_lsn.

    到这里commit完整结束。

  • 相关阅读:
    nginx超时问题
    linux打包文件,压缩文件
    centos查看文件夹大小
    Nginx反向代理和负载均衡
    nginx的location配置详解
    nginx错误Upstream timed out
    mysql处理函数
    SQL左右连接中的on and和on where的区别
    html select change事件触发
    get,post区别
  • 原文地址:https://www.cnblogs.com/xpchild/p/3694839.html
Copyright © 2020-2023  润新知