共計(jì) 3420 個(gè)字符,預(yù)計(jì)需要花費(fèi) 9 分鐘才能閱讀完成。
本篇內(nèi)容介紹了“MySQL 事務(wù)的提交過(guò)程”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓丸趣 TV 小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
MySQL 事務(wù)提交過(guò)程
開(kāi)啟 binlog 后事務(wù)提交流程會(huì)變成兩階段提交,這里的兩階段提交并不涉及分布式事務(wù),當(dāng)然 mysql 把它稱之為內(nèi)部 xa 事務(wù)(Distributed Transactions),與之對(duì)應(yīng)的還有一個(gè)外部 xa 事務(wù)。
這里所謂的兩階段提交分別是 prepare 階段和 commit 階段。
內(nèi)部 xa 事務(wù)主要是 mysql 內(nèi)部為了保證 binlog 與 redo log 之間數(shù)據(jù)的一致性而存在的,這也是由其架構(gòu)決定的(binlog 在 mysql 層,而 redo log 在存儲(chǔ)引擎層);
外部 xa 事務(wù)則是指支持多實(shí)例分布式事務(wù),這個(gè)才算是真正的分布式事務(wù)。
既然是 xa 事務(wù),必然涉及到兩階段提交,對(duì)于內(nèi)部 xa 而言,同樣存在著提交的兩個(gè)階段。
下文會(huì)結(jié)合源碼詳細(xì)解讀內(nèi)部 xa 的兩階段提交過(guò)程,以及各種情況下,mysqld crash 后,mysql 如何恢復(fù)來(lái)保證事務(wù)的一致性。
數(shù)據(jù)庫(kù)版本:5.6.16
操作系統(tǒng)版本:CentOS 6.5
配置文件參數(shù):
log-bin=/my/log/mysql-bin
binlog_format=ROW
set autocommit=0
innodb_support_xa=1
sync_binlog=1
innodb_flush_log_at_trx_commit=1
【innodb_flush_log_at_trx_commit=1,sync_binlog=1
不同的模式區(qū)別在于,寫文件調(diào)用 write 和落盤 fsync 調(diào)用的頻率不同,所導(dǎo)致的后果是 mysqld 或 os crash 后,不嚴(yán)格的設(shè)置可能會(huì)丟失事務(wù)的更新。
雙一模式是最嚴(yán)格的模式,這種設(shè)置情況下,單機(jī)在任何情況下不會(huì)丟失事務(wù)更新。】
測(cè)試條件:
set autocommit=0;
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;
測(cè)試語(yǔ)句:
insert into user values(1, sanzhang , 張三
commit;
prepare 階段:
1. 設(shè)置 undo state=TRX_UNDO_PREPARED;//trx_undo_set_state_at_prepare 調(diào)用
2. 刷事務(wù)更新產(chǎn)生的 redo 日志;【步驟 1 產(chǎn)生的 redo 日志也會(huì)刷入】
MYSQL_BIN_LOG::prepare
ha_prepare_low
{
engine:
binlog_prepare
innobase_xa_prepare
mysql:
trx_prepare_for_mysql
{
1.trx_undo_set_state_at_prepare // 設(shè)置 undo 段的標(biāo)記為 TRX_UNDO_PREPARED
2. 設(shè)置事務(wù)狀態(tài)為 TRX_STATE_PREPARED
3.trx_flush_log_if_needed // 將產(chǎn)生的 redolog 刷入磁盤
}
}
commit 階段:
1. 將事務(wù)產(chǎn)生的 binlog 寫入文件,刷入磁盤;
2. 設(shè)置 undo 頁(yè)的狀態(tài), 置為 TRX_UNDO_TO_FREE 或 TRX_UNDO_TO_PURGE; // trx_undo_set_state_at_finish 調(diào)用
3. 記錄事務(wù)對(duì)應(yīng)的 binlog 偏移,寫入系統(tǒng)表空間; //trx_sys_update_mysql_binlog_offset 調(diào)用
MYSQL_BIN_LOG::commit
ordered_commit
{
1.FLUSH_STAGE
flush_cache_to_file // 刷 binlog
2.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 位點(diǎn),設(shè)置 undo 狀態(tài)
trx_commit_in_memory(trx, lsn); // 釋放鎖資源,清理保存點(diǎn)列表,清理回滾段
}
}
}
在任何情況下(機(jī)器掉電)mysqld crash 或者 os crash,MySQL 仍然能保證數(shù)據(jù)庫(kù)的一致性。數(shù)據(jù)的一致性是如何做到的哪?正是二階段提交。
我們結(jié)合幾種場(chǎng)景來(lái)分析下二階段提交是如何做到的:
1.prepare 階段,redo log 落盤前,mysqld crash
2.prepare 階段,redo log 落盤后,binlog 落盤前,mysqld crash
3.commit 階段,binlog 落盤后,mysqld crash
對(duì)于第一種情況,由于 redo 沒(méi)有落盤,毫無(wú)疑問(wèn),事務(wù)的更新肯定沒(méi)有寫入磁盤,數(shù)據(jù)庫(kù)的一致性受影響;
對(duì)于第二種情況,這時(shí)候 redo log 寫入完成,但 binlog 還未寫入,事務(wù)處于 TRX_STATE_PREPARED 狀態(tài),這是提交還是回滾呢?
對(duì)于第三種情況,此時(shí),redo log 和 binlog 都已經(jīng)落盤,只是 undo 狀態(tài)沒(méi)有更新,雖然 redo log 和 binlog 已經(jīng)一致了,事務(wù)是否應(yīng)該提交?
我們結(jié)合 mysqld 異常重啟后的執(zhí)行邏輯以及關(guān)鍵的源代碼。
對(duì)于第三種情況,我們可以搜集到未提交事務(wù)的 binlog event,所以需要提交;
對(duì)于第二種情況,由于 binlog 未寫入,需要通過(guò)執(zhí)行回滾操作來(lái)保證數(shù)據(jù)庫(kù)的一致性。
異常重啟后,如何判斷事務(wù)該提交還是回滾
1. 讀 binlog 日志,獲取崩潰時(shí)沒(méi)有提交的 event; //info- commit_list 中含有該元素
2. 若存在,則對(duì)應(yīng)的事務(wù)要提交;否則需要回滾。
判斷事務(wù)提交或回滾源碼如下:
上面討論了兩階段提交的基本流程,以及服務(wù)器異常 crash 后,mysql 如何重啟恢復(fù)保證 binlog 和數(shù)據(jù)的一致性。
簡(jiǎn)而言之,對(duì)于異常的 xa 事務(wù),若 binlog 已落盤,則事務(wù)應(yīng)該提交;binlog 未落盤,則事務(wù)就應(yīng)該回滾。
// 異常重啟后,回滾流程
innobase_rollback_by_xid
rollback_by_xid
trx_rollback_resurrected
trx_rollback_active
row_undo
{ // 從回滾頁(yè)獲取 undo 記錄 // 分析 undo 記錄類型 if (insert)
row_undo_ins else row_undo_mod
}
// 異常重啟后,提交流程
commit_by_xid
trx_commit_for_mysql
// 寫 binlog 接口
handler.cc:binlog_log_row
sql/binlog.cc:commit
mysys/my_sync:my_sync
sql/binlog.cc:sync_binlog_file
handler/ha_innodb.cc:innobase_xa_prepare
binlog 日志文件是為了解決 MySQL 主從復(fù)制功能而引入的一份新日志文件,它包含了引發(fā)數(shù)據(jù)變更的事件日志集合。
從庫(kù)請(qǐng)求主庫(kù)發(fā)送 binlog 并通過(guò)日志事件還原數(shù)據(jù)寫入從庫(kù),所以從庫(kù)的數(shù)據(jù)來(lái)源為 binlog。
這樣 MySQL 主庫(kù)只需做到 binlog 與本地?cái)?shù)據(jù)一致就可以保證主從庫(kù)數(shù)據(jù)一致(暫且忽略網(wǎng)絡(luò)傳輸引發(fā)的主從不一致)。
“MySQL 事務(wù)的提交過(guò)程”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注丸趣 TV 網(wǎng)站,丸趣 TV 小編將為大家輸出更多高質(zhì)量的實(shí)用文章!