資料庫事務隔離級別

2021-07-11 00:50:07 字數 3995 閱讀 8489

併發問題可歸納為以下幾類:

a.丟失更新:撤銷乙個事務時,把其他事務已提交的更新資料覆蓋(a和

b事務併發執行,

a事務執行更新後,提交;

b事務在

a事務更新後,

b事務結束前也做了對該行資料的更新操作,然後回滾,則兩次更新操作都丟失了)。

b.髒讀:乙個事務讀到另乙個事務未提交的更新資料(a和

b事務併發執行,

b事務執行更新後,

a事務查詢

b事務沒有提交的資料,

b事務回滾,則

a事務得到的資料不是資料庫中的真實資料。也就是髒資料,即和資料庫中不一致的資料)。

c.不可重複讀:乙個事務讀到另乙個事務已提交的更新資料(a和

b事務併發執行,

a事務查詢資料,然後

b事務更新該資料,

a再次查詢該資料時,發現該資料變化了)。

d. 覆蓋更新:這是不可重複讀中的特例,乙個事務覆蓋另乙個事務已提交的更新資料(即

a事務更新資料,然後

b事務更新該資料,

a事務查詢發現自己更新的資料變了)。

e.虛讀(幻讀):乙個事務讀到另乙個事務已提交的新插入的資料(a和

b事務併發執行,

a事務查詢資料,

b事務插入或者刪除資料,

a事務再次查詢發現結果集中有以前沒有的資料或者以前有的資料消失了)。

資料庫系統提供了四種事務隔離級別供使用者選擇:

a.serializable

(序列化):乙個事務在執行過程中完全看不到其他事務對資料庫所做的更新(事務執行的時候不允許別的事務併發執行。事務序列化執行,事務只能乙個接著乙個地執行,而不能併發執行。)。

b.repeatable read

(可重複讀):乙個事務在執行過程中可以看到其他事務已經提交的新插入的記錄,但是不能看到其他其他事務對已有記錄的更新。

c.read commited

(讀已提交資料):乙個事務在執行過程中可以看到其他事務已經提交的新插入的記錄,而且能看到其他事務已經提交的對已有記錄的更新。

d.read uncommitted

(讀未提交資料):乙個事務在執行過程中可以看到其他事務沒有提交的新插入的記錄,而且能看到其他事務沒有提交的對已有記錄的更新。

丟失更新 髒讀

非重複讀

覆蓋更新

幻像讀未提交讀 y

y yy y

已提交讀 n

n yy y

可重複讀 n

n nn y

序列化 n

n nn n

隔離級別

資料庫系統有四個隔離級別(大多數資料庫預設級別為read commited)。對資料庫使用何種隔離級別要審慎分析,因為

1. 維護乙個最高的隔離級別雖然會防止資料的出錯,但是卻導致了並行度的損失,以及導致死鎖出現的可能性增加。

2. 然而,降低隔離級別,卻會引起一些難以發現的bug。

serializable(序列化)

新增範圍鎖(比如表鎖,頁鎖等,關於range lock,我也沒有很深入的研究),直到transaction a結束。以此阻止其它transaction b對此範圍內的insert,update等操作。

幻讀,髒讀,不可重複讀等問題都不會發生。

repeatable read(可重複讀)

對於讀出的記錄,新增共享鎖直到transaction a結束。其它transaction b對這個記錄的試圖修改會一直等待直到transaction a結束。

可能發生的問題:當執行乙個範圍查詢時,可能會發生幻讀。

read committed(提交讀)

在transaction a中讀取資料時對記錄新增共享鎖,但讀取結束立即釋放。其它transaction b對這個記錄的試圖修改會一直等待直到a中的讀取過程結束,而不需要整個transaction a的結束。所以,在transaction a的不同階段對同一記錄的讀取結果可能是不同的。

可能發生的問題:不可重複讀。

read uncommitted(未提交讀)

不新增共享鎖。所以其它transaction b可以在transaction a對記錄的讀取過程中修改同一記錄,可能會導致a讀取的資料是乙個被破壞的或者說不完整不正確的資料。

另外,在transaction a中可以讀取到transaction b(未提交)中修改的資料。比如transaction b對r記錄修改了,但未提交。此時,在transaction a中讀取r記錄,讀出的是被b修改過的資料。

可能發生的問題:髒讀。

問題

我們看到,當執行不同的隔離級別時,可能會發生各種各樣不同的問題。下面對它們進行總結並舉例說明。

幻讀

幻讀發生在當兩個完全相同的查詢執行時,第二次查詢所返回的結果集跟第乙個查詢不相同。

發生的情況:沒有範圍鎖。

例子:

事務1事務2select * 

from users

where age 

between 10 and 30

insert into users 

values(3, 'bob', 27);

commit;

select* 

from users

where age 

between 10 and 30;

如何避免:實行序列化隔離模式,在任何乙個低階別的隔離中都可能會發生。

不可重複讀

在基於鎖的並行控制方法中,如果在執行select時不新增讀鎖,就會發生不可重複讀問題。

在多版本並行控制機制中,當乙個遇到提交衝突的事務需要回退但卻被釋放時,會發生不可重複讀問題。

事務1事務2select* 

from users 

where  id = 1;

update users 

set age = 21

where id = 1;

commit; /* in multiversion concurrency*/ control, or lock-based read committed *

select* 

from users 

where id = 1;

commit; /* lock-based repeatable read */

在上面這個例子中,事務2提交成功,它所做的修改已經可見。然而,事務1已經讀取了乙個其它的值。在序列化和可重複讀的隔離級別中,資料庫管理系統會返回舊值,即在被事務2修改之前的值。在提交讀和未提交讀隔離級別下,可能會返回被更新的值,這就是「不可重複讀」。

有兩個策略可以防止這個問題的發生:

1. 推遲事務2的執行,直至事務1提交或者回退。這種策略在使用鎖時應用。(悲觀鎖機制,比如用select for update為資料行加上乙個排他鎖)

2. 而在多版本並行控制中,事務2可以被先提交。而事務1,繼續執行在舊版本的資料上。當事務1終於嘗試提交時,資料庫會檢驗它的結果是否和事務1、事務2順序執行時一樣。如果是,則事務1提交成功。如果不是,事務1會被回退。(樂觀鎖機制)

髒讀

髒讀發生在乙個事務a讀取了被另乙個事務b修改,但是還未提交的資料。假如b回退,則事務a讀取的是無效的資料。這跟不可重複讀類似,但是第二個事務不需要執行提交。 

事務1事務2select* 

from users

where id = 1;

update users 

set age = 21

where id = 1

select

from users 

where id = 1;

commit; /* lock-based dirty read */

資料庫事務隔離級別

資料庫事務的隔離級別有4個,由低到高依次為read uncommitted read committed repeatable read serializable,這四個級別可以逐個解決髒讀 不可重複讀 幻讀這幾類問題。可能出現 不會出現 髒讀不可重複讀 幻讀read uncommitted rea...

資料庫事務隔離級別

資料庫事務的隔離級別有4個,由低到高依次為read uncommitted read committed repeatable read serializable,這四個級別可以逐個解決髒讀 不可重複讀 幻讀這幾類問題。可能出現 不會出現 髒讀不可重複讀 幻讀read uncommitted rea...

資料庫事務隔離級別

資料庫事務的隔離級別有4個,由低到高依次為read uncommitted read committed repeatable read serializable 這四個級別可以逐個解決髒讀 不可重複讀 幻讀 這幾類問題。可能出現 不會出現 髒讀不可重複讀 幻讀read uncommitted re...