More  

小編的世界 優質文選 資料

事務與日志流程


2021年10月29日 - 資料小編 卷心菜 
   

卷心菜

一、事務

1、事務介紹

事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永遠更新面向數據的資源。通過將一組相關操作組合為一個要麼全部成功要麼全部失敗的單元,可以簡化錯誤恢複病史應用程序更加可靠。一個邏輯共奏單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性:

原子性(A) - 對於數據修改,要麼全部都執行,要麼全都不執行。隔離性(C) - 在所有的操作沒有執行完畢之前,其他會話不能夠看到中間改變的一致性(I) - 事務發生前和發生後,根據數據的規則,總額應該匹配。持久性(D) - 事務一旦被提交,其結果就是永久性的,系統崩潰也不會影響

在 MySQL 命令行的默認設置下,事務是自動提交的,即執行了SQL 語句之後會馬上執行 commit操作。還可以用 begin 、start transaction 來顯式的開始一個事務。commit 在默認設置下是等價於 commit work 的,表示提交事務。rollback 在默認設置下等價於 rollback work,表示事務回滾。savepoint xxx 表示定義一個保存點,在一個事務中可以有多個 保存點。release savepoint xxx 表示刪除一個保存點,當沒有該保存點的時 候執行該語句,會拋出一個異常。rollback to xxx 表示回滾到某個保存點。

2、事務的實現

原子性的實現

想要保證事務的原子性,就需要在異常發生時,對已經執行的操作進行回滾,而在MySQL 中,恢複機制是通過回滾日志(undo log)實現的,所有事務進行的修改都會先記錄到這個回滾日志中,然後在對數據庫中的對應行進行寫入。

在日志文件中:在事務中使用的每一條 INSERT 都對應了一條 DELETE,每一條 UPDATE 也都對應一條相反的 UPDATE 語句。回滾日志最終落在了磁盤上。在5.6版本之前的undo log存儲在ibdata中,在後續的版本則單獨存儲,mysql8則存放在undo_log中,如下圖:

當mysql重啟時判斷是否提交,如果沒有提交則自動回滾。回滾日志(undo log)一定是先刷新到磁盤中的。當系統崩潰時會自動執行回滾,另外可以手動執行undo log 文件進行回滾。

持久化的實現

事務commit提交後會先記錄到重做日志redo log 中。然後根據規則在刷新到磁盤中。事務被提交,數據一定會被寫入到數據庫中並持久存儲起來,通常來說當事務已經被提交之後,就無法再次回滾了。與原子性一樣,事務的持久性也是通過日志來實現的,MySQL 使用重做日志(redo log)實現事務的持久性,重做日志由兩部分組成,一是內存中的重做日志緩沖區,因為重做日志緩沖區在內存中,所以它是易失的,另一個就是在磁盤上的重做日志文件,它是持久的。

在mysql中,日志優先。因為日志沒有太多的要求,他轉化為二進制後會非常的小。是很快能夠寫入的,但是數據就會有比較大的時間消耗。所以如果在已經commit且已redo log寫入到日志中後系統崩潰了,則可以通過redo log 重新刷新到磁盤。

隔離性的實現

原子性和持久性是為了保持事務本身的性質。隔離性是指事務之間應保持的關系。隔離性要求不同事務之間影響是互不干擾的。隔離性的實現離不開鎖機制的存在。

一致性的實現

一致性主要與他們的校驗有關系。它通過檢查點CKP的情況來確保事務的合法性與沒有被破壞。

二、事務日志

沒有手動開啟事務的SQL寫操作語句,默認會增加事務。如下圖。

對mysql來說,事務日志有限於數據。Innodb的事務日志是指Redo log,簡稱Log,保存在日志文件ib_logfile裏面。Innodb還有另外一個日志Undo log,但Undo log是存放在共享表空間裏面的(ibdata*文件,存儲的是check point日志序列號)。

1、事務日志流程

以"update user set name=0 where id=1"為例,如下圖。

sql(寫操作)會進入到mysql的innodb中。mysql 會根據sql語句的條件從磁盤中查找id=1的數據,並放入到buffer pool中。mysql記錄回滾日志undo logmysql執行器更新buffer pool中的數據為name=0執行器把更新後的數據放入重做日志redo log buffer中。生成重做日志redo log。mysql執行器把更新後的數據生成binlog。mysql在根據生成的redo log和binlog,以2PC的方式確認數據的正確性。如果無誤,則存儲到磁盤中。

如上,在整個過程中,buffer pool、innodb_log_buffer、log buffer 是存儲在內存中的。剩餘類似binlog等,是存儲在磁盤上的。

2、mysql刷新策略與高並發

innodb_flush_log_at_trx_commit=0|1|2 是控制mysql事務日志刷新策略,注意本設置主要是針對於rado log日志的寫入,而不是指定數據,是指重做日志的寫入。高並發場景下會設置為2 折中方案。如下圖

第1種策略:mysql默認是第1種策略,每次都刷新磁盤。這樣能保證數據實時寫入,如果系統出現問題只限於最後一條信息丟失。數據庫出問題常常出現在commit前後,在commit前有undo log可以回滾。在commit後有redo log日志。可以盡可能保證數據完整性。不過策略1性能最低。第0種策略:在生產undo log且存儲到innodb_log_buffer後,再到磁盤。每1秒flush 到log file且寫入磁盤文件。如果在flush的時候mysql發生異常,會丟失1s的數據。如果此時有N個事務,則innodb_log_buffer中會記錄N個事務信息結合。如在高並發的場景下,可能瞬間有上萬個事務同時進行。這樣可能就會丟失1W*1s的數據,損失就比較大了。這裏的丟失主要是因為mysql進程異常丟失的數據。第2種策略:策略2和策略0相似,不過策略2寫入是日志緩存write寫入log buffer。先寫入到系統的文件緩存中,在每1s寫入數據。再通過它每隔1s寫入磁盤。在這裏發生異常,同樣會丟失1s的數據。這裏主要是因為系統崩潰發生異常導致的數據丟失1s的數據。 write 是從一塊內存寫入到系統os的緩存裏面。是從一塊內存寫入到另一塊內存。而fluash刷新是從緩存中的信息寫入到磁盤。同比之下系統崩潰的可能性比mysql經常異常的概率要小的多。從性能比較來說,策略1性能最低。策略2居中。性能最好的是策略1。但是從性能和數據的可靠性來說策略2的性價比更高。所以高並發下我們往往選擇第2中策略。

  大家在看