天天看點

ACID 的作用以及實作原理

資料庫事務的四大特性,分别是原子性、一緻性、隔離性和持久性,簡稱為ACID。本文是對四大特性及它們在MySQL中的實作原理的介紹。

原子性

原子性是指一個事務是一個不可分割的工作機關,其中的操作要麼都做,要麼都不做。

如果事務中一個sql語句執行失敗,則已執行的語句也必須復原,資料庫退回到事務前的狀态。是以實作原子性的關鍵,是當事務復原時能夠撤銷所有已經成功執行的sql語句。

首先我們要簡單了解InnoDB存儲引擎提供的兩種事務日志:redo log(重做日志)和undo log(復原日志)。redo log用于保證事務持久性;undo log則是事務原子性和隔離性實作的基礎。

InnoDB實作復原,靠的是undo log:當事務對資料庫進行修改時,InnoDB會生成對應的undo log;如果事務執行失敗或調用了rollback,導緻事務需要復原,便可以利用undo 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當機而丢失,進而滿足了持久性要求。

隔離性

隔離性是指,事務内部的操作與其他事務是隔離的,并發執行的各個事務之間不能互相幹擾。

隔離性追求的是并發情形下事務之間互不幹擾,這與多線程程式的執行過程類似。簡單起見,我們僅考慮最簡單的讀操作和寫操作(暫時不考慮帶鎖讀等特殊操作),那麼隔離性的探讨,主要可以分為兩個方面:

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

(一個事務)寫操作對(另一個事務)讀操作的影響:MVCC(多版本并發控制)保證隔離性。

如果一個事務A在執行寫操作時,如果遇到另一個事務B想要執行寫操作,那麼B的寫操作是會被阻塞的,至于鎖住的是資料行還是整個表,這要看隔離級别(級别越高越安全,但是效率也越低);而事務B如果想要執行的是讀操作,它将被允許通過MVCC提供的資料版本資訊來讀資料。

一緻性

一緻性是指事務執行結束後,資料庫的完整性限制沒有被破壞,事務執行的前後都是合法的資料狀态。資料庫的完整性限制包括但不限于:實體完整性(如行的主鍵存在且唯一)、列完整性(如字段的類型、大小、長度要符合要求)、外鍵限制、使用者自定義完整性(如轉賬前後,兩個賬戶餘額的和應該不變)。

我對一緻性的了解是:資料庫能正确地執行事務。資料庫本身的完整性限制不會被破壞,事務的邏輯能夠成功地執行并對資料産生正确的影響。

實作一緻性的措施包括:

保證原子性、持久性和隔離性,如果這些特性無法保證,事務的一緻性也無法保證

資料庫本身提供保障,例如不允許向整形列插入字元串值、字元串長度不能超過列的限制等

應用層面進行保障,例如如果轉賬操作隻扣除轉賬者的餘額,而沒有增加接收者的餘額。