什么是ACID


ACID是衡量事務的四個特性:

  • 原子性(Atomicity,或稱不可分割性)
  • 一致性(Consistency)
  • 隔離性(Isolation)
  • 持久性(Durability)

原子性:原子性是指一個事務是一個不可分割的工作單位,其中的操作要么都做,要么都不做;如果事務中一個sql語句執行失敗,則已執行的 語句也必須回滾,數據庫退回到事務前的狀態。

實現原理:undo log

在說明原子性原理之前,首先介紹一下MySQL的事務日志。MySQL的日志有很多種,如二進制日志、錯誤日志、查詢日志、慢查詢日志等,此外InnoDB存儲引擎還提供了兩種事務日志:redo log(重做日志)和undo log(回滾日志)。其中redo log用於保證事務持久性;undo log則是事務原子性和隔離性實現的基礎。

下面說回undo log。實現原子性的關鍵,是當事務回滾時能夠撤銷所有已經成功執行的sql語句。InnoDB實現回滾,靠的是undo log:當事務對數據庫進行修改時,InnoDB會生成對應的undo log;如果事務執行失敗或調用了rollback,導致事務需要回滾,便可以利用undo log中的信息將數據回滾到修改之前的樣子。

undo log屬於邏輯日志,它記錄的是sql執行相關的信息。當發生回滾時,InnoDB會根據undo log的內容做與之前相反的工作:對於每個insert,回滾時會執行delete;對於每個delete,回滾時會執行insert;對於每個update,回滾時會執行一個相反的update,把數據改回去。

以update操作為例:當事務執行update時,其生成的undo log中會包含被修改行的主鍵(以便知道修改了哪些行)、修改了哪些列、這些列在修改前后的值等信息,回滾時便可以使用這些信息將數據還原到update之前的狀態。

持久性:持久性是指事務一旦提交,它對數據庫的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。

 

實現原理:redo log

 

redo log和undo log都屬於InnoDB的事務日志。下面先聊一下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大大減少。

隔離性:與原子性、持久性側重於研究事務本身不同,隔離性研究的是不同事務之間的相互影響。隔離性是指,事務內部的操作與其他事務是隔離的,並發執行的各個事務之間不能互相干擾。嚴格的隔離性,對應了事務隔離級別中的Serializable (可串行化),但實際應用中出於性能方面的考慮很少會使用可串行化。

隔離性追求的是並發情形下事務之間互不干擾。簡單起見,我們僅考慮最簡單的讀操作和寫操作(暫時不考慮帶鎖讀等特殊操作),那么隔離性的探討,主要可以分為兩個方面:

  • (一個事務)寫操作對(另一個事務)寫操作的影響:鎖機制保證隔離性
  • (一個事務)寫操作對(另一個事務)讀操作的影響:MVCC保證隔離性

2. 鎖機制

首先來看兩個事務的寫操作之間的相互影響。隔離性要求同一時刻只能有一個事務對數據進行寫操作,InnoDB通過鎖機制來保證這一點。

鎖機制的基本原理可以概括為:事務在修改數據之前,需要先獲得相應的鎖;獲得鎖之后,事務便可以修改數據;該事務操作期間,這部分數據是鎖定的,其他事務如果需要修改數據,需要等待當前事務提交或回滾后釋放鎖。

行鎖與表鎖

按照粒度,鎖可以分為表鎖、行鎖以及其他位於二者之間的鎖。表鎖在操作數據時會鎖定整張表,並發性能較差;行鎖則只鎖定需要操作的數據,並發性能好。但是由於加鎖本身需要消耗資源(獲得鎖、檢查鎖、釋放鎖等都需要消耗資源),因此在鎖定數據較多情況下使用表鎖可以節省大量資源。MySQL中不同的存儲引擎支持的鎖是不一樣的,例如MyIsam只支持表鎖,而InnoDB同時支持表鎖和行鎖,且出於性能考慮,絕大多數情況下使用的都是行鎖。

一致性:一致性是指事務執行結束后,數據庫的完整性約束沒有被破壞,事務執行的前后都是合法的數據狀態。數據庫的完整性約束包括但不限於:實體完整性(如行的主鍵存在且唯一)、列完整性(如字段的類型、大小、長度要符合要求)、外鍵約束、用戶自定義完整性(如轉賬前后,兩個賬戶余額的和應該不變)。

 實現

可以說,一致性是事務追求的最終目標:前面提到的原子性、持久性和隔離性,都是為了保證數據庫狀態的一致性。此外,除了數據庫層面的保障,一致性的實現也需要應用層面進行保障。

實現一致性的措施包括:

  • 保證原子性、持久性和隔離性,如果這些特性無法保證,事務的一致性也無法保證
  • 數據庫本身提供保障,例如不允許向整形列插入字符串值、字符串長度不能超過列的限制等
  • 應用層面進行保障,例如如果轉賬操作只扣除轉賬者的余額,而沒有增加接收者的余額,無論數據庫實現的多么完美,也無法保證狀態的一致

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM