MySQl事务创建,开始以及提交,以及insert的提交

来源:这里教程网 时间:2026-03-01 16:45:12 作者:

创建事务 trx_allocate_for_mysql中调用trx_create 进行创建 方法中分配一个内存对象,然后初始化这个对象的值,可以看到trx->id = ut_dulint_zero; 在创建的时候是设置了0, 创建undo_mutex 事务的提交 trx_commit_for_mysql     trx->op_info = "committing";     trx_start_if_not_started(trx);     mutex_enter(&kernel_mutex);     trx_commit_off_kernel(trx);     mutex_exit(&kernel_mutex); 开始一个事务 trx_start_low     if (rseg_id == ULINT_UNDEFINED) {         // 分配roll segment         rseg_id = trx_assign_rseg();     }     // 事务系统包含一个数组,有对应的roll segment,大小256,一个事务有256个roll segment,rseg_id 是上面分配的, 系统最多也只能有256个回滚段     rseg = trx_sys_get_nth_rseg(trx_sys, rseg_id);     //事务开始时就分配事务ID     trx->id = trx_sys_get_new_trx_id();     /* The initial value for trx->no: ut_dulint_max is used in     read_view_open_now: */     trx->no = ut_dulint_max;     trx->rseg = rseg;     trx->conc_state = TRX_ACTIVE;     trx->start_time = time(NULL);     UT_LIST_ADD_FIRST(trx_list, trx_sys->trx_list, trx);     return(TRUE); } 回滚段的分配trx_assign_rseg 回滚段也是使用列表进行管理,会跳过系统回滚段 事务的提交 trx_commit_off_kernel 针对insert update 的undo 做了区分,在事务提交的时候,会修改undo的状态 事务提交的代码很好的描述了mtr的作用,文档中的描述是确保在文件层面确保事务的完整性。 因为这个事务涉及到了undo文件和binlog 文件 关于update的undo 清理 trx_purge_add_update_undo_to_history 将undo的加入到history list,然后清理undo段 insert的操作涉及到可见性的问题,insert的undo是设置成free状态,undo 是purge,insert的undo并没有放入到history list 中, insert的undo和update的undo为何区分开来处理? create table为什么放到事务中处理? 上面描述的是innodb中的事务,下面看下insert语句执行的过程是怎么提交的,先看下堆栈

trx_prepare_for_mysql(trx_t * trx) (/root/mysql-5.0.15/innobase/trx/trx0trx.c:1917)
innobase_xa_prepare(THD * thd, bool all) (/root/mysql-5.0.15/sql/ha_innodb.cc:7257)
ha_commit_trans(THD * thd, bool all) (/root/mysql-5.0.15/sql/handler.cc:688)
ha_autocommit_or_rollback(THD * thd, int error) (/root/mysql-5.0.15/sql/handler.cc:844)
mysql_insert(THD * thd, TABLE_LIST * table_list, List<Item> & fields, List<List<Item> > & values_list, List<Item> & update_fields, List<Item> & update_values, enum_duplicates duplic, bool ignore) (/root/mysql-5.0.15/sql/sql_insert.cc:603)
mysql_execute_command(THD * thd) (/root/mysql-5.0.15/sql/sql_parse.cc:3239)
mysql_parse(THD * thd, char * inBuf, uint length) (/root/mysql-5.0.15/sql/sql_parse.cc:5536)
dispatch_command(enum_server_command command, THD * thd, char * packet, unsigned int packet_length) (/root/mysql-5.0.15/sql/sql_parse.cc:1697)
do_command(THD * thd) (/root/mysql-5.0.15/sql/sql_parse.cc:1498)
handle_one_connection(void * arg) (/root/mysql-5.0.15/sql/sql_parse.cc:1143)
libpthread.so.0!start_thread (Unknown Source:0)
libc.so.6!clone (Unknown Source:0)

在mysql_insert中先

error=write_record(thd, table ,&info);
然后在执行binlog的写入
if (mysql_bin_log.is_open())
{
if (error <= 0)
thd->clear_error();
Query_log_event qinfo(thd, thd->query, thd->query_length,
transactional_table, FALSE);
if (mysql_bin_log.write(&qinfo) && transactional_table)
error=1;
}
然后对事务表进行提交或回滚
if (transactional_table)
error=ha_autocommit_or_rollback(thd,error);
在提交方法ha_commit_trans中
if (!trans->no_2pc && trans->nht > 1)
{
for (; *ht && !error; ht++)
{
int err;
if ((err= (*(*ht)->prepare)(thd, all))) --调用innobase_xa_prepare进入innodb prepare 
--调用trx_prepare_off_kernel,就是将undo修改下状态标记,根据参数设置控制刷盘, 然后调用binlog_prepare 进入prepare状态这个什么也没做
{
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
error= 1;
}
statistic_increment(thd->status_var.ha_prepare_count,&LOCK_status);
}
DBUG_EXECUTE_IF("crash_commit_after_prepare", abort(););
if (error || (is_real_trans && xid &&
(error= !(cookie= tc_log->log(thd, xid)))))
{
ha_rollback_trans(thd, all);
error= 1;
goto end;
}
DBUG_EXECUTE_IF("crash_commit_after_log", abort(););
}
error=ha_commit_one_phase(thd, all) ? cookie ? 2 : 1 : 0; --进入阶段的提交 
-调用innobase_commit进行InnoDB 的提交- trx_commit_for_mysql- trx_commit_off_kernel 
--进行提交,innodb的提交以及binlog的提交,binlog的提交也就是追加了COMMIT标记,因为前面已经写入文件了
DBUG_EXECUTE_IF("crash_commit_before_unlog", abort(););
if (cookie)
tc_log->unlog(cookie, xid);
DBUG_EXECUTE_IF("crash_commit_after", abort(););
end:
if (is_real_trans)
start_waiting_global_read_lock(thd);
}

