小編的世界 優質文選 資料
字體大小:
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
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中策略。