標籤:
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(insert、update、delete)語句和DCL(commit、rollback)語句,均是使用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_commandtrans_commit_stmtha_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 記錄事務前的未經處理資料。
所以當一個事務執行時實際發生過程簡化描述如下:
- 先記錄 undo/redo log,確保日誌刷到磁碟上持久儲存。
- 更新資料記錄,快取作業並非同步刷盤。
- 提交事務,在 redo log 中寫入 commit 記錄。
在 MySQL 執行事務過程中如果因故障中斷,可以通過 redo log 來重做事務或通過 undo log 來復原,確保了資料的一致性。
這些都是由事務性儲存引擎來完成的,但 binlog 不在事務儲存引擎範圍內,而是由 MySQL Server 來記錄的。
那麼就必須保證 binlog 資料和 redo log 之間的一致性,所以開啟了 binlog 後實際的事務執行就多了一步,如下:
- 先記錄 undo/redo log,確保日誌刷到磁碟上持久儲存。
- 更新資料記錄,快取作業並非同步刷盤。
- 將交易記錄持久化到 binlog。
- 提交事務,在 redo log 中寫入提交記錄。
這樣的話,只要 binlog 沒寫成功,整個事務是需要復原的,而 binlog 寫成功後即使 MySQL Crash 了都可以恢複事務並完成提交。
要做到這點,就需要把 binlog 和事務關聯起來,而只有保證了 binlog 和交易資料的一致性,才能保證主從資料的一致性。
所以 binlog 的寫入過程不得不嵌入到純粹的事務儲存引擎執行過程中,並以內部分散式交易(xa 事務)的方式完成兩階段交易認可。
參考
1、《高效能MySQL》
MySQL事務提交過程(一)