insert 写redo log的堆栈,在5.0版本中prepare阶段就写入redo log,flush log file

log_group_write_buf(log_group_t * group, unsigned char * buf, unsigned long len, dulint start_lsn, ulint new_data_offset) (/root/mysql-5.0.15/innobase/log/log0log.c:1263)
log_write_up_to(dulint lsn, ulint wait, unsigned long flush_to_disk) (/root/mysql-5.0.15/innobase/log/log0log.c:1449)
trx_prepare_off_kernel(trx_t * trx) (/root/mysql-5.0.15/innobase/trx/trx0trx.c:1894)
trx_prepare_for_mysql(trx_t * trx) (/root/mysql-5.0.15/innobase/trx/trx0trx.c:1930)
innobase_xa_prepare(THD * thd, bool all) (/root/mysql-5.0.15/sql/ha_innodb.cc:7257)
ha_commit_trans(THD * thd, bool all) (/root/mysql-5.0.15/sql/handler.cc:688)
ha_autocommit_or_rollback(THD * thd, int error) (/root/mysql-5.0.15/sql/handler.cc:844)
mysql_insert(THD * thd, TABLE_LIST * table_list, List<Item> & fields, List<List<Item> > & values_list, List<Item> & update_fields, List<Item> & update_values, enum_duplicates duplic, bool ignore) (/root/mysql-5.0.15/sql/sql_insert.cc:603)
mysql_execute_command(THD * thd) (/root/mysql-5.0.15/sql/sql_parse.cc:3239)
mysql_parse(THD * thd, char * inBuf, uint length) (/root/mysql-5.0.15/sql/sql_parse.cc:5536)
dispatch_command(enum_server_command command, THD * thd, char * packet, unsigned int packet_length) (/root/mysql-5.0.15/sql/sql_parse.cc:1697)
do_command(THD * thd) (/root/mysql-5.0.15/sql/sql_parse.cc:1498)
handle_one_connection(void * arg) (/root/mysql-5.0.15/sql/sql_parse.cc:1143)
libpthread.so.0!start_thread (Unknown Source:0)
libc.so.6!clone (Unknown Source:0)

为什么不在log_group_write_buf这个阶段,写入到从库上去,从库接受,这样就去掉binlog 了。 有兴趣学习源码的加群一起学习啊 QQ:  700072075

相关推荐