mysql5.7.41commit流程源码解读

2023-02-15 17:17:04 浏览数 (1)

写在前面

为了使代码好看, 很多时候 函数返回 True 表示失败, False表示成功. 但还是得看个人习惯, 比如我更习惯在条件前面加个not或者!, 虽然会有额外的转换, 但是代码更易读.

可以结合debug信息一起分析(mysqld --debug).

本来准备继续写5.7.38的, 结果打开了8.31, 但是debug的是5.7.41的... 差别不大,不影响 -_-

如何找到commit代码

其实就是搜索commit关键字.

如果你看过mysql代码结构的话, 你会知道sql/sql_parse.cc 就是解析客户端发过来的sql的, 只需要在里面搜索commit就可以...

没看过的话, 可以使用grep搜索, 但是会很多....

代码语言:javascript复制
grep -r 'COMMIT' | awk -F ':' '{print $1}' | sort | uniq | grep -vE 'storage|test|*.h'

也可以从mysql连接,到handler再到command处理... 有空了再说吧...

SQLCOM_COMMIT

看起来很多, 实际上就是:

1.判断空指针之类的

2.获取参数completion_type

3.事务提交(主要看这个)

4.释放锁

5.根据参数completion_type决定要不要开启新事务或者断开客户端连接.

completion_type值: 0(默认):不管 1:事务提交后,开启新事务 2:事务提交后,断开客户端连接.

代码语言:javascript复制
case SQLCOM_COMMIT: {
      assert(thd->lock == nullptr ||
             thd->locked_tables_mode == LTM_LOCK_TABLES);
      bool tx_chain =
          (lex->tx_chain == TVL_YES ||
           (thd->variables.completion_type == 1 && lex->tx_chain != TVL_NO));
      bool tx_release =
          (lex->tx_release == TVL_YES ||
           (thd->variables.completion_type == 2 && lex->tx_release != TVL_NO));
      if (trans_commit(thd)) goto error;
      thd->mdl_context.release_transactional_locks();
      /* Begin transaction with the same isolation level. */
      if (tx_chain) {
        if (trans_begin(thd)) goto error;
      } else {
        /* Reset the isolation level and access mode if no chaining
         * transaction.*/
        trans_reset_one_shot_chistics(thd);
      }
      /* Disconnect the current client connection. */
      if (tx_release) thd->killed = THD::KILL_CONNECTION;
      my_ok(thd);
      break;
    }

trans_commit

sql/transaction.cc

值列出关键信息,去掉断言,dbug之类的

代码语言:javascript复制
trans_check_state(thd)  /*检查状态*/
ha_commit_trans(thd, true, ignore_global_read_lock)  /*主要看这个*/
/*重置事务状态*/
trans_track_end_trx(thd) /*事务结束*/
​

ha_commit_trans

sql/transaction.cc

不考虑slave. 不考虑太多的if情况,不考虑read_only情况,不考虑原子DDL

代码语言:javascript复制
commit_owned_gtids(thd, all);
Transaction_ctx *trn_ctx = thd->get_transaction()
XID_STATE *xid_state = trn_ctx->xid_state(); /*使用xa控制内部两阶段提交*/
MDL_request mdl_request /*获取MDL锁对象*/
thd->mdl_context.acquire_lock(&mdl_request,thd->variables.lock_wait_timeout))/*获取锁失败就rollback*/
release_mdl = true
tc_log->prepare(thd, all) /*tc_log: Transaction Coordinator Log*/
xid_state->set_state(XID_STATE::XA_PREPARED) /*如果上一步不报错的话, 就设置xa状态为XA_PREPARED*/
tc_log->commit(thd, all)
thd->mdl_context.release_lock(mdl_request.ticket) /*释放锁*/
/*剩下的就是重置状态之类的了*/

主要看 tc_log->prepare 和 tc_log->commit

mysqld.cc启动的时候就有tc_log

代码语言:javascript复制
/* sql/mysqld.cc     init_server_components*/
if (total_ha_2pc > 1 || (1 == total_ha_2pc && opt_bin_log)) {
  if (opt_bin_log)
    tc_log = &mysql_bin_log; /* class MYSQL_BIN_LOG : public TC_LOG */
  else
    tc_log = &tc_log_mmap;
}

tc_log->prepare

binlog.cc MYSQL_BIN_LOG::prepare

代码语言:javascript复制
ha_prepare_low(thd, all)
代码语言:javascript复制
ha_prepare_low
    binlog_prepare /*实际上啥也没做*/
    innobase_xa_prepare /*storage/innobase/handler/ha_innodb.cc*/

debug信息

代码语言:javascript复制
T@2: | | | | | | | >ha_prepare_low
T@2: | | | | | | | | >binlog_prepare
T@2: | | | | | | | | <binlog_prepare 1829
T@2: | | | | | | | | >innobase_xa_prepare /*自己加的*/
T@2: | | | | | | | | | >innobase_trx_init
T@2: | | | | | | | | | <innobase_trx_init 2628
T@2: | | | | | | | | <innobase_xa_prepare 17248
T@2: | | | | | | | <ha_prepare_low 2371

tc_log->commit

binlog.cc MYSQL_BIN_LOG::commit

代码语言:javascript复制
ha_commit_low
    binlog_commit
    Ha_trx_info::reset
    innobase_commit
    Ha_trx_info::reset

debug信息

代码语言:javascript复制
T@2: | | | | | | | | >ha_commit_low
T@2: | | | | | | | | | >binlog_commit
T@2: | | | | | | | | | <binlog_commit 2021
T@2: | | | | | | | | | >Ha_trx_info::reset
T@2: | | | | | | | | | <Ha_trx_info::reset 101
T@2: | | | | | | | | | >innobase_commit
T@2: | | | | | | | | | | trans: ending transaction
T@2: | | | | | | | | | | >innobase_trx_init
T@2: | | | | | | | | | | <innobase_trx_init 2628
T@2: | | | | | | | | | | >THD::get_trans_pos
T@2: | | | | | | | | | | | return: file: m3342.000003, pos: 767
T@2: | | | | | | | | | | <THD::get_trans_pos 2863
T@2: | | | | | | | | | <innobase_commit 4515
T@2: | | | | | | | | | >Ha_trx_info::reset
T@2: | | | | | | | | | <Ha_trx_info::reset 101
T@2: | | | | | | | | | >Transaction_ctx::cleanup
T@2: | | | | | | | | | | >Rpl_transaction_ctx::cleanup
T@2: | | | | | | | | | | <Rpl_transaction_ctx::cleanup 47
T@2: | | | | | | | | | | >free_root
T@2: | | | | | | | | | | | enter: root: 0x7f79c4004610  flags: 1
T@2: | | | | | | | | | | <free_root 489
T@2: | | | | | | | | | <Transaction_ctx::cleanup 416
T@2: | | | | | | | | <ha_commit_low 1951

总结

关于redo和binlog先后顺序, 从函数调用来看是先调用的binlog,后redo. 还挺复杂的,以后再细看吧...

0 人点赞