個人MySQL的事務特性原理學習筆記總結
個人MySQL的事務特性原理筆記總結
事務Transaction
是資料庫區別於檔案系統的重要特性之一,也是MySQL等關係型資料庫區別於NoSQL資料庫的重要方面。本文會先介紹事務的4個特性,然後講解每個特性在MySQL中的實現原理。過程中有不對的地方,歡迎讀者指出矯正與探討。
一、基礎概念
### 1. 事務定義
根據MySQL官網介紹:
參考
Transactions are atomic units of work that can be committed or rolled back. When a transaction makes multiple changes to the database, either all the changes succeed when the transaction is committed, or all the changes are undone when the transaction is rolled back.
事務是可以提交 或回退的原子工作單元。當事務對資料庫進行多次更改時,要麼在提交事務後所有更改成功,要麼在回滾事務後撤消所有更改。
2. 事務控制語句
- START TRANSACTION | BEGIN:顯示地開啟一個事務。
- COMMIT:也可以寫成COMMIT WORK,會提交事務,修改資料會寫入redo日誌進行持久化。
- ROLLBACK:回滾會結束使用者的事務,並撤銷在該事務中已經修改的資料。
3. 事務特性
事務具體有四個特性ACID,嚴格來說,事務一定要同時滿足這4個特性。但是不同的資料庫廠商會在這個定義上有所調整。例如在MYSQL中,也只有InnoDB儲存引擎完全滿足ACID,像MyISAM儲存引擎就完全不支援事務。ACID是以下4個詞的縮寫:
- 原子性(atomicity)
- 一致性(consistency)
- 隔離性(isolation)
- 永續性(durability)
如無特殊說明,後文中描述的內容都是基於InnoDB儲存引擎。
二、原子性
1. 原子性定義
原子性指一個事務是資料庫中不可分割的工作單位。只有事務中的所有操作CRUD
都執行成功,才算整個事務成功。事務中的任何一個SQL語句執行失敗,已經執行成功的SQL語句也必須撤銷,資料庫狀態應該回到執行事務前的狀態。
2. 實現
在說明實現原理之前,先介紹一下MySQL的事務日誌。MySQL的日誌有很多種,比如:二進位制日誌、查詢日誌、慢查詢日誌、錯誤日誌。此外InnoDB儲存引擎還提供了兩種事務日誌:redo log(重做日誌)和undo log(回滾日誌)。其中redo log用於保證事務資料的持久化,undo log用於事務的回滾以及MVCC機制。
說回undo log,實現原子性的保證,是當事務回滾時能夠撤銷該事務中已經執行成功的所有SQL語句。undo log 能夠保證原子性在於:當事務對資料庫進行資料修改、刪除、增加操作時,會產生對應的undo log日誌,這樣如果事務或者語句由於某種原因失敗了,又或者執行了ROLLBACK語句請求回滾,就可以利用這些undo資訊將資料回滾到修改之前的樣子。
undo是邏輯日誌,會記錄事務中執行的SQL語句。當InnoDB儲存引擎回滾時,它會根據undo log的中事務的語句,執行相反的SQL語句。對於每個INSERT,InnoDB儲存引擎會完成一個DELETE;對於每個DELETE,InnoDB儲存引擎會執行一個INSERT;對於每個UPDATE,InnoDB儲存引擎會執行一個相反的UPDATE,將修改前的行修改回去。
MySQL在information_schema資料庫下新增一張資料字典表為INNODB_TRX_UNDO,用來記錄事務對應的undo log。該字典表會記錄當前事務的這些資訊:事務ID、回滾事務ID、插入型別、參與的表ID、發生變化的資料資訊等.這樣在需要會根據對應資訊進行回滾。
三、永續性
1. 定義
事務一旦提交,其結果就是永久性的。即使發生宕機等故障,資料庫也能將資料恢復。
2. 實現
永續性的實現依賴於上面提到的另一個事務日誌:redo log日誌,也叫重做日誌。重做日誌由兩部分組成:一個是記憶體中的重做日誌緩衝redo log buffer
,它是易失的;二是重做日誌檔案,redo log file
,它是持久的。
InnoDB是事務的儲存引擎,其通過Force Log at Commit機制實現事務的永續性。當對資料庫進行修改時,會將執行的語句先新增到redo log buffer
中,當事務提交COMMIT
時,InnoDB儲存引擎會將redo log buffer
的日誌寫入到redo log日誌中進行持久化。同時還會進行一次fsync操作,可以確保重做日誌寫入磁碟。
3. redo log存在的背景
InnoDB作為MySQL的儲存引擎,資料是存放在磁碟中的,但如果每次讀寫資料都需要磁碟IO,效率會很低。為此,InnoDB提供了快取(Buffer Pool),Buffer Pool中包含了磁碟中部分資料頁的對映,作為訪問資料庫的緩衝:當從資料庫讀取資料時,會首先從Buffer Pool中讀取,如果Buffer Pool中沒有,則從磁碟讀取後放入Buffer Pool;當向資料庫寫入資料時,會首先寫入Buffer Pool,Buffer Pool中修改的資料會定期重新整理到磁碟中(這一過程稱為髒頁重新整理)。
Buffer Pool的使用大大提高了讀寫資料的效率,但是也帶了新的問題:如果MySQL宕機,而此時Buffer Pool中修改的資料還沒有重新整理到磁碟,就會導致資料的丟失,事務的永續性無法保證。
於是,redo log被引入來解決這個問題:當資料修改時,除了修改Buffer Pool中的資料,還會在redo log記錄這次操作;當事務提交時,會呼叫fsync介面對redo log進行刷盤。如果MySQL宕機,重啟時可以讀取redo log中的資料,對資料庫進行恢復。redo log採用的是WAL(Write-ahead logging,預寫式日誌),所有修改先寫入日誌,再更新到Buffer Pool,保證了資料不會因MySQL宕機而丟失,從而滿足了永續性要求。
既然redo log也需要在事務提交時將日誌寫入磁碟,為什麼它比直接將Buffer Pool中修改的資料寫入磁碟(即刷髒)要快呢?主要有以下兩方面的原因:
- 刷髒是隨機IO,因為每次修改的資料位置隨機,但寫redo log是追加操作,屬於順序IO。
- 刷髒是以資料頁(Page)為單位的,MySQL預設頁大小是16KB,一個Page上一個小修改都要整頁寫入;而redo log中只包含真正需要寫入的部分,無效IO大大減少。
附上一張InnoDB記憶體資料物件圖
四、隔離性
1. 定義
事務的隔離性是指__事務內的資料庫操作與其他事務是隔離的,即該事務提交前對其他事務都不可見,事務間的操作是相互不影響的。__
2. 鎖機制
隔離性的原理主要依賴鎖來完成。可以參考我的另一篇文章 https://www.cnblogs.com/process-h/p/14173838.html
五、一致性
1. 定義
一致性是指__事務將資料庫從一種狀態轉變為下一種一致的狀態。__在事務開始之前和事務結束以後,資料庫的完整性約束沒有被破壞。比如說在表中有一個姓名欄位,是唯一約束(即姓名不可重複),這時有個事務對姓名欄位做了修改,但是事務提交或回滾後,姓名欄位變得非唯一了,那這就破壞了事務的一致性要求。
2. 實現
一致性也是事務追求的最終目的:__前面提到的原子性、永續性和隔離性,都是為了保證資料庫狀態的一致性。__不過,僅僅靠資料庫來實現一致性還不夠,也需要應用層面提供保障,比如在涉及轉賬業務時,A賬戶轉出100,B賬戶入賬100這2步需要一起完成,而不能只從A賬戶轉出100.
參考文獻
《深入淺出MySQL》
《MySQL技術內幕:InnoDB儲存引擎》
https://dev.mysql.com/doc/refman/5.6/en/mysql-acid.html
https://dev.mysql.com/doc/refman/5.6/en/innodb-multi-versioning.html
https://www.cnblogs.com/kismetv/p/10331633.html#!comments