資料庫事務的方方面面

西召發表於2019-05-05

事務是關係型資料的一個重要特性,但很少有人能對事務有全面性的瞭解,這篇文章就把事務的方方面面講給你。

事務的概念

什麼是事務

資料庫事務(事務)是資料庫管理系統執行過程中的一個邏輯單位,由一個有限的資料庫操作序列構成。

併發問題

五種併發問題分別是:

  • 第一類丟失更新
  • 第二類丟失更新
  • 髒讀
  • 不可重複讀
  • 幻讀

下面依次舉例講解一下。

更新丟失(Lost Update)

一個事務覆蓋另一個事務已提交的更新資料叫丟失更新。

第一類丟失更新:

時間 draw money事務 deposit money事務
T1 開始事務
T2 開始事務
T3 查詢賬戶餘額為10,000
T4 查詢賬戶餘額為10,000
T5 存入1,000並修改餘額為11,000
T6 提交事務
T7 取出1,000並修改餘額為9,000
T8 撤銷事務並回滾餘額為10,000

最終餘額少了1,000。

第二類丟失更新:

時間 draw money事務 deposit money事務
T1 開始事務
T2 開始事務
T3 查詢賬戶餘額為10,000
T4 查詢賬戶餘額為10,000
T5 取出1,000並修改餘額為9,000
T6 提交事務
T7 存入1,000並修改餘額為11,000
T8 提交事務

最終餘額多了1,000。

髒讀(Dirty Read)

一個事務讀取到另一個事務還沒提交的資料叫髒讀。

例如,事務A修改了一行資料,但沒有提交,事務 B讀取了被事務A修改後的資料,之後事務A因為某種原因Rollback了,那麼事務B讀取的資料就是髒的。

時間 draw money事務 deposit money事務
T1 開始事務
T2 開始事務
T3 查詢賬戶餘額為10,000
T4 取出1,000並修改餘額為9,000
T5 查詢賬戶餘額為9,000 (髒讀)
T6 撤銷事務並回滾餘額為10,000
T7 存入1,000並修改餘額為11,000
T8 提交事務

不可重複讀(NonRepeatable Read)

一個事務先後讀到另一個事務提交之前的資料和已提交的更新資料。

A和B事務併發執行,A事務查詢資料,然後B事務更新該資料,A再次查詢該資料時,發現該資料變化了。

不可重複讀經常發生在updatedelete操作。

時間 draw money事務 deposit money事務
T1 開始事務
T2 開始事務
T3 查詢賬戶餘額為10,000
T4 查詢賬戶餘額為10,000
T5 取出1,000並修改餘額為9,000
T6 提交事務
T7 查詢賬戶餘額為9,000
T8 同一個事務兩次查詢查到的結果不同

幻讀(Phantom Read)

事務在操作過程中進行兩次查詢,第二次查詢的結果包含了第一次查詢中未出現的資料或者缺少了第一次查詢中出現的資料。

例如,A和B事務併發執行,A事務查詢資料,B事務插入或者刪除資料,A事務再次查詢發現結果集中有以前沒有的資料,或者以前有的資料消失了,彷彿出現了幻覺。

幻讀經常發生在insert操作。

時間 draw money事務 deposit money事務
T1 開始事務
T2 開始事務
T3 查詢賬號數為100,000
T4 註冊新賬號
T5 提交事務
T6 查詢賬號數為100,001
T7 同一個事務兩次查詢查到的結果不同

事務的特性

事務有4個特性,被稱為ACID,分別是:

  • 永續性 (Durability)
  • 隔離性 (Isolation)
  • 一致性 (Consistency)
  • 原子性 (Atomicity)

原子性

一個事務(transaction)中的所有操作,要麼全部完成,要麼全部不完成,不會結束在中間某個環節。

事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。

一致性

在事務開始之前和事務結束以後,資料庫的完整性沒有被破壞。

即事務前後,資料庫的狀態都滿足所有的完整性約束。

隔離性

資料庫允許多個併發事務同時對其資料進行讀寫和修改的能力,隔離性可以防止多個事務併發執行時由於交叉執行而導致資料的不一致。

事務隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和序列化(Serializable)。

永續性

事務處理結束後,對資料的修改就是永久的,即便系統故障也不會丟失。

