事務隔離級別概念,事例以及分析.

ANSI/ISO SQL92 標準定義了一些數據庫操做的隔離級別:
l   未提交讀( read uncommitted
l   提交讀( read committed
l   重複讀( repeatable read
l   序列化( serializable
      也就是隔離級別,0,1,2,3。 ANSI/ISO SQL92 標準有很詳細的說明,但是這個說明詳細是詳細,就是看不明白。今天經高人指點,茅廁頓開。
      隔離級別0與事務無關,而且不加鎖,也就是說例如select * from t1,系統掃描過和讀取的每一行都不加鎖。
      隔離級別1與事務無關,只對正在取數的行加鎖,取完數立刻開鎖,也就是說,begin tran 而後select * from t1即便沒有commit,鎖也會自動打開。
      隔離級別2與事務有關,對掃描過的地方加鎖。例如,select * from t1,系統從第1行開始掃描,掃描到第5行的時候,1到5行都處於鎖定狀態,直到commit,這些鎖才解開。
      隔離級別3與事務有關,對全表加鎖。

from:  http://marasuzifk.blog.51cto.com/24580/59591程序員

事務的四個屬性:原子性(atomicity)、一致性(consistency)、隔離性(isolation)和持久性(durability)。sql

1.原子性(Atomic)
    最重要的原則,也是最容易理解的原則。被事務管理的全部方法,要麼一塊兒被提交,要麼一塊兒回滾。數據庫

2.一致性(Consistency)
    事務在系統完整性中實施一致性,若是事務成功地完成,那麼系統中全部變化將正確地應用,系統處於新有效狀態。若是在事務中出現錯誤,那麼系統中的全部變化將自動地回滾,系統返回到原始狀態。併發

3.隔離性(Isolation)
    在隔離狀態執行事務,使它們好像是系統在給定時間內執行的惟一操做。若是有兩個事務,運行在相同的時間內,執行相同的功能,事務的隔離性將確保每一事務在系統中認爲只有該事務在使用系統。這種屬性有時稱爲串行化,爲了防止事務操做間的混淆,必須串行化或序列化請求,使得在同一時間僅有一個請求用於同一數據。重要的是,在隔離狀態執行事務,系統的狀態有多是不一致的,在結束事務前,應確保系統處於一致狀態。可是在每一個單獨的事務中,系統的狀態可能會發生變化。若是事務不是在隔離狀態運行,它就可能從系統中訪問數據,而系統可能處於不一致狀態。經過提供事務隔離,能夠阻止這類事件的發生。oracle

4.持久性(Durability)
    持久性意味着一旦事務執行成功,在系統中產生的全部變化將是永久的。應該存在一些檢查點防止在系統失敗時丟失信息。甚至硬件自己失敗,系統的狀態仍能經過在日誌中記錄事務完成的任務進行重建。

數據庫鎖概念:
    在數據庫中有兩種基本的鎖類型:排它鎖(Exclusive Locks,即X鎖)和共享鎖(Share Locks,即S鎖)。當數據對象被加上排它鎖時,其餘的事務不能對它讀取和修改。加了共享鎖的數據對象能夠被其餘事務讀取,但不能修改。數據庫利用這兩種基本的鎖類型來對數據庫的事務進行併發控制。
    從程序員的角度看,鎖分爲如下兩種類型: 
    1.樂觀鎖(Optimistic Lock) 
    樂觀鎖假定在處理數據時,不須要在應用程序的代碼中作任何事情就能夠直接在記錄上加鎖、即徹底依靠數據庫來管理鎖的工做。通常狀況下,當執行事務處理時SQL Server會自動對事務處理範圍內更新到的表作鎖定。 
    2.悲觀鎖(Pessimistic Lock) 
    悲觀鎖對數據庫系統的自動管理不感冒,須要程序員直接管理數據或對象上的加鎖處理,並負責獲取、共享和放棄正在使用的數據上的任何鎖。函數

事務隔離級別
    一個事務必須與其它事務進行隔離的程度。較低的隔離級別能夠增長併發,但代價是下降數據的正確性。相反,較高的隔離級別能夠確保數據的正確性,但可能對併發產生負面影響。

數據庫併發操做存在的異常狀況:
1. 更新丟失(Lost update):兩個事務都同時更新一行數據可是第二個事務卻中途失敗退出致使對數據兩個修改都失效了這是系統沒有執行任何鎖操做所以併發事務並無被隔離開來sqlserver

2. 髒讀取(Dirty Reads):一個事務開始讀取了某行數據可是另一個事務已經更新了此數據但沒有可以及時提交。這是至關危險極可能全部操做都被回滾測試

3. 不可重複讀取(Non-repeatable Reads):一個事務對同一行數據重複讀取兩次可是卻獲得了不一樣結果。例如在兩次讀取中途有另一個事務對該行數據進行了修改並提交atom

4. 兩次更新問題(Second lost updates problem):沒法重複讀取特例,有兩個併發事務同時讀取同一行數據而後其中一個對它進行修改提交而另外一個也進行了修改提交這就會形成第一次寫操做失效spa

5. 幻讀(Phantom Reads):也稱爲幻像(幻影)。事務在操做過程當中進行兩次查詢,第二次查詢結果包含了第一次查詢中未出現的數據(這裏並不要求兩次查詢SQL語句相同)這是由於在兩次查詢過程當中有另一個事務插入數據形成的

爲了不上面出現幾種狀況在標準SQL規範中定義了4個事務隔離級別,不一樣隔離級別對事務處理不一樣 。

1.未受權讀取(Read Uncommitted):也稱未提交讀。容許髒讀取但不容許更新丟失,若是一個事務已經開始寫數據則另一個數據則不容許同時進行寫操做但容許其餘事務讀此行數據。該隔離級別能夠經過「排他寫鎖」實現。事務隔離的最低級別,僅可保證不讀取物理損壞的數據。與READ COMMITTED 隔離級相反,它容許讀取已經被其它用戶修改但還沒有提交肯定的數據。

2. 受權讀取(Read Committed):也稱提交讀。容許不可重複讀取但不容許髒讀取。這能夠經過「瞬間共享讀鎖」和「排他寫鎖」實現,讀取數據的事務容許其餘事務繼續訪問該行數據,可是未提交寫事務將會禁止其餘事務訪問該行。SQL Server 默認的級別。在此隔離級下,SELECT 命令不會返回還沒有提交(Committed) 的數據,也不能返回髒數據。

3. 可重複讀取(Repeatable Read):禁止不可重複讀取和髒讀取。可是有時可能出現幻影數據,這能夠經過「共享讀鎖」和「排他寫鎖」實現,讀取數據事務將會禁止寫事務(但容許讀事務),寫事務則禁止任何其餘事務。在此隔離級下,用SELECT 命令讀取的數據在整個命令執行過程當中不會被更改。此選項會影響系統的效能,非必要狀況最好不用此隔離級。

4. 串行(Serializable):也稱可串行讀。提供嚴格的事務隔離,它要求事務序列化執行,事務只能一個接着一個地執行,但不能併發執行。若是僅僅經過「行級鎖」是沒法實現事務序列化的,必須經過其餘機制保證新插入的數據不會被剛執行查詢操做事務訪問到。事務隔離的最高級別,事務之間徹底隔離。若是事務在可串行讀隔離級別上運行,則能夠保證任何併發重疊事務均是串行的。

隔離級別 更新丟失 髒讀取 重複讀取 幻讀
未受權讀取 N Y Y Y
受權讀取 N N Y Y
可重複讀取 N N N Y
串行 N N N

N

-------------

本示例文檔演示SQL SERVER,ORACLE下不一樣事務隔離級別的區別,以及兩種數據庫自己的特色
  爲了模擬併發環境,SQL SERVER在SMO程序中打開兩個查詢窗口便可。oracle能夠用兩個sql *plus程序鏈接到相同數據庫來模擬
  SQL SERVER、ORACLE中兩個併發用戶用事務1,事務2簡稱。
  全部測試例子,都以最初測試表腳本運行後狀態爲基準。
  在下列例子中,set transaction isolation level語句會改變會話的隔離級別,直到會話結束。故測試完畢須要改回默認級別。
  最後,但並非最不重要。如下的演示和相關解釋,都是基於易於理解的原則來的,實際的狀況可能更復雜,但對開發人員來講,理解如此程度的簡化模型已經足夠了。

 

測試表腳本:
SQL SERVER
CREATE TABLE [Customer](
 [CustID] [int] NOT NULL,
 [Fname] [nvarchar](20),
 [Lname] [nvarchar](20),
 [Address] [nvarchar](50),
 [City] [nvarchar](20),
 [State] [nchar](2) DEFAULT ('CA'),
 [Zip] [nchar](5) NOT NULL,
 [Phone] [nchar](10)
)
insert into customer values(1, 'Gary', 'Mckee', '111 Main', 'Palm Springs', 'CA', 94312, 7605551212)
insert into customer values(2, 'Tom', 'Smith', '609 Geogia', 'Fresno' 'JP', 33045, 5105551212)
insert into customer values(3, 'Jams', 'bond', 'ST Geogie 21', 'Washington', 'NY', 20331, 4405551864)

ORACLE
CREATE TABLE Customer(
 CustID int NOT NULL,
 Fname nvarchar2(20),
 Lname nvarchar2(20),
 Address nvarchar2(50),
 City nvarchar2(20),
 State nchar(2) DEFAULT 'CA',
 Zip nchar(5) NOT NULL,
 Phone nchar(10)
);
insert into customer values(1, 'Gary', 'Mckee', '111 Main', 'Palm Springs', 'CA', 94312, 7605551212);
insert into customer values(2, 'Tom', 'Smith', '609 Geogia', 'Fresno', 'JP', 33045, 5105551212);
insert into customer values(3, 'Jams', 'bond', 'ST Geogie 21', 'Washington', 'NY', 20331, 4405551864);

 

1. Sqlserver與oracle單條語句處理對比
SQL SERVER單條語句默認自動提交,即單條語句自動做爲一個事務處理;而oracle的原則是儘可能延後提交,除非遇到顯式提交命令或者DDL語句。
SQL SERVER
打開事務1:
運行:select * from customer
能夠看到表有3條記錄
運行:insert into customer values(4, 'Hello', 'world', 'paradise road 01', 'heaven', 'XY', 00001, 1234564321)
轉到事務2:
運行:select * from customer
能夠看到事務1中剛插入的custid爲4的記錄。

ORACLE
打開事務1,運行:
select * from customer;
能夠看到表有3條記錄,運行:
insert into customer values(4, 'Hello', 'world', 'paradise road 01', 'heaven', 'XY', 00001, 1234564321);
轉到事務2,運行:
select * from customer;
能看到的仍是3條記錄,事務1中剛插入的一條記錄未自動提交,看不到。
轉到事務1,運行:
commit;
轉到事務2,運行:
select * from customer;
如今能看到4條記錄了。


2. 丟失更新
Sqlserver徹底兼容ANSI 92標準定義的4個隔離級別。它的默認隔離級別是提交讀(read committed),在該級別下,可能會有丟失更新的問題。Oracle的默認情形也同樣。故再也不重複。
SQL SERVER
打開事務1運行:
set transaction isolation level read committed
begin tran
select * from customer  --看到3條記錄
如今切換到事務2,此時事務1還未結束。在事務2中運行:
set transaction isolation level read committed
begin tran
select * from customer  --看到3條記錄,和事務1中相同
如今假設事務1事務繼續運行,修改數據並提交:
update customer set state = 'TK' where CustID = 3
commit
回到事務2,事務2根據先前查詢到的結果修改數據:
update customer set Zip = 99999 where state = 'NY'
commit
結果由於事務1已經修改了事務2的where條件數據,事務2未成功修改數據(其實準確的說應該算是幻象讀引發的更新失敗。不過若知足條件的記錄數多的話,事務2的update可能更新比預期的數量少的記錄數,也可算"丟失"了部分本應完成的更新。我的認爲只要明白實際上發生了什麼便可,沒必要過度追究字眼)。丟失更新還可能有別的情形,好比事務2也是
update customer set state = 'KO' where CustID = 3
兩個事務都結束後,事務2的結果反映到數據庫中,但事務1的更新丟失了,事務2也不知道本身覆蓋了事務1的更新。


3.髒讀演示
sqlserver的默認隔離級別是提交讀(read committed),當手工將其改成未提交讀時,事務能夠讀取其它事務沒提交的數據;oracle因爲自身特殊實現機制,能夠理解爲自身基礎的隔離級別就是可重複讀(與ANSI標準仍是有區別的,後面例子會說明)。
SQL SERVER
打開事務1,運行:
begin tran
select * from customer
 update customer set state = 'TN' where CustID = 3
轉到事務2,運行:
set transaction isolation level read uncommitted
begin tran
select * from customer
此時看到的數據是事務1已經更新但還未提交的(3號記錄state值TN)。而若是事務1發覺數據處理有誤,轉到事務1,進行回滾:
 Rollback
此時事務2如根據剛讀取的數據進一步處理,會形成錯誤。它讀取的數據並未更新到數據庫,是"髒"的。

ORACLE
ANSI定義未提交讀(read uncommitted)級別本意不是爲了故意引入錯誤,而是提供一種可能的最大併發程度級別,即一個事務的數據更新不影響其它事務的讀取。Oracle從內核層面實現了更新數據不阻塞讀。能夠說它提供未提交讀級別的兼容,但沒有髒讀問題。(詳情參考對應PPT文檔)故oracle沒有手工設置read uncommitted級別的語句。


4.不可重複讀
Sql server的默認級別沒有髒讀問題,但存在不可重複讀問題。Oracle默認級別也是提交讀,不過它由於自身特殊機制,在語句一級不存在不可重複讀問題。也就是說當運行時間較長的查詢時,查詢結果是與查詢開始時刻一致的(即便查詢過程當中其它事務修改了要查詢的數據),而SQL SERVER就存在問題(sql server 2005新特性提供了可選的語句一級一致性支持,叫作行版本機制,實際上能夠說是照着oracle的多版原本的,大致原理差很少)。
因爲語句一級的事務一致性難以演示,下面例子是事務一級,提交讀隔離級別下發生的不可重複讀現象:
SQL SERVER
打開事務1,運行:
set transaction isolation level read committed
begin tran
select * from customer where State = 'CA'
能夠獲得1條記錄,這個時候事務2中運行:
set transaction isolation level read committed
begin tran
update Customer set state = 'JP' where state = 'CA'
commit
事務2插入一條記錄並提交。回到事務1,事務1繼續運行,此時它再次相同的查詢,並藉此做進一步修改,卻發現讀取到的數據發生了變化。
select * from customer where State = 'CA'
--2次讀取不一致,以後的數據處理應該取消。不然不正確
update Customer set city = 'garden' where state = 'CA'
commit
讀取未能得到記錄。也就是說在同一事務中兩次相同的查詢得到了不一樣的結果,產生讀取不可重複現象。

ORACLE
儘管oracle在默認隔離級別下提供了語句級的事務讀一致性,但在事務級仍然是會出現不可重複讀現象。和sql server同樣,故再也不重複。


5.幻像讀
當sqlserver的隔離級別設置爲可重複讀(repeatable read),能夠解決上面例子出現的問題。其內部是經過事務期間保持讀鎖來實現的。
SQL SERVER
開始事務1,修改事務級別爲可重複讀,執行:
set transaction isolation level repeatable read
begin tran
select * from customer where State = 'CA'
和上例同樣獲得1條記錄,這個時候事務2中運行:
set transaction isolation level repeatable read
begin tran
update Customer set state = 'JP' where state = 'CA'
commit
會發現事務2一直等待,並不結束。返回事務1,運行:
select * from customer where State = 'CA'  --2次讀取結果一致
update Customer set city = 'garden' where state = 'CA'
commit
事務2成功結束後,再返回事務1,發現事務1也完成了。經過鎖機制阻塞其它事務的修改,保持了事務期間讀取的一致性。然而,若是是插入數據,則仍是會出現問題:
開始事務1,修改事務級別爲可重複讀,執行:
set transaction isolation level repeatable read
begin tran
select * from customer where State = 'CA'
獲得1條記錄,這個時候事務2中運行:
set transaction isolation level repeatable read
begin tran
insert into customer values(4, 'hellow', 'world', 'paradise 001', 'garden', 'CA', 00000, 1119995555)
commit
發現事務2馬上提交併正常結束了。返回事務1,運行:
select * from customer where State = 'CA'
會發現獲得了2條記錄。這種現象就叫作幻像讀。

ORACLE
因爲自身特殊的機制,oracle沒有提供一致讀隔離級別的選項,想要得到一致讀的效果,實際上須要將事務提高到串行化等級,即serializable。


6.串行化級別不一樣數據庫實現
在這個級別,能夠認爲事務中的數據不管什麼時候都是一致的,此級別使它顯得好像沒有其它用戶在修改數據,數據庫在事務開始時候被"凍結"(至少,對於本事務涉及的數據如此)。然而在不一樣數據庫中,其實現機制也不一樣。
SQL SERVER
開始事務1,運行:
set transaction isolation level serializable
begin tran
select * from customer where State = 'CA'
會獲得1條記錄,這時事務2開始運行:
set transaction isolation level serializable
begin tran
insert into customer values(4, 'hellow', 'world', 'paradise 001', 'garden', 'CA', 00000, 1119995555)
commit
會發現事務2掛起,它在等待事務1結束。回到事務1,繼續:
select * from customer where State = 'CA'
update Customer set city = 'garden' where state = 'CA'
commit
在片刻的等待之後,事務1獲得相似以如下格式消息:
消息1205,級別13,狀態56,第1 行
事務(進程ID 51)與另外一個進程被死鎖在鎖資源上,而且已被選做死鎖犧牲品。請從新運行該事務。
而事務2更新了數據並正常結束。這是由於兩個事務都設置成了串行化級別,當遇到衝突時候,sql server根據必定的規則選擇犧牲掉其中一個事務,來保證事務的串行性。上面的例子,若是將事務2的隔離級別改成提交讀,那麼事務2會等待事務1完成,以後本身正常完成(由於事務2沒有串行需求,不會有死鎖)。

ORACLE
在oracle中,經過多版本,能夠在必定程度上避免死鎖。
開始事務1,運行:
set transaction isolation level serializable;
select * from customer where State = 'CA'; --set tran語句隱式開始事務
獲得1條記錄,而後事務2開始運行:
set transaction isolation level serializable;
insert into customer values(4, 'hellow', 'world', 'paradise 001', 'garden', 'CA', 00000, 1119995555);
commit;
能夠發現事務2馬上完成,沒有阻塞。回到事務1繼續:
select * from customer where State = 'CA';
update Customer set city = 'garden' where state = 'CA';
commit;
事務1中的第二次查詢和事務開始時刻一致,就好像事務2已經完成的提交不存在。事務最終正常更新完畢,並保持了"事務開始"時刻的數據一致性。
然而,若是事務1,2修改一樣的數據行,也會有錯誤,
開始事務1,運行:
set transaction isolation level serializable;
select * from customer where State = 'CA'; --set tran語句隱式開始事務
獲得1條記錄,而後事務2開始運行:
set transaction isolation level serializable;
update customer set state = 'KO' where state = 'CA';
commit;
能夠發現事務2馬上完成,沒有阻塞。回到事務1繼續:
select * from customer where State = 'CA';
update Customer set city = 'garden' where state = 'CA';
commit;
出現錯誤信息:
第 1 行出現錯誤:
ORA-08177: 沒法連續訪問此事務處理
總的來講,oracle利用多版本的方式實現串行化級別更少形成死鎖,除非兩個事務修改了相同的數據行,通常也不會形成衝突。

 

7.不一樣隔離級別的相互影響
前面的例子基本都是兩個相同隔離級別事務的狀況。若是不一樣隔離級別的事務發生衝突,會有什麼不一樣嗎?實際上,對於一個事務來講,其它事務的隔離級別對它來講是未知的,更進一步,甚至數據庫中有沒有其它事務,有多少事務也不知道。影響事務訪問數據就兩方面因素:該事務自身的隔離級別,該事務要訪問的數據上面鎖的狀態。
SQL SERVER
開始事務1,運行:
set transaction isolation level serializable
begin tran
select * from customer where State = 'CA'
事務1的查詢得到1條記錄,轉到事務2,運行:
set transaction isolation level read uncommitted
begin tran
select * from customer
事務2查詢得到3條記錄,回到事務1,運行:
update Customer set city = 'garden' where state = 'CA'
切換到事務2,運行:
select * from customer
update customer set state = 'KO' where state = 'CA'
commit;
由於事務2隔離級別爲未提交讀,所以事務1中剛做的修改能夠馬上從查詢看到,即便事務1還未結束。進一步的update由於事務1對記錄加了獨佔鎖,所以事務2掛起。回到事務1將其提交:
Commit
事務1正常結束,獨佔鎖釋放,從而讓事務2得以繼續修改數據,並最終完成。

ORACLE
Oracle數據庫的隔離級別設置語句只有read committed和serializable(read only暫不討論),加上其特殊鎖機制,不一樣隔離級別事務間的影響除了上例(例6)中兩個都爲serializable的狀況,其它均可視爲互不阻塞。

 

8.頁鎖與行鎖(限sql server)
Sql server的鎖能夠到行一級。然而它又存在自動的鎖定擴大,鎖定轉換。所以存在一些意想不到的狀況。下面是演示:
開始事務1,運行:
set transaction isolation level read committed
begin tran
select * from customer where State = 'CA'
update Customer set city = 'garden' where state = 'CA'
理論上來講,在提交讀級別下,上面的update語句只是在state值爲CA的數據行上加了獨佔鎖,表中其它數據應該能夠被其它事務更新,然而,以下事務2開始:
set transaction isolation level read committed
begin tran
select * from customer
update customer set state = 'KO' where state = 'JP'
commit
發現事務2陷入阻塞狀態。儘管它們更新的不是同一條記錄。回到事務1,運行:
Commit
事務1結束後事務2才繼續運行至結束。
若是咱們在表上加入索引,以下:
CREATE NONCLUSTERED INDEX [idx_state] ON [dbo].[Customer] ( [State])
再重複上面的步驟,會發現阻塞再也不存在。
PS:這種現象應該和數據庫默認加鎖參數/機制有關,應該能夠調整,但目前手中沒有進一步資料。故僅羅列了現象。

ORACLE
Oracle在數據一級只有一種數據行上的鎖,所以不存在sql server中這些問題。

 

9.Set transaction語句的做用週期
前面全部的例子,都是在會話窗口中進行的演示。一旦使用了set transaction語句,會影響整個會話。除非再顯式改變隔離級別,不然將保持到會話結束。例如:
開始事務1,假設會話一開始處於SQL SERVER的默認隔離級別(read committed):
begin tran
select * from customer where State = 'CA'
select * from sys.dm_tran_locks
系統視圖sys.dm_tran_locks能夠查看當前的加鎖狀況,到目前位置,只有數據庫級的鎖。繼續運行:
set transaction isolation level repeatable read
select * from customer where State = 'CA'
select * from sys.dm_tran_locks
commit
接下來的語句改變了隔離級別到可重複讀,接下來的查詢,會看到行級鎖的記錄。在上面事務提交後,運行:
begin tran
select * from customer where State = 'CA'
select * from sys.dm_tran_locks
commit
仍然會從視圖sys.dm_tran_locks看到行級鎖記錄。整個會話期間都受到影響。

可是,若是調用存儲過程,函數,則過程/函數中事務隔離級別的改變並不會對調用環境形成影響。能夠經過如下例子說明,首先,建立一個存儲過程腳本:
CREATE PROCEDURE [dbo].[test_tran_level]
AS
BEGIN
 BEGIN TRAN
    SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
 SELECT * FROM CUSTOMER
 UPDATE CUSTOMER SET STATE = 'SS' WHERE CustID = 3
 SELECT * FROM sys.dm_tran_locks
 COMMIT
END
而後,在會話窗口調用該過程,會話窗口當前隔離級別爲默認的提交讀:
Exec test_tran_level
運行的結果能夠看到讀取鎖信息,再在會話中運行:
begin tran
select * from customer where State = 'CA'
select * from sys.dm_tran_locks
commit
視圖sys.dm_tran_locks並未有讀鎖的記錄,說明事務隔離級別仍然是提交讀。

問題

 

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

 

幻讀

 

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

 

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

 

例子:

 

 

事務1 事務2

SELECT * FROM users

WHERE age BETWEEN 10 AND 30;

 
  INSERT INTO users VALUES (3, 'Bob', 27);
SELECT * FROM users 
WHERE age BETWEEN 10 AND 30;
 

 

 

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

 

不可重複讀

在基於鎖的並行控制方法中,若是在執行select時不添加讀鎖,就會發生不可重複讀問題。

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

 

事務1 事務2
SELECT * 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提交或者回退。這種策略在使用鎖時應用。

2. 而在多版本並行控制中,事務2能夠被先提交。而事務1,繼續執行在舊版本的數據上。當事務1終於嘗試提交時,數據庫會檢驗它的結果是否和事務一、事務2順序執行時同樣。若是是,則事務1提交成功。若是不是,事務1會被回退。

 

髒讀

髒讀發生在一個事務A讀取了被另外一個事務B修改,可是還未提交的數據。假如B回退,則事務A讀取的是無效的數據。這跟不可重複讀相似,可是第二個事務不須要執行提交。 

 

事務1 事務2
SELECT * FROM users WHERE id = 1;   
  UPDATE users SET age = 21
WHERE id = 1;
SELECT FROM users WHERE id = 1;  
  COMMIT; /* lock-based DIRTY READ */
相關文章
相關標籤/搜索