mysql事務提交過程

MySQL作爲一種關係型數據庫,已被廣泛應用到互聯網中的諸多項目中。今天我們來討論下事務的提交過程。

                                                       MySQL體系結構

 

由於mysql插件式存儲架構,導致開啓binlog後,事務提交實質是二階段提交,通過兩階段提交,來保證存儲引擎和二進制日誌的一致。

本文僅討論binlog未打卡狀態下的提交流程,後續會討論打開binlog選項後的提交邏輯。

   

測試環境

OS:WIN7

ENGINE:

bin-log:off

DB:

   

測試條件

set autocommit=0;

複製代碼

-- ----------------------------

-- Table structure for `user`

-- ----------------------------

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (

`id` int(20) NOT NULL,

`account` varchar(20) NOT NULL,

`name` varchar(20) NOT NULL,

PRIMARY KEY (`id`),

KEY `id` (`id`) USING BTREE,

KEY `name` (`name`) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

複製代碼

   

測試語句

insert into user values(1, 'sanzhang', '張三');
commit;

   

一般常用的DML:Data Manipulation Language 數據操縱語言,對錶的數據進行操作,(insert、update、delete )語句 和 DCL:Data Control Language 數據庫控制語言

(創建用戶、刪除用戶、授權、取消授權)語句 和 DDL:Data Definition Language 數據庫定義語言,對數據庫內部的對象進行創建、刪除、修改的操語句

,均是使用MySQL提供的公共接口mysql_execute_command,來執行相應的SQL語句。我們來分析下mysql_execute_command接口執行的流程:

複製代碼

mysql_execute_command

{

   switch (command)

   {

       case SQLCOM_INSERT:

                mysql_insert();

                break;

       case SQLCOM_UPDATE:

                mysql_update();

                break;

       case SQLCOM_DELETE:

                mysql_delete();

                break;

       ......

   }

   if thd->is_error()  //語句執行錯誤

     trans_rollback_stmt(thd);

  else

    trans_commit_stmt(thd);

}

複製代碼

   

從上述流程中,可以看到執行任何語句,最後都會執行trans_rollback_stmt或者trans_commit_stmt,這兩個分別是語句回滾和語句提交。

語句提交,對於非自動模式下,主要有兩個作用:

1、釋放autoinc鎖,這個鎖主要用來處理多個事務互斥的獲取自增序列。因此,無論最後執行的是語句提交還是語句回滾,該資源都是需要立馬釋放掉的。

2、標識語句在事務中的位置,方便語句級回滾。執行commit後,可以進入commit流程。

現在看下具體的事務提交流程:

複製代碼

mysql_execute_command

trans_commit_stmt

ha_commit_trans(thd, FALSE);

{

    TC_LOG_DUMMY:ha_commit_low

        ha_commit_low()   

            innobase_commit

            {

                //獲取innodb層對應的事務結構

                trx = check_trx_exists(thd);

                if(單個語句,且非自動提交)

                {

                     //釋放自增列佔用的autoinc鎖資源

                     lock_unlock_table_autoinc(trx);

                     //標識sql語句在事務中的位置,方便語句級回滾

                     trx_mark_sql_stat_end(trx);

                }

                else 事務提交

                {

                     innobase_commit_low()

                     {  

                        trx_commit_for_mysql();

                            <span style="color: #ff0000;">trx_commit</span>(trx); 

                     }

//確定事務對應的redo日誌是否落盤【根據flush_log_at_trx_commit參數,確定redo日誌落盤方式】

                    trx_commit_complete_for_mysql(trx);

trx_flush_log_if_needed_low(trx->commit_lsn);

log_write_up_to(lsn);

                }

            }

}

複製代碼

複製代碼

trx_commit

    trx_commit_low

        {

            trx_write_serialisation_history

            {

                trx_undo_update_cleanup //供purge線程處理,清理回滾頁

            }

            trx_commit_in_memory

            {

                lock_trx_release_locks //釋放鎖資源

                trx_flush_log_if_needed(lsn) //刷日誌

                trx_roll_savepoints_free //釋放savepoints

            }

        }

複製代碼

 

MySQL是通過WAL方式,來保證數據庫事務的一致性和持久性,即ACID特性中的C(consistent)和D(durability)。

WAL(Write-Ahead Logging)是一種實現事務日誌的標準方法,具體而言就是:

1、修改記錄前,一定要先寫日誌;

2、事務提交過程中,一定要保證日誌先落盤,才能算事務提交完成。

通過WAL方式,在保證事務特性的情況下,可以提高數據庫的性能。

   

從上述流程可以看出,提交過程中,主要做了4件事情,

1、清理undo段信息,對於innodb存儲引擎的更新操作來說,undo段需要purge,這裏的purge主要職能是,真正刪除物理記錄。在執行delete或update操作時,實際舊記錄沒有真正刪除,只是在記錄上打了一個標記,而是在事務提交後,purge線程真正刪除,釋放物理頁空間。因此,提交過程中會將undo信息加入purge列表,供purge線程處理。

2、釋放鎖資源,mysql通過鎖互斥機制保證不同事務不同時操作一條記錄,事務執行後纔會真正釋放所有鎖資源,並喚醒等待其鎖資源的其他事務;

3、刷redo日誌,前面我們說到,mysql實現事務一致性和持久性的機制。通過redo日誌落盤操作,保證了即使修改的數據頁沒有即使更新到磁盤,只要日誌是完成了,就能保證數據庫的完整性和一致性;

4、清理保存點列表,每個語句實際都會有一個savepoint(保存點),保存點作用是爲了可以回滾到事務的任何一個語句執行前的狀態,由於事務都已經提交了,所以保存點列表可以被清理了。

   

關於mysql的鎖機制,purge原理,redo日誌,undo段等內容,其實都是數據庫的核心內容。

MySQL 本身不提供事務支持,而是開放了存儲引擎接口,由具體的存儲引擎來實現,具體來說支持 MySQL 事務的存儲引擎就是 InnoDB。

存儲引擎實現事務的通用方式是基於 redo log 和 undo log。

簡單來說,redo log 記錄事務修改後的數據, undo log 記錄事務前的原始數據。

所以當一個事務執行時實際發生過程簡化描述如下:

  1. 先記錄 undo/redo log,確保日誌刷到磁盤上持久存儲。
  2. 更新數據記錄,緩存操作並異步刷盤。
  3. 提交事務,在 redo log 中寫入 commit 記錄。

在 MySQL 執行事務過程中如果因故障中斷,可以通過 redo log 來重做事務或通過 undo log 來回滾,確保了數據的一致性。

這些都是由事務性存儲引擎來完成的,但 binlog 不在事務存儲引擎範圍內,而是由 MySQL Server 來記錄的。

那麼就必須保證 binlog 數據和 redo log 之間的一致性,所以開啓了 binlog 後實際的事務執行就多了一步,如下:

  1. 先記錄 undo/redo log,確保日誌刷到磁盤上持久存儲。
  2. 更新數據記錄,緩存操作並異步刷盤。
  3. 將事務日誌持久化到 binlog。
  4. 提交事務,在 redo log 中寫入commit記錄。

這樣的話,只要 binlog 沒寫成功,整個事務是需要回滾的,而 binlog 寫成功後即使 MySQL Crash 了都可以恢復事務並完成提交。

要做到這點,就需要把 binlog 和事務關聯起來,而只有保證了 binlog 和事務數據的一致性,才能保證主從數據的一致性。

所以 binlog 的寫入過程不得不嵌入到純粹的事務存儲引擎執行過程中,並以內部分佈式事務(xa 事務)的方式完成兩階段提交。

   

參考

    1、《高性能MySQL》   

出處:http://www.cnblogs.com/exceptioneye

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章