MySQL的事务管理机制
这里mysql 事务管理机制,只讲述在mysql 是如何管理事务,不包括存储引擎的实现。当前mysql的存储引擎中只有innodb实现了事务的ACID,并且实现机制和oracle是一致的,主要使用了mvcc的实现理论。
本文主要讲述在存储引擎上,mysql公共层的事务管理机制,mysql的事务分两种,一种是标准的事务,也叫normal transaction ,还有一个叫statement transaction。其中normal transaction是标准的实现ACID的事务,而statement transaction是就是一个语句是一个事务,就是在mysql发展中一开始是支持nested transaction,后来觉得在normal transaction中假如有nested transaction,假如normal transaction rollback或者commit,其实nested transaction都是需要rollback后者commit。而statement transaction不是一种标准事务,它来自于BerkeleyDB,就是把每个statement都叫做statement transaction,其实是个nested transaction,而且statement transaction是符合”all or nothing”标准,就是说它提交了,是可见的, visible, but not durable,可见也是在parent transaction和其他的同一个parent transaction的nested transaction。
平时我们可以设置mysql autocommit为true,其实这里是只有statement transaction,没有normal transaction,就是把每个statement transaction当作一个normal transaction。
在提交事务,需要判断是statement transaction还是normal transaction,假如是normal transaction,就需要整个提交,假如是statement transaction是不提交,只是表示前面的更新当前normal transaction是可以见的,假如是跨存储引擎的事务怎么办?其实不管statement transaction和normal transaction,是通一个数据结构实现的,struct THD_TRANS
{
/ true is not all entries in the ht[] support 2pc /
bool no_2pc;
/ storage engines that registered in this transaction /
Ha_trx_info ha_list;
/
The purpose of this flag is to keep track of non-transactional
void reset()
}
里面的Ha_trx_info是个链表,就是会把当前事务涉及到存储引擎注册在里面,一个存储引擎只能注册一个,而且在提交前,还需要判断这个链表是不是真有更新。bool no_2pc就是判断是否需要两阶段提交,两阶段主要针对多个实体的事务,在mysql中事务会使用2pc,主要根据:1、就是参与事务存储引擎都支持两阶段提交,2,至少2个存储引擎都做了数据的修改。从这个条件看,一般分布式事务会使用两阶段事务,在一个节点同一个存储引擎的事务都是一阶段提交的,而且假如使用两阶段,也必要,因为同一个存储引擎的事务有日志可以保证了,使用两阶段还会造成一定的性能损失。
mysql在提交normal transaction,会判断statement transaction都提交了,假如没有提交就会出错,就是判断Ha_trx_info ha_list == null,因为statement transaction都是执行完,自动提交,normal transaction则需要外部提供commit操作,当然其他一些DDL操作也会触发提交normal transaction。
int ha_commit_trans(THD thd, bool all)
{
int error= 0, cookie= 0;
/
‘all’ means that this is either an explicit commit issued by
user, or an implicit commit issued by a DDL.
/
THD_TRANS trans= all ? &thd->transaction.all : &thd->transaction.stmt;
/
“real” is a nick name for a transaction for which a commit will
make persistent changes. E.g. a ‘stmt’ transaction inside a ‘all’
transation is not ‘real’: even though it’s possible to commit it,
the changes are not durable as they might be rolled back if the
enclosing ‘all’ transaction is rolled back.
/
bool is_real_trans= all || thd->transaction.all.ha_list == 0;
Ha_trx_info ha_info= trans->ha_list;
my_xid xid= thd->transaction.xid_state.xid.get_my_xid();
DBUG_ENTER(“ha_commit_trans”);
/
We must not commit the normal transaction if a statement
transaction is pending. Otherwise statement transaction
flags will not get propagated to its normal transaction’s
counterpart.
/
DBUG_ASSERT(thd->transaction.stmt.ha_list == NULL ||
trans == &thd->transaction.stmt);
if (thd->in_sub_stmt)
{
/
Since we don’t support nested statement transactions in 5.0,
we can’t commit or rollback stmt transactions while we are inside
stored functions or triggers. So we simply do nothing now.
TODO: This should be fixed in later ( >= 5.1) releases.
/
if (!all)
DBUG_RETURN(0);
/
We assume that all statements which commit or rollback main transaction
are prohibited inside of stored functions or triggers. So they should
bail out with error even before ha_commit_trans() call. To be 100% safe
let us throw error in non-debug builds.
/
DBUG_ASSERT(0);
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
DBUG_RETURN(2);
}
大家看看这个在mysql 中hander.cc的函数,就是提交事务的,就会通过DBUG_ASSERT(thd->transaction.stmt.ha_list == NULL ||
trans == &thd->transaction.stmt);判断假如是normal transaction必须transaction.stmt.ha_list 是提交了,就是== null,要么我现在提交的是thd->transaction.stmt(statement transaction)
后面该函数还需要判断是否有存储引擎修改了数据,还的判断是否需要两阶段提交,等等,假如需要两阶段就分别使用prepare和ha_commit_one_phase来提交事务.