天天看點

資料庫弱一緻性四個隔離級别

SQL-92标準中定義了四個隔離級别,這四個隔離級别在以前版本的SQL Server中即受到支援:

READ UNCOMMITTED是限制性最弱的隔離級别,因為該級别忽略其他事務放置的鎖。使用READ UNCOMMITTED級别執行的事務,可以讀取尚未由其他事務送出的修改後的資料值,這些行為稱為“髒”讀。這是因為在Read Uncommitted級别下,讀取資料不需要加S鎖,這樣就不會跟被修改的資料上的X鎖沖突。比如,事務1修改一行,事務2在事務1送出之前讀取了這一行。如果事務1復原,事務2就讀取了一行沒有送出的資料,這樣的資料我們認為是不存在的。

READ COMMITTED(Nonrepeatable reads)是SQL Server預設的隔離級别。該級别通過指定語句不能讀取其他事務已修改但是尚未送出的資料值,禁止執行髒讀。在目前事務中的各個語句執行之間,其他事務仍可以修改、插入或删除資料,進而産生無法重複的讀操作,或“影子”資料。比如,事務1讀取了一行,事務2修改或者删除這一行并且送出。如果事務1想再一次讀取這一行,它将獲得修改後的資料或者發現這一樣已經被删除,是以事務的第二次讀取結果與第一次讀取結果不同,是以也叫不可重複讀。

query1:事務1

<a></a>

檢視鎖的情況如下圖所示,我們發現在隻有在資料庫級别的S鎖,而沒有在表級别或者更低級别的鎖,這是因為在Read Committed級别下,S鎖在語句執行完以後就被釋放。

query2:事務2

在開啟另外一個update事務以後,我們再去檢視目前的鎖狀況,如下圖所示,我們發現在表(Object)級别上加了IX鎖,在這張表所在的Page上也加了IX鎖,因為表加了聚集索引,是以在葉子結點上加了X鎖,這個鎖的類型是KEY。

然後我們回到事務1當中再次執行查詢語句,我們會發現查詢被阻塞,我們建立一個查詢query3來檢視這個時候的鎖狀況,其查詢結果如下,我們可以發現查詢操作需要在KEY級别上申請S鎖,在Page和表(Object)上面申請IS鎖,但是因為Key上面原先有了X鎖,與目前讀操作申請的S鎖沖突,是以這一步處于WAIT狀态。

如果此時送出事務2的update操作,那麼事務1的select操作不再被阻塞,得到查詢結果,但是我們發現此時得到的查詢結果與第一次得到的查詢結果不同,這也是為什麼将read committed稱為不可重複讀,因為同一個事物内的兩次相同的查詢操作的結果可能不同。

REPEATABLE READ是比READ COMMITTED限制性更強的隔離級别。該級别包括READ COMMITTED,并且另外指定了在目前事務送出之前,其他任何事務均不可以修改或删除目前事務已讀取的資料。并發性低于 READ COMMITTED,因為已讀資料的共享鎖在整個事務期間持有,而不是在每個語句結束時釋放。比如,事務1讀取了一行,事務2想修改或者删除這一行并且送出,但是因為事務1尚未送出,資料行中有事務1的鎖,事務2無法進行更新操作,是以事務2阻塞。如果這時候事務1想再一次讀取這一行,它讀取結果與第一次讀取結果相同,是以叫可重複讀。

查詢鎖狀态的結果如下圖所示,我們發現在KEY上面加了S鎖,在Page和Object上面加了IS鎖,這是因為在Repeatable Read級别下S鎖要在事務執行完以後才會被釋放。

 query2:事務2

執行上述update操作的時候發現該操作被阻塞,這是因為update操作要加排它鎖X,而因為原先的查詢操作的S鎖沒有釋放,是以兩者沖突。我們建立一個查詢3執行查詢鎖狀态操作,發現結果如下圖所示,我們可以發現是WAIT發生在對KEY加X鎖的操作上面。

此時再次執行查詢1中的select操作,我們發現查詢結果跟第一次相同,是以這個叫做可重複讀操作。但是可重複讀操作并不是特定指兩次讀取的資料一模一樣,Repeatable Read存在的一個問題是幻讀,就是第二次讀取的資料傳回的條目數比第一次傳回的條目數更多。

比如在Repeatable Read隔離級别下,事務1第一次執行查詢select id from users where id&gt;1 and id &lt;10,傳回的結果是2,4,6,8。這個時候事務1沒有送出,那麼對2,4,6,8上面依然保持有S鎖。此時事務2執行一次插入操作insert into user(id) valuse(3),插入成功。此時再次執行事務1中的查詢,那麼傳回結果就是2,3,4,6,8。這裡的3就是因為幻讀而出現的。是以可以得出結論:REPEATABLE READ隔離級别保證了在相同的查詢條件下,同一個事務中的兩個查詢,第二次讀取的内容肯定包換第一次讀到的内容。

SERIALIZABLE 是限制性最強的隔離級别,因為該級别鎖定整個範圍的鍵,并一直持有鎖,直到事務完成。該級别包括REPEATABLE READ,并增加了在事務完成之前,其他事務不能向事務已讀取的範圍插入新行的限制。比如,事務1讀取了一系列滿足搜尋條件的行。事務2在執行SQL statement産生一行或者多行滿足事務1搜尋條件的行時會沖突,則事務2復原。這時事務1再次讀取了一系列滿足相同搜尋條件的行,第二次讀取的結果和第一次讀取的結果相同。

重複讀是為了保證在一個事務中,相同查詢條件下讀取的資料值不發生改變,但是不能保證下次同樣條件查詢,結果記錄數不會增加。

幻讀就是為了解決這個問題而存在的,他将這個查詢範圍都加鎖了,是以就不能再往這個範圍内插入資料,這就是SERIALIZABLE 隔離級别做的事情。

在Read Uncommitted級别下,讀操作不加S鎖;

在Read Committed級别下,讀操作需要加S鎖,但是在語句執行完以後釋放S鎖;

在Repeatable Read級别下,讀操作需要加S鎖,但是在事務送出之前并不釋放S鎖,也就是必須等待事務執行完畢以後才釋放S鎖。

在Serialize級别下,會在Repeatable Read級别的基礎上,添加一個範圍鎖。保證一個事務内的兩次查詢結果完全一樣,而不會出現第一次查詢結果是第二次查詢結果的子集。

本文轉自xwdreamer部落格園部落格,原文連結:http://www.cnblogs.com/xwdreamer/archive/2011/01/18/2297042.html,如需轉載請自行聯系原作者