mysql 提交事务_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)
{caseSQLCOM_INSERT:
mysql_insert();break;caseSQLCOM_UPDATE:
mysql_update();break;caseSQLCOM_DELETE:
mysql_delete();break;
......
}if thd->is_error() //語句執行錯誤
trans_rollback_stmt(thd);elsetrans_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();trx_commit(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?中寫入commit記錄。
這樣的話,只要 binlog 沒寫成功,整個事務是需要回滾的,而 binlog 寫成功后即使 MySQL Crash 了都可以恢復事務并完成提交。
要做到這點,就需要把 binlog 和事務關聯起來,而只有保證了 binlog 和事務數據的一致性,才能保證主從數據的一致性。
所以 binlog 的寫入過程不得不嵌入到純粹的事務存儲引擎執行過程中,并以內部分布式事務(xa 事務)的方式完成兩階段提交。
二、MySQL事務提交過程(二)
前一章節我們介紹了在關閉binlog的情況下,事務提交的大概流程。之所以關閉binlog,是因為開啟binlog后事務提交流程會變成兩階段提交,這里的兩階段提交并不涉及分布式事務,當然mysql把它稱之為內部xa事務(Distributed Transactions),與之對應的還有一個外部xa事務。
這里所謂的兩階段提交分別是prepare階段和commit階段。
內部xa事務主要是mysql內部為了保證binlog與redo log之間數據的一致性而存在的,這也是由其架構決定的(binlog在mysql層,而redo log 在存儲引擎層);
外部xa事務則是指支持多實例分布式事務,這個才算是真正的分布式事務。
既然是xa事務,必然涉及到兩階段提交,對于內部xa而言,同樣存在著提交的兩個階段。
下文會結合源碼詳細解讀內部xa的兩階段提交過程,以及各種情況下,mysqld crash后,mysql如何恢復來保證事務的一致性。
測試環境在前章節的基礎上加了:
配置文件參數:
log-bin=D:\mysql\log\5-6-21\mysql-bin
binlog_format=ROWset autocommit=0;
innodb_support_xa=1sync_binlog=1;
innodb_flush_log_at_trx_commit=1;
【innodb_flush_log_at_trx_commit=1,sync_binlog=1
不同的模式區別在于,寫文件調用write和落盤fsync調用的頻率不同,所導致的后果是mysqld 或 os crash后,不嚴格的設置可能會丟失事務的更新。
雙一模式是最嚴格的模式,這種設置情況下,單機在任何情況下不會丟失事務更新。】
prepare階段:
1.設置undo state=TRX_UNDO_PREPARED;?//trx_undo_set_state_at_prepare調用
2.刷事務更新產生的redo日志;【步驟1產生的redo日志也會刷入】
MYSQL_BIN_LOG::prepareha_prepare_low
{
engine:
binlog_prepare
innobase_xa_prepare
mysql:
trx_prepare_for_mysql
{1.trx_undo_set_state_at_prepare //設置undo段的標記為TRX_UNDO_PREPARED2.設置事務狀態為TRX_STATE_PREPARED3.trx_flush_log_if_needed //將產生的redolog刷入磁盤
}
}
commit階段:
1.將事務產生的binlog寫入文件,刷入磁盤;
2.設置undo頁的狀態,置為TRX_UNDO_TO_FREE或TRX_UNDO_TO_PURGE;??//?trx_undo_set_state_at_finish調用
3.記錄事務對應的binlog偏移,寫入系統表空間;?//trx_sys_update_mysql_binlog_offset調用
MYSQL_BIN_LOG::commitordered_commit
{1.FLUSH_STAGE
flush_cache_to_file//刷binlog2.SYNC_STAGE
sync_binlog_file//Call fsync() to sync the file to disk.3.COMMIT_STAGE
ha_commit_low
{
binlog_commit
innobase_commit
trx_commit(trx)
{
trx_write_serialisation_history(trx, mtr);//更新binlog位點,設置undo狀態
trx_commit_in_memory(trx, lsn);//釋放鎖資源,清理保存點列表,清理回滾段
}
}
}
在任何情況下(機器掉電)mysqld crash或者os crash,MySQL仍然能保證數據庫的一致性。數據的一致性是如何做到的哪?正是二階段提交。
我們結合幾種場景來分析下二階段提交是如何做到的:
1.prepare階段,redo log落盤前,mysqld crash
2.prepare階段,redo log落盤后,binlog落盤前,mysqld crash
3.commit階段,binlog落盤后,mysqld crash
對于第一種情況,由于redo沒有落盤,毫無疑問,事務的更新肯定沒有寫入磁盤,數據庫的一致性受影響;
對于第二種情況,這時候redo log寫入完成,但binlog還未寫入,事務處于TRX_STATE_PREPARED狀態,這是提交還是回滾呢?
對于第三種情況,此時,redo log和binlog都已經落盤,只是undo狀態沒有更新,雖然redo log和binlog已經一致了,事務是否應該提交?
我們結合mysqld異常重啟后的執行邏輯以及關鍵的源代碼。
對于第三種情況,我們可以搜集到未提交事務的binlog event,所以需要提交;
對于第二種情況,由于binlog未寫入,需要通過執行回滾操作來保證數據庫的一致性。
異常重啟后,如何判斷事務該提交還是回滾
1.讀binlog日志,獲取崩潰時沒有提交的event; ?//info->commit_list中含有該元素
2.若存在,則對應的事務要提交;否則需要回滾。
判斷事務提交或回滾源碼如下:
上面討論了兩階段提交的基本流程,以及服務器異常crash后,mysql如何重啟恢復保證binlog和數據的一致性。
簡而言之,對于異常的xa事務,若binlog已落盤,則事務應該提交;binlog未落盤,則事務就應該回滾。
//異常重啟后,回滾流程
innobase_rollback_by_xid
rollback_by_xid
trx_rollback_resurrected
trx_rollback_active
row_undo
{//從回滾頁獲取undo記錄//分析undo記錄類型if (insert)
row_undo_inselserow_undo_mod
}
//異常重啟后,提交流程
commit_by_xid
trx_commit_for_mysql
//寫binlog接口
handler.cc:binlog_log_row
sql/binlog.cc:commitmysys/my_sync:my_sync
sql/binlog.cc:sync_binlog_file
handler/ha_innodb.cc:innobase_xa_prepare
binlog日志文件是為了解決MySQL主從復制功能而引入的一份新日志文件,它包含了引發數據變更的事件日志集合。
從庫請求主庫發送 binlog 并通過日志事件還原數據寫入從庫,所以從庫的數據來源為 binlog。
這樣 MySQL 主庫只需做到 binlog 與本地數據一致就可以保證主從庫數據一致(暫且忽略網絡傳輸引發的主從不一致)。
本文整理自:
https://www.cnblogs.com/exceptioneye/p/5451960.html
https://www.cnblogs.com/exceptioneye/p/5451976.html
總結
以上是生活随笔為你收集整理的mysql 提交事务_MySQL事务提交过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: avformat_open_input返
- 下一篇: linux使用mysql命令行工具_我使