提升性能與一致性:MySQL 5.7中的組提交與兩階段提交機制解析
MySQL 的組提交(Group Commit)是一項優(yōu)化技術(shù),旨在提升數(shù)據(jù)庫系統(tǒng)的性能與事務(wù)處理效率。它通過將多個事務(wù)的提交操作合并為一個批處理操作,減少磁盤 IO 和鎖定開銷,從而加速事務(wù)處理。
我們的數(shù)據(jù)庫需頻繁執(zhí)行數(shù)據(jù)變更操作,并將變更數(shù)據(jù)持久化,以便進(jìn)行崩潰恢復(fù)、主從同步及回滾等,這涉及到 binlog、redolog 及 undolog 的寫入。頻繁的文件寫入會觸發(fā)頻繁的磁盤操作。為降低提交操作的開銷,MySQL 引入了組提交技術(shù),將多個事務(wù)的提交操作合并為一個批處理操作,以減少磁盤 IO 次數(shù)。此批處理操作包含多個事務(wù)的修改,并一次性寫入二進(jìn)制日志。
通過以下命令可以查看組提交的配置:
mysql> show variables like '%group_commit%';
+-----------------------------------------+-------+
| Variable_name | Value |
+-----------------------------------------+-------+
| binlog_group_commit_sync_delay | 0 |
| binlog_group_commit_sync_no_delay_count | 0 |
+-----------------------------------------+-------+
2 rows in set (0.00 sec)
- binlog_group_commit_sync_delay
延遲多長時間再通過 fsync 進(jìn)行刷盤,將數(shù)據(jù)持久化
- binlog_group_commit_sync_no_delay_count
- 累積多少次操作后再通過 fsync 進(jìn)行刷盤,將數(shù)據(jù)持久化
注意,這兩個條件是或的關(guān)系,只要滿足其一,即會觸發(fā)提交動作。
說到這里我們不得不提一下什么是事務(wù)的兩階段提交。
什么是事務(wù)的 2 階段提交?
所謂的 MySQL 事務(wù)的兩階段提交,是在更新過程中,確保 binlog 和 redolog 一致性的一種手段。
圖片
上圖中右側(cè)部分即為兩階段提交。其過程如下:
- Prepare 階段
此階段 SQL 已成功執(zhí)行并生成 redolog,處于準(zhǔn)備階段。
- BinLog 持久化
- binlog 提交,通過 write() 將 binlog 內(nèi)存日志數(shù)據(jù)寫入文件緩沖區(qū);
- 通過 fsync() 將 binlog 從文件緩沖區(qū)永久寫入磁盤。
- Commit
- 在執(zhí)行引擎內(nèi)部執(zhí)行事務(wù)操作,更新 redolog,處于提交階段。
write 和 fsync 是與文件系統(tǒng)和磁盤 IO 相關(guān)的兩個不同操作。
write 操作將數(shù)據(jù)寫入文件的緩沖區(qū),這意味著 write 操作完成后,并不一定立即將數(shù)據(jù)持久化到磁盤上,而是將數(shù)據(jù)暫時存儲在內(nèi)存中。
fsync 用于強制將文件的修改持久化到磁盤上。它通常與 write 配合使用,以確保文件的修改在 fsync 操作完成后被寫入磁盤。
那么,為什么這個過程需要用兩階段提交的方式呢?
假設(shè)我們執(zhí)行一條 SQL 語句,修改它的 name 為 Paidaxing :update user set name = 'paidaxing' where id = 10。
如果先寫入 redo log 成功,但還未寫入 bin log 時系統(tǒng)崩潰。MySQL 重啟后,可以根據(jù) redolog 將記錄更新為'paidaxing'。但由于 binlog 未成功寫入,無法記錄這次變更,主備同步時缺少這條 SQL,導(dǎo)致主備庫之間數(shù)據(jù)不一致。
反之,先寫入 binlog 成功,但未及寫入 redolog 時系統(tǒng)崩潰。MySQL 重啟后,由于 redo log 未寫入,數(shù)據(jù)庫記錄保持舊值。但 binlog 已成功寫入,主備同步時將新值同步到備庫,導(dǎo)致主備庫之間數(shù)據(jù)不一致。
如上述例子所示,如果不引入兩階段提交,在 bin log 和 redo log 無法保證一致性的情況下,主備庫之間的數(shù)據(jù)會不一致。
為了解決這一問題,引入了兩階段提交,以整體控制 redo log 和 bin log 的一致性寫入。
2 階段如何保證一致性的?
引入兩階段提交之后,事務(wù)的提交過程可能有以下三種情況:
情況一:一階段提交之后崩潰即在寫入 redo log,處于 prepare 狀態(tài)的時候崩潰。此時已經(jīng)寫了 redo log,并處于 prepare 狀態(tài),但 binlog 還沒寫入。此時如果崩潰恢復(fù),直接回滾事務(wù)即可,這樣主備庫是一致的,都沒有執(zhí)行這個事務(wù)。
情況二:一階段提交成功,寫完 binlog 之后崩潰此時,redo log 處于 prepare 狀態(tài),binlog 已寫入。這時檢查 binlog 中的事務(wù)是否存在并且完整。如果存在且完整,則直接提交事務(wù);如果不存在或者不完整,則回滾事務(wù)。
情況三:redolog 處于 commit 狀態(tài)時崩潰重啟后的處理方案同情況二。
由此得出結(jié)論,兩階段提交能夠確保數(shù)據(jù)的一致性。
如何判斷 binlog 和 redolog 達(dá)成一致了?
當(dāng) MySQL 寫完 redo log 并將其標(biāo)記為 prepare 狀態(tài)時,會在 redo log 中記錄一個 XID,該 XID 全局唯一地標(biāo)識著這個事務(wù)。而當(dāng)你設(shè)置 sync_binlog=1 時,在完成上述第一階段寫 redo log 后,MySQL 會對應(yīng) binlog 并將其直接刷新到磁盤中。
下圖展示了磁盤上的 row 格式的 binlog 記錄。在 binlog 結(jié)束的位置也記錄了一個 XID。
圖片
只要這個 XID 與 redo log 中記錄的 XID 一致,MySQL 就會認(rèn)為 binlog 和 redo log 在邏輯上是一致的。
言歸正傳:
在引入組提交之后,兩階段提交的過程會發(fā)生一些變化,因為日志的刷盤過程會因組提交而需要等待,因此情況會變成這樣:
圖片
這里的 write 和 fsync 是與文件系統(tǒng)和磁盤 IO 相關(guān)的兩個不同操作。
write 操作將數(shù)據(jù)寫入文件的緩沖區(qū),這意味著 write 操作完成后,數(shù)據(jù)并不一定立即持久化到磁盤,而是暫時存儲在內(nèi)存中。
fsync 用于強制將文件的修改持久化到磁盤上。它通常與 write 配合使用,以確保文件的修改在 fsync 操作完成后被寫入磁盤。
因此,用于將緩沖區(qū)內(nèi)容持久化到磁盤的 fsync 步驟被延遲了。它會等待一個組中的多個事務(wù)都處于 Prepare 階段后,再進(jìn)行一次組提交,即將日志一次性持久化到磁盤中。