事務的隔離級別

在介紹事務的隔離性的適合提到了事務的4個隔離級別:

  • 未提交讀(Read Uncommitted)
  • 已提交讀(Read Committed)
  • 可重複讀(Repeatable Read)
  • 可序列化(Serializable)

同時事務的隔離級別也與前面提到的5中併發問題有關。隔離級別越高,越能保證資料的完整性和一致性,但是對併發效能的影響也越大。

隔離級別與併發問題:

隔離級別 髒讀 不可重複讀 幻讀
未提交讀 可能 可能 可能
已提交讀 不可能 可能 可能
可重複讀 不可能 不可能 可能
可序列化 不可能 不可能 不可能

對於多數應用程式,可以優先考慮把資料庫系統的隔離級別設為Read Committed。它能夠避免更新丟失、髒讀,而且具有較好的併發效能。儘管它會導致不可重複讀、幻讀這些併發問題,在可能出現這類問題的個別場合,可以由應用程式採用悲觀鎖或樂觀鎖來控制。

MySQL InnoDB 預設隔離級別是可重複讀(Repeatable read) 。Oracle等資料庫預設都是已提交讀(Read Committed),即只能讀取到已經提交的資料。

讀未提交(Read Uncommitted)

讀事務不阻塞其他讀事務和寫事務,未提交的寫事務阻塞其他寫事務但不阻塞讀事務。

此隔離級別可以防止更新丟失,但不能防止髒讀、不可重複讀、幻讀。

讀已提交(Read Committed)

讀事務允許其他讀事務和寫事務,未提交的寫事務禁止其他讀事務和寫事務。

讀未提交可以防止更新丟失、髒讀,但不能防止不可重複讀、幻讀。

可重複讀(Repeatable Read)

以操作同一行資料為前提,讀事務禁止其他寫事務但不阻塞讀事務,未提交的寫事務禁止其他讀事務和寫事務。

此隔離級別可以防止更新丟失、髒讀、不可重複讀,但不能防止幻讀。

可序列化(Serializable)

提供嚴格的事務隔離,它要求事務序列化執行,事務只能一個接著一個地執行,不能併發執行。

此隔離級別可以防止更新丟失、髒讀、不可重複讀、幻讀。

如果僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的資料不會被剛執行查詢操作的事務訪問到。

事務操作

BEGIN

BEGINSTART TRANSACTION :顯式開啟一個事務。

COMMIT

COMMITCOMMIT WORK : 提交事務,並使已對資料庫進行的所有修改成為永久性的。

ROLLBACK

ROLLBACKROLLBACK WORK : 回滾並撤銷正在進行的所有未提交的修改。

SAVEPOINT

SAVEPOINT identifier : SAVEPOINT 允許在事務中建立一個儲存點,一個事務中可以有多個 SAVEPOINT。

RELEASE SAVEPOINT identifier : 刪除一個事務的儲存點,當沒有指定的儲存點時,執行該語句會丟擲一個異常。

ROLLBACK

ROLLBACK : 回滾事務。

ROLLBACK TO identifier : 把事務回滾到儲存點。

TRANSACTION

SET TRANSACTION : 設定事務的隔離級別。

InnoDB 儲存引擎提供事務的隔離級別有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。

AUTOCOMMIT

select @@autocommit : 檢視事務自動提交設定。

set autocommit=0 : 設定事務不自動提交。

set autocommit=1 : 設定事務自動提交。

-- 檢視事務自動提交設定
select @@autocommit;
-- 設定事務不自動提交

set autocommit=0;

-- 設定事務自動提交
set autocommit=0;
複製程式碼

事務日誌

事務的機制實現很大一部依賴事務日誌檔案。

事務日誌是一個與資料庫檔案分開的檔案。它儲存對資料庫進行的所有更改,並全部記錄插入、更新、刪除、提交、回退和資料庫模式變化。事務日誌還稱作前滾日誌或重做日誌。事務日誌是備份和恢復的重要元件。

資料庫鎖

是資料庫事務很重要的一個話題,由於篇幅原因,我們這裡不展開講了,下面我會專門提供一篇文章講解。


Wechat-westcall

相關資源

Wechat-westcall

MySQL系列文章:

相關文章