引言:在網上搜了很多關於事務的文章,感覺單獨來看都很難看懂,所以綜合自己的理解寫一篇我自己能理解的關於關係型資料庫事務的文章。

一、事務特徵

我們都知道資料庫事務具備ACID特性:

Atomic(原子性):一個事務要麼成功,要麼失敗

Consistency(一致性):一致性代表了底層資料儲存的完整性。事務執行前後資料庫都必須處於一個合法的狀態。什麼才是一個合法的狀態?

比如滿足資料庫的唯一性約束、資料型別驗證、引用完整性等,更復雜的還包括事務執行後結果不會和現實業務期望的結果不同。

舉個例子:比如由兩張表學生表和專業表,因為學生要有一個所屬的專業,所以學生表需要引用專業表,但是如果新增一個專業和一個學生,而這個學生恰好屬於這個新專業,那麼這兩個插入操作必須放在一個事務中如果有一個失敗都要回滾,不然就會導致學生可能引用一個不存在的專業,出現事務執行後資料庫引用不完整。

另外比如銀行系統有多個賬戶,錢只會在賬戶之間相互轉移,而不會憑空消失,如果要將賬戶A的錢轉移到賬戶B,那麼賬戶A的扣錢操作比如和賬戶B的加錢操作要麼一起成功,要麼一起失敗,保證事務執行前後業務資料都處於一致的狀態。

Isolation(隔離性):隔離性意味著事務必須在不干擾其他程序或事務的前提下獨立執行

Durability(永續性):事務結束後,事務處理的結果必須能夠得到固化。

二、問題

如果沒有任何併發控制機制,不同的執行緒執行事務時會出現幾類問題:

1.更新丟失

(1)更新丟失(Lostupdate)

兩個事務同時更新,第二個事務回滾會覆蓋第一個事務更新的資料,導致更新丟失

(2)兩次更新問題(Secondlost updates problem)

兩個事務都讀取了資料,並同時更新,第一個事務更新失敗,因為被第二個事務覆蓋。

2.髒讀(Dirty Reads)

事務T1讀取到事務T2修改了但是還未提交的資料,之後事務T2又回滾其更新操作,導致事務T1讀到的是髒資料。

3.不可重複讀(Non-repeatableReads)

事務T1讀取某一行資料後,事務T2對其做了修改,當事務T1再次讀該資料時得到與前一次不同的值。

4.幻讀(Phantom Reads)

事務T1讀取在讀取範圍資料時,事務T2又插入一條資料,當事務T1再次資料這個範圍資料時發現不一樣了,出現了一些“幻影行”。

三、併發控制

為此我們需要通過提供不同型別的“鎖”機制針對資料庫事務進行不同程度的併發訪問控制,由此產生了不同的事務隔離級別:隔離級別(低->高)

●讀未提交(Read Uncommitted)

含義解釋:只限制同一資料寫事務禁止其他寫事務。解決更新丟失

名稱解釋:可讀取未提交資料

所需的鎖:排他寫鎖

●讀提交(Read Committed)

含義解釋:只限制同一資料寫事務禁止其它讀寫事務。解決髒讀,以及更新丟失

名稱解釋:必須提交以後的資料才能被讀取

所需的鎖:排他寫鎖、瞬間共享讀鎖

●可重複讀(Repeatable Read)

含義解釋:限制同一資料寫事務禁止其他讀寫事務,讀事務禁止其它寫事務(允許讀)。解決不可重複讀,以及更新丟失髒讀

注意沒有解決幻讀,解決幻讀的方法是增加範圍鎖(range lock)或者表鎖。

名稱解釋:能夠重複讀取

所需的鎖:排他寫鎖、共享讀鎖

●序列化(Serializable)

提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接著一個地執行,但不能併發執行。如果僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的資料不會被剛執行查詢操作的事務訪問到。

限制所有讀寫事務都必須序列化實行。

下表是各隔離級別對各種異常的控制能力。

更新丟失

髒讀

不可重複讀

幻讀

RU(讀未提交)

避免

RC(讀提交)

避免

避免

RR(可重複讀)

避免

避免

避免

S(序列化)

避免

避免

避免

避免

四、常用資料庫預設隔離級別

各類資料庫預設事務隔離級別

資料庫

預設級別

MySQL

可重複讀(Repeatable Read

Oracle

讀提交(Read Committed)

SQLServer

讀提交(Read Committed)

DB2

讀提交(Read Committed)

PostgreSQL

讀提交(Read Committed)