1. 程式人生 > 實用技巧 >個人MySQL的事務特性原理學習筆記總結

個人MySQL的事務特性原理學習筆記總結

目錄

個人MySQL的事務特性原理筆記總結

​ 事務Transaction是資料庫區別於檔案系統的重要特性之一,也是MySQL等關係型資料庫區別於NoSQL資料庫的重要方面。本文會先介紹事務的4個特性,然後講解每個特性在MySQL中的實現原理。過程中有不對的地方,歡迎讀者指出矯正與探討。

一、基礎概念

	### 1. 事務定義

​ 根據MySQL官網介紹:
​ 參考

https://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_acid

​ 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中修改的資料寫入磁碟(即刷髒)要快呢?主要有以下兩方面的原因:

  1. 刷髒是隨機IO,因為每次修改的資料位置隨機,但寫redo log是追加操作,屬於順序IO。
  2. 刷髒是以資料頁(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