【MySQL(十八)】事務 兩段式提交

mysql在事務執行時,需要寫入兩種日誌,一種是server層的binlog,另一種是引擎層的redo log。事務commit時,以上兩種類型的日誌的寫入需要遵循兩段式提交協議。

什麼是兩段式提交?

爲了保證分佈式事務場景下事務的一致性。因爲在分佈式背景下,事務語句來自不同實例,因此需要一個協調者角色。每一個實例的語句先執行prepare,並且將結果告知協調者,如果全部成功,協調者再發出commit命令,各個實例再執行提交。防止部分實例失敗,導致分佈式事務語句沒有全部執行。當然兩階段提交併不能完全解決問題,只是一種思路。

mysql裏,因爲binlog和redo log也是不同層面的兩種日誌,也有分佈式事務的特徵,即要麼全部成功要麼全部失敗,所以也使用了兩段式提交的思路。

爲什麼必須兩段式提交?

因爲如果redo log和binlog只有一個執行成功,將會導致主從不一致。比如只有redo log成功,那麼從庫無法更新;如果只有binlog成功,主庫無法更新。

redo log和binlog爲什麼必須是兩個?能否合二爲一?

個人認爲,理論上設計一種新的log,可以具備redo log和binlog的全部特性,那當然是可以的。但是,mysql在發展過程中的演化,導致了現在兩種log並存的場面。redo log是innodb引擎特有的日誌,在引擎內生成,用於保證事務的持久性。binlog是mysql的server層日誌,用於做歸檔,可以複製和增量備份。binlog是server層特性,與用什麼引擎沒有關係。

那麼,在目前的情況下,二者是否可以合二爲一?

比如只用redo log,不行,因爲只有innodb採用redo log,這樣換成其他引擎不就玩完了?什麼,讓其他引擎也強加上redo log?那也不行,因爲redo log不支持歸檔,redo log是循環寫入的,寫完後就必須清空之前的文件才能繼續寫入;

那隻用binlog,也不行,因爲binlog並不會記錄更新的細節信息,比如修改了哪個頁的哪一個偏移位置的數據,只有binlog缺乏恢復數據的能力。

redo log和binlog如何關聯?

有一個公共的字段叫做XID,在兩個日誌裏都存在。可以當成是事務的唯一id。

兩段式提交過程?

先執行redo log的prepare階段。會將事務內容寫入redo log。事務處於prepare階段。具體怎麼寫由innodb_flush_log_at_trx_commit參數控制:

0:只寫入log buffer,不持久化;

1:調用fsync持久化至磁盤;

2:調用write寫入文件系統的page cache,由操作系統決定持久化時機;

另外,mysql會有一個後臺線程每隔一秒將log buffer的redo log持久化至磁盤,fsync方式。

一般會選擇設置爲2,這樣性能好一些,但是如果在fsync之前,系統掛了,redo log就丟了。而對於金融類的系統,需要較高可靠性,可以設置爲1。

接着會執行binlog的寫入,在事務提交之前,binlog也是記錄到binlog cache中的,在提交時,會將binlog持久化,具體行爲由另一個參數sync_binlog控制:

0:調用write寫入文件系統的page cache;

1:調用fsync持久化至磁盤;

N:每次提交只write,直到累積N個事務後再fsync;

同樣的,對於金融類等系統,需要設置爲1。兩個參數都設置爲1的配置方式被稱爲“雙1配置”,可以保證不丟數據。

最後執行兩段式提交的commit,將本地redo log commit;

如何恢復數據?

如果寫binlog前掛了,事務不會在主庫和從庫提交,需要回滾這部分事務;

如果commit前掛了,事務處於prepare,但是已經寫了binlog,由於binlog有可能沒有fsync,所以需要判斷binlog是否完整。怎麼取?從本地redo log中取到對應事務的XID,看XID對應的事務是否已經在binlog中被提交,如果是,則提交本地redo log,否則回滾;那又是如何判斷binlog是否完整?如果是語句模式,會有commit語句;如果是行模式,會有XID Event;

當然由於prepare階段redo log的並一定fsync,所以這裏可能會丟本地數據。

組提交

實際上,爲了優化fsync性能,binlog和redo log在持久化時都使用了組提交機制,也就是一組日誌一起fsync,提升了性能。

如何保證兩個log的執行順序

待補充

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