创建事务
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
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/25719946/viewspace-2898623/ ,如需转载,请注明出处,否则将追究法律责任。