InnoDB---深入理解事务提交--01

当事务正常执行结束时,事务的提交操作,是实现事务原子性的主要操作之一,这个过程很重要。如下我们重点分析事务提交过程中一些重要环节。

一事务提交的整体过程

    InnoDB MySQL Server 联合实现了事务的提交全程,相关过程参见表 10-2 ,在这个表里面,从上到下,是一个事务提交过程的栈,上面是栈顶下面是栈底,底层的函数调用了上层的函数。

10-2 事务提交的相关函数栈表

函数名称与作用

函数过程

在内存中标识事务完成,然后释放事务锁

1.    调用 lock_trx_release_locks() 释放锁

2.    断言事务的状态是在内存中已经完成(标志是上一步完成设置的)

3.    调用 trx_undo_insert_cleanup() 释放插入的 UNDO 日志

4.    生成新的 LSN

5.    根据 innodb_flush_log_at_trx_commit 参数值的 情况确定是否调用 trx_flush_log_if_needed() 刷出日志到物理存储(即是否实现预写日志机制)

6.    如果是回滚操作,则为回滚操作设置一些值,如设置事务的状态为宏 TRX_STATE_FORCED_ROLLBACK

7.    重新初始化事务对象的结构体值以备再用

trx_commit_low

1.    Mini-Transaction 事务提交

2.    调用 trx_commit_in_memory() 完成事务在内存中的提交等操作

trx_commit

1.    调用 trx_commit_low() 完成事务提交

2.    也会被 trx_rollback_finish() 调用,用于回滚操作

trx_commit_for_mysql

1.    根据事务的状态,执行不同的操作。调用 trx_commit 是因为事务的状体需要从 TRX_STATE_ACTIVETRX_STATE_PREPARED 转变为 TRX_STATE_COMMITTED_IN_MEMORY

innobase_commit_low

1.    调用 trx_commit_for_mysql() 完成事务提交

innobase_commit

1.    调用 innobase_commit_low() 完成事务提交(设置事务提交标志并释放事务相关的锁)

2.    调用 trx_commit_complete_for_mysql() 刷出日志

以上是 InnoDB 层的事务提交相关代码,从上到下是从栈顶到栈底的主要函数

ha_commit_low

MySQL Server 层通过 handle 接口对底层的存储进行事务管理的操作,通过函数指针 ht->commit() 调用了 InnoDB innobase_commit() 函数

TC_LOG_DUMMY::commit

MySQL 层不提供 REDO 日志服务,但提供了一个伪接口来模拟逻辑上的日志提交操作

ha_commit_trans

MySQL 层进行的事务提交

trans_commit_stmt

trans_commit

MySQL 层进行的事务提交,前者是对单语句事务进行提交,后者是对多语句事务进行提交

二事务提交和日志刷出之间的关系

    通常情况下,数据库引擎通过严格两阶段锁( SS2PL ,参见第二章)来实现并发控制技术,但为了满足数据的一致性要求,事务的 ACID 特性中的一致性 C 要求事务在提交前,要先刷出日志即符合 WAL 预先日志机制。但是, InnoDB 支持事务的提交但却没有遵循 WAL 预先日志机制,这就可能带来数据不一致的问题。

    如下是对 innobase_commit() 函数的主要流程分析,从这个分析可以看出,事务先被设置的提交标识,然后锁被释放。之后再执行 trx_commit_complete_for_mysql() 函数完成日志的刷出操作。所以我们说 InnoDB 不符合 WAL 预先日志机制。

/*****************************************************************//**

Commits a transaction in an InnoDB database or marks an SQL statement ended.

@return 0 or deadlock error if the transaction was aborted by another higher priority transaction. */

static

int

   // 提交一个事务,完成事务提交的标识,然后刷出日志(如此的方式, 违反了 WAL 预写日志的机制

    handlerton*     hton,            /*!< in: InnoDB handlerton */ //InnoDB 引擎的句柄

    THD*            thd,              /*!< in: MySQL thread handle of the user for whom the transaction should be committed */ // 用户会话

    bool            commit_trx)     /*!< in: true - commit transaction false - the current SQL statement ended */   // 是否提交

{...

    if (commit_trx   // 值为 TRUE 表示要提交事务

|| (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) {

...

         innobase_commit_low (trx); // 调用栈: -> trx_commit_for_mysql() -> trx_commit(trx) -> trx_commit_low() ->   trx_commit_in_memory() -> lock_trx_release_locks() lock_trx_release_locks() 这个函数中执行如下重要代码

                //...   省略一些代码

                //lock_release(trx);   在设置事务提交已经完成的标志后才释放锁。 锁在设置提交标志后才释放,符合 SS2PL 协议

...

/* Now do a write + flush of logs. */

        if (!read_only) {

             trx_commit_complete_for_mysql (trx);   // 重要的步骤:刷出日志(刷出日志的过程可参见下一节“日志落盘”中的标题二)

}

    } else {     // 不提交事务

...

}

...

}

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章