事務(wù)的隔離級別
SQL Server通過在鎖資源上使用不同類型的鎖來隔離事務(wù)。為了開發(fā)安全的事務(wù),定義事務(wù)內(nèi)容以及應(yīng)在何種情況下回滾至關(guān)重要,定義如何以及在多長時間內(nèi)在事務(wù)中保持鎖定也同等重要。這由隔離級別決定。應(yīng)用不同的隔離級別,SQL Server賦予開發(fā)者一種能力,讓他們?yōu)槊恳粋€單獨事務(wù)定義與其他事務(wù)的隔離程度。事務(wù)隔離級別的定義如下:
- 是否在讀數(shù)據(jù)的時候使用鎖
- 讀鎖持續(xù)多長時間
- 在讀數(shù)據(jù)的時候使用何種類型的鎖
- 讀操作希望讀已經(jīng)被其他事務(wù)排他鎖住的數(shù)據(jù)時,怎么辦?在這種情況下,SQL Server可以:
- 一直等到其他事務(wù)釋放鎖
- 讀沒有提交的數(shù)據(jù)
- 讀數(shù)據(jù)最后提交后的版本
ANSI 99定義了4種事務(wù)隔離級別,SQL Server 2005能夠完全支持這些級別:
- 未提交讀 在讀數(shù)據(jù)時不會檢查或使用任何鎖。因此,在這種隔離級別中可能讀取到?jīng)]有提交的數(shù)據(jù)。
- 已提交讀 只讀取提交的數(shù)據(jù)并等待其他事務(wù)釋放排他鎖。讀數(shù)據(jù)的共享鎖在讀操作完成后立即釋放。已提交讀是SQL Server的默認隔離級別。
- 可重復(fù)讀 像已提交讀級別那樣讀數(shù)據(jù),但會保持共享鎖直到事務(wù)結(jié)束。
- 可序列化 工作方式類似于可重復(fù)讀。但它不僅會鎖定受影響的數(shù)據(jù),還會鎖定這個范圍。這就阻止了新數(shù)據(jù)插入查詢所涉及的范圍,這種情況可以導(dǎo)致幻像讀。
此外,SQL Server還有兩種使用行版本控制來讀取數(shù)據(jù)的事務(wù)級別(本章后文將詳細檢驗這些隔離級別)。行版本控制允許一個事務(wù)在數(shù)據(jù)排他鎖定后讀取數(shù)據(jù)的最后提交版本。由于不必等待到鎖釋放就可進行讀操作,因此查詢性能得以大大增強。這兩種隔離級別如下:
- 已提交讀快照 它是一種提交讀級別的新實現(xiàn)。不像一般的提交讀級別,SQL Server會讀取最后提交的版本并因此不必在進行讀操作時等待直到鎖被釋放。這個級別可以替代提交讀級別。
- 快照 這種隔離使用行版本來提供事務(wù)級別的讀取一致性。這意味著在一個事務(wù)中,由于讀一致性可以通過行版本控制實現(xiàn),因此同樣的數(shù)據(jù)總是可以像在可序列化級別上一樣被讀取而不必為防止來自其他事務(wù)的更改而被鎖定。
無論定義什么隔離級別,對數(shù)據(jù)的更改總是通過排他鎖來鎖定并直到事務(wù)結(jié)束時才釋放。
很多情況下,定義正確的隔離級別并不是一個簡單的決定。作為一種通用的規(guī)則,要選擇在盡可能短的時間內(nèi)鎖住最少數(shù)據(jù),但同時依然可以為事務(wù)提供它所需的安全程度的隔離級別。
已提交讀
在SQL Server 2005中,已提交讀隔離級別是建立連接時的默認隔離級別。這個級別存在兩種類型:已提交讀和已提交讀快照隔離級別。應(yīng)用哪種類型由數(shù)據(jù)庫選項定義。已提交讀級別會在讀數(shù)據(jù)之前等待,直到阻塞鎖被釋放。已提交讀快照級別會在數(shù)據(jù)被其他事務(wù)阻塞時使用行版本控制來讀數(shù)據(jù)最后一次提交的版本。
使用已提交讀級別:
BEGIN TRAN
SELECT
FirstName, LastName, EmailAddress
FROM
Person.Contact
WHERE
ContactID = 1
現(xiàn)在假設(shè)另一事務(wù)在事務(wù)打開狀態(tài)下更改了EmailAddress。打開第二個查詢窗口并執(zhí)行以下批來UPDATE EmailAddress,但不提交事務(wù):
USE AdventureWorks;
BEGIN TRAN
UPDATE
Person.Contact
SET
WHERE
ContactID = 1
這個UPDATE 語句會正常運行。一行受到了影響,即使數(shù)據(jù)在這個事務(wù)還沒有運行完之前已被查詢窗口1中的事務(wù)讀取。因為已提交讀級別并不會在事務(wù)結(jié)束前保持用于SELECT語句的共享鎖。共享鎖會在數(shù)據(jù)讀取之后立即被SQL Server釋放。需要一致讀的時候這將是一個問題。我們將下面的"獲取一致的可重復(fù)讀操作"實現(xiàn)。
現(xiàn)在切換到查詢窗口1并嘗試再次讀數(shù)據(jù):
SELECT
FirstName, LastName, EmailAddress
FROM
Person.Contact
WHERE
ContactID = 1
由于SELECT語句被阻塞,因此這個查詢并沒有結(jié)束。SQL Server會嘗試在ContactID= 1的鍵上獲取一個共享鎖,但是由于在查詢窗口2中的UPDATE語句對其有一個排他鎖,因此這個操作不可能完成。雖然查詢窗口2處于已提交讀級別(由于您沒有更改默認級別),但排他鎖依然存在。這個阻塞將持續(xù)存在,因為數(shù)據(jù)更改的排他鎖會一直保持直到事務(wù)結(jié)束。
切換到查詢窗口2,讓查詢窗口1中的查詢繼續(xù)運行。鍵入并執(zhí)行以下SELECT語句檢查數(shù)據(jù)庫中的授權(quán)和等待的鎖。
可以看一個狀態(tài)為WAIT的共享鎖。這是查詢窗口1中運行的查詢。它在等待查詢窗口2中的查詢,后者在同樣的資源上有一個排他鎖。
在查詢窗口2中執(zhí)行一個ROLLBACK TRAN語句來回滾UPDATE語句。然后切換回查詢窗口1??梢钥吹?,查詢窗口1中的查詢完成了,并且其結(jié)果與以前的一樣。查詢窗口2中的事務(wù)結(jié)束的時候,鎖被釋放了,以至查詢窗口1中的查詢不再被阻塞。由于查詢窗口2中的事務(wù)回滾,因此查詢窗口1中得到的結(jié)果是原來的數(shù)據(jù)。如果查詢窗口2中的事務(wù)被提交,則查詢窗口1中會得到新的數(shù)據(jù)作為結(jié)果。
在查詢窗口1中執(zhí)行一個COMMIT TRAN語句并關(guān)閉所有的查詢窗口。
可以看出,在(默認)已提交讀級別中SQL Server會等到排他鎖釋放之后再進行讀操作,以此來獲取真正的提交數(shù)據(jù)。還可以看出,共享鎖會持續(xù)到數(shù)據(jù)被讀取之后,而排他鎖會持續(xù)到事務(wù)提交之后。在許多事務(wù)幾乎同時更改數(shù)據(jù)的時候這種行為可能會造成問題。在這些情況下,由于排他鎖造成的阻塞,讀數(shù)據(jù)會非常慢。但在有些情況下,使用最后提交的數(shù)據(jù)版本是恰當?shù)?。在這些情況下,可以將已提交讀級別更改為已提交讀快照級別。
如果要在窗口1讀取數(shù)據(jù)的話,可以使用這樣的方法:
SELECT
FirstName, LastName, EmailAddress
FROM
Person.Contact WITH (NOLOCK)
WHERE
ContactID = 1
讓它取消所有的鎖機制,那么排他鎖也不會影響到這句查詢。
使用NOLOCK注意:在 SQL Server 中,NOLOCK 提示將啟用"未提交讀"行為。在 SQL Server Mobile 中,使用 NOLOCK 提示仍會賦予"提交讀"隔離級別。SQL Server Mobile 將維護數(shù)據(jù)副本,以確保可以讀取數(shù)據(jù)而不需要使用共享鎖幫助保護數(shù)據(jù)。
使用已提交讀快照級別
激活已提交讀快照級別
USE master;
ALTER DATABASE AdventureWorks
SET READ_COMMITTED_SNAPSHOT ON
注意:設(shè)置 READ_COMMITTED_SNAPSHOT 選項時,數(shù)據(jù)庫中僅允許存在執(zhí)行 ALTER DATABASE 命令的連接。在 ALTER DATABASE 完成之前,數(shù)據(jù)庫中不允許有其他打開的連接。數(shù)據(jù)庫不必處于單用戶模式。
現(xiàn)在,執(zhí)行以下代碼開始一個事務(wù)并像前面一樣更改EmailAddress(但要讓事務(wù)處于打開狀態(tài)):
USE AdventureWorks;
BEGIN TRAN
UPDATE Person.Contact
WHERE ContactID = 1;
打開第二個查詢窗口并執(zhí)行以下語句來讀取ContactID 1的列Name和EmailAddress列。
USE AdventureWorks;
BEGIN TRAN
SELECT FirstName, LastName, EmailAddress
FROM Person.Contact
WHERE ContactID = 1;
執(zhí)行以下語句來回滾事務(wù)并切換回已提交讀級別(這個查詢將等待直到關(guān)閉查詢窗口2):
ROLLBACK TRAN
GO
USE master;
ALTER DATABASE AdventureWorks
SET READ_COMMITTED_SNAPSHOT OFF
重要提示 這個隔離級別可以用于減少阻塞。但要意識到這是一個數(shù)據(jù)庫選項。當它發(fā)生了更改,將在數(shù)據(jù)庫系統(tǒng)中使用已提交讀級別的所有事務(wù)也會改變它們的行為。因此,只有在所有這些事務(wù)讀最后提交的數(shù)據(jù)版本與讀真正提交的數(shù)據(jù)版本在邏輯上同樣正確的時候,使用這種級別才是明智的。
獲取一致的可重復(fù)讀操作
已提交讀級別的一個缺點是,一個事務(wù)讀取的數(shù)據(jù)在事務(wù)運行期間可能被另一個事務(wù)更改。因此,在兩種已提交讀級別下,不能保證一致性讀。獲取一致性讀的意思是,在一個事務(wù)中,讀取的數(shù)據(jù)始終是一樣的。
1. 已提交讀在讀數(shù)據(jù)的時候使用共享鎖,但在讀操作完成后會立即釋放這個鎖。因此,其他事務(wù)可以更改剛被讀過的數(shù)據(jù)。
2. 已提交讀快照讀取最后一次提交的數(shù)據(jù)版本。當它第二次讀數(shù)據(jù)的時候,最后一次提交的版本可能由于第二個事務(wù)已經(jīng)提交了對數(shù)據(jù)的更改而變成一個新版本。
在需要一致性讀的時候(例如對于報表),可能這種不一致性會導(dǎo)致問題。想象一下,您的事務(wù)通過數(shù)據(jù)計算了一些商業(yè)數(shù)值。在已提交讀級別中進行這種計算的時候,可能由于基礎(chǔ)數(shù)據(jù)在事務(wù)計算過程中發(fā)生了變化而導(dǎo)致這些值被錯誤計算。為了成功地執(zhí)行這個計算,可以使用快照隔離級別。它會使用行版本管理來提供數(shù)據(jù)的提交版本,但與已提交讀快照不同的是,它總會提供在開始事務(wù)時最后提交的數(shù)據(jù)版本。因此,SQL Server始終會在整個事務(wù)執(zhí)行過程中獲取同樣的數(shù)據(jù)。
使用快照隔離級別
快照隔離級別需要在數(shù)據(jù)庫中一次性地激活。激活之后,每個連接可以在需要的時候使用它。
USE master;
ALTER DATABASE AdventureWorks
SET ALLOW_SNAPSHOT_ISOLATION ON;
現(xiàn)在假設(shè)我們希望運行一些基于Sales.SalesOrderDetail表的報表,但需要一致性的讀操作。執(zhí)行以下語句為事務(wù)激活快照隔離級別并開始一個返回訂單行合計的事務(wù)。記住OrderTotal的值。
USE AdventureWorks;
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
BEGIN TRAN
SELECT SUM(LineTotal) as OrderTotal
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = 43659
參數(shù)SNAPSHOT的含義:
1. 指定事務(wù)中任何語句讀取的數(shù)據(jù)都將是在事務(wù)開始時便存在的數(shù)據(jù)的事務(wù)上一致的版本。事務(wù)只能識別在其開始之前提交的數(shù)據(jù)修改。在當前事務(wù)中執(zhí)行的語句將看不到在當前事務(wù)開始以后由其他事務(wù)所做的數(shù)據(jù)修改。其效果就好像事務(wù)中的語句獲得了已提交數(shù)據(jù)的快照,因為該數(shù)據(jù)在事務(wù)開始時就存在。
2. 除非正在恢復(fù)數(shù)據(jù)庫,否則 SNAPSHOT 事務(wù)不會在讀取數(shù)據(jù)時請求鎖。讀取數(shù)據(jù)的 SNAPSHOT 事務(wù)不會阻止其他事務(wù)寫入數(shù)據(jù)。寫入數(shù)據(jù)的事務(wù)也不會阻止 SNAPSHOT 事務(wù)讀取數(shù)據(jù)。
3. 在數(shù)據(jù)庫恢復(fù)的回滾階段,如果嘗試讀取由其他正在回滾的事務(wù)鎖定的數(shù)據(jù),則 SNAPSHOT 事務(wù)將請求一個鎖。在事務(wù)完成回滾之前,SNAPSHOT 事務(wù)會一直被阻塞。當事務(wù)取得授權(quán)之后,便會立即釋放鎖。
4. 必須將 ALLOW_SNAPSHOT_ISOLATION 數(shù)據(jù)庫選項設(shè)置為 ON,才能開始一個使用 SNAPSHOT 隔離級別的事務(wù)。如果使用 SNAPSHOT 隔離級別的事務(wù)訪問多個數(shù)據(jù)庫中的數(shù)據(jù),則必須在每個數(shù)據(jù)庫中將 ALLOW_SNAPSHOT_ISOLATION 都設(shè)置為 ON。
5. 不能將通過其他隔離級別開始的事務(wù)設(shè)置為 SNAPSHOT 隔離級別,否則將導(dǎo)致事務(wù)中止。如果一個事務(wù)在 SNAPSHOT 隔離級別開始,則可以將它更改為另一個隔離級別,然后再返回 SNAPSHOT。一個事務(wù)從執(zhí)行 BEGIN TRANSACTION 語句開始。
6. 在 SNAPSHOT 隔離級別下運行的事務(wù)可以查看由該事務(wù)所做的更改。例如,如果事務(wù)對表執(zhí)行 UPDATE,然后對同一個表發(fā)出 SELECT 語句,則修改后的數(shù)據(jù)將包含在結(jié)果集中。
打開第二個查詢窗口并更新SalesOrderDetail表以更改查詢窗口1中用到的基礎(chǔ)數(shù)據(jù)。(如果希望重復(fù)這個示例,將OrderQty的值5更改為其他數(shù)字以使以下代碼能真正地更改數(shù)據(jù)庫中的數(shù)據(jù)):
USE AdventureWorks;
UPDATE Sales.SalesOrderDetail
SET OrderQty = 5
WHERE SalesOrderID = 43659
AND ProductID = 777
關(guān)閉查詢窗口2,切換到查詢窗口1,然后重復(fù)下面的SELECT語句。
SELECT SUM(LineTotal) as OrderTotal
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = 43659
可以看出,由于快照隔離級別忽略了事務(wù)運行過程中數(shù)據(jù)的更改,因此結(jié)果與以前的相同。在快照級別下總會提供在事務(wù)開始時最后提交的值。
提交這個事務(wù)并執(zhí)行以下代碼再次重復(fù)這個查詢:現(xiàn)在可看到,由于事務(wù)結(jié)束了,因此結(jié)果發(fā)生了變化。
COMMIT TRAN
SELECT SUM(LineTotal) as OrderTotal
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = 43659
執(zhí)行以下代碼關(guān)閉AdventureWorks數(shù)據(jù)庫的快照隔離級別:
ALTER DATABASE AdventureWorks
SET ALLOW_SNAPSHOT_ISOLATION OFF;
避免同時發(fā)生的數(shù)據(jù)更新
如前所述,快照隔離級別并不在讀操作的時候鎖定數(shù)據(jù),但能夠在整個事務(wù)中提供一致性的視圖。在某些情況下,有必要在整個事務(wù)的執(zhí)行過程中鎖定數(shù)據(jù)以避免其他事務(wù)對數(shù)據(jù)的更改。假設(shè)希望為一個訂單開發(fā)票。首先需要獲取數(shù)據(jù)并檢查它,然后為其生成發(fā)票。在這種情況下,需要從事務(wù)起始就鎖定數(shù)據(jù)以避免其他事務(wù)更改它。在這種情況下,快照隔離或者已提交讀隔離級別都不是好的選擇。對于這種情況,可以使用可重復(fù)讀隔離級別。這個隔離級別與沒有快照的已提交讀級別的工作過程相似,但它會保持共享鎖直至事務(wù)結(jié)束。因此,它防止了對數(shù)據(jù)的更新。
使用可重復(fù)讀隔離級別
假設(shè)希望處理OrderID為43659的訂單。首先,必須選擇數(shù)據(jù)。為了防止其他事務(wù)更改正在讀的數(shù)據(jù),使用可重復(fù)讀隔離。
USE AdventureWorks;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRAN
SELECT SalesOrderID, SalesOrderDetailID, ProductID, OrderQty
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = 43659
參數(shù)REPEATABLE READ的含義:
1. 指定語句不能讀取已由其他事務(wù)修改但尚未提交的行,并且指定,其他任何事務(wù)都不能在當前事務(wù)完成之前修改由當前事務(wù)讀取的數(shù)據(jù)。
2. 對事務(wù)中的每個語句所讀取的全部數(shù)據(jù)都設(shè)置了共享鎖,并且該共享鎖一直保持到事務(wù)完成為止。這樣可以防止其他事務(wù)修改當前事務(wù)讀取的任何行。其他事務(wù)可以插入與當前事務(wù)所發(fā)出語句的搜索條件相匹配的新行。如果當前事務(wù)隨后重試執(zhí)行該語句,它會檢索新行,從而產(chǎn)生幻讀。由于共享鎖一直保持到事務(wù)結(jié)束,而不是在每個語句結(jié)束時釋放,所以并發(fā)級別低于默認的 READ COMMITTED 隔離級別。此選項只在必要時使用。
打開第二個查詢窗口并執(zhí)行以下代碼嘗試更新SalesOrderDetail表以更改查詢窗口1中要使用的基礎(chǔ)數(shù)據(jù):
UPDATE Sales.SalesOrderDetail
SET OrderQty = 5
WHERE SalesOrderID = 43659
AND ProductID = 777
查詢會等待。不像快照隔離級別,不可能更新數(shù)據(jù),因為共享鎖會保持以防止其他事務(wù)更改數(shù)據(jù)。這個鎖可以通過前面用過的管理視圖sys.dm_tran_locks查看。
單擊工具條上的"取消執(zhí)行查詢"按鈕取消在查詢窗口2中的查詢。而執(zhí)行以下INSERT語句在訂單中加入一個新行項。
INSERT INTO Sales.SalesOrderDetail
(
SalesOrderID,
CarrierTrackingNumber,
OrderQty,
ProductID,
SpecialOfferID,
UnitPrice,
UnitPriceDiscount
)
VALUES(43659,'4911-403C-98',1,758,1,874,0)
注意,即使正處于可重復(fù)讀隔離級別,這個語句也會成功執(zhí)行。因為可重復(fù)讀會鎖定數(shù)據(jù)以阻止數(shù)據(jù)的更新,但INSERT語句向數(shù)據(jù)庫中插入新數(shù)據(jù),這是允許的。新行處于查詢窗口1中事務(wù)SELECT語句的查詢范圍之中,所以會在事務(wù)下一次獲取相同數(shù)據(jù)的時候被讀取到。這稱作幻像讀。
重復(fù)SELECT語句并提交這個事務(wù),如下所示:
SELECT SalesOrderID, SalesOrderDetailID, ProductID, OrderQty
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = 43659
COMMIT TRAN
可以觀察到,新行被SELECT語句讀取了,因為它處于這個語句的查詢范圍之內(nèi)??芍貜?fù)讀級別會阻止現(xiàn)有數(shù)據(jù)被更改,但不會阻止新數(shù)據(jù)插入SELECT語句的查詢范圍內(nèi)。
其他
SET TRANSACTION一共有以下幾種級別:
SET TRANSACTION ISOLATION LEVEL
{ READ UNCOMMITTED
| READ COMMITTED
| REPEATABLE READ
| SNAPSHOT
| SERIALIZABLE
}
[ ; ]
上面的例子中沒有提到的幾種隔離級別的說明:
- READ UNCOMMITTED
指定語句可以讀取已由其他事務(wù)修改但尚未提交的行。
在 READ UNCOMMITTED 級別運行的事務(wù),不會發(fā)出共享鎖來防止其他事務(wù)修改當前事務(wù)讀取的數(shù)據(jù)。READ UNCOMMITTED 事務(wù)也不會被排他鎖阻塞,排他鎖會禁止當前事務(wù)讀取其他事務(wù)已修改但尚未提交的行。設(shè)置此選項之后,可以讀取未提交的修改,這種讀取稱為臟讀。在事務(wù)結(jié)束之前,可以更改數(shù)據(jù)中的值,行也可以出現(xiàn)在數(shù)據(jù)集中或從數(shù)據(jù)集中消失。該選項的作用與在事務(wù)內(nèi)所有 SELECT 語句中的所有表上設(shè)置 NOLOCK 相同。這是隔離級別中限制最少的級別。
在 SQL Server 2005 中,您還可以使用下列任意一種方法,在保護事務(wù)不臟讀未提交的數(shù)據(jù)修改的同時盡量減少鎖定爭用:
1. READ COMMITTED 隔離級別,并將 READ_COMMITTED_SNAPSHOT 數(shù)據(jù)庫選項設(shè)置為 ON。
2. SNAPSHOT 隔離級別。
- READ COMMITTED
指定語句不能讀取已由其他事務(wù)修改但尚未提交的數(shù)據(jù)。這樣可以避免臟讀。其他事務(wù)可以在當前事務(wù)的各個語句之間更改數(shù)據(jù),從而產(chǎn)生不可重復(fù)讀取和幻像數(shù)據(jù)。該選項是 SQL Server 的默認設(shè)置。
READ COMMITTED 的行為取決于 READ_COMMITTED_SNAPSHOT 數(shù)據(jù)庫選項的設(shè)置:
1. 如果將 READ_COMMITTED_SNAPSHOT 設(shè)置為 OFF(默認設(shè)置),則數(shù)據(jù)庫引擎 會使用共享鎖防止其他事務(wù)在當前事務(wù)執(zhí)行讀取操作期間修改行。共享鎖還會阻止語句在其他事務(wù)完成之前讀取由這些事務(wù)修改的行。語句完成后便會釋放共享鎖。
2. 如果將 READ_COMMITTED_SNAPSHOT 設(shè)置為 ON,則數(shù)據(jù)庫引擎 會使用行版本控制為每個語句提供一個在事務(wù)上一致的數(shù)據(jù)快照,因為該數(shù)據(jù)在語句開始時就存在。不使用鎖來防止其他事務(wù)更新數(shù)據(jù)。
當 READ_COMMITTED_SNAPSHOT 數(shù)據(jù)庫選項設(shè)置為 ON 時,您可以使用 READCOMMITTEDLOCK 表提示為 READ_COMMITTED 隔離級別上運行的事務(wù)中的各語句請求共享鎖,而不是行版本控制。
注意:設(shè)置 READ_COMMITTED_SNAPSHOT 選項時,數(shù)據(jù)庫中僅允許存在執(zhí)行 ALTER DATABASE 命令的連接。在 ALTER DATABASE 完成之前,數(shù)據(jù)庫中不允許有其他打開的連接。數(shù)據(jù)庫不必處于單用戶模式。
- SERIALIZABLE
請指定下列內(nèi)容:
1. 語句不能讀取已由其他事務(wù)修改但尚未提交的數(shù)據(jù)。
2. 任何其他事務(wù)都不能在當前事務(wù)完成之前修改由當前事務(wù)讀取的數(shù)據(jù)。
3. 在當前事務(wù)完成之前,其他事務(wù)不能使用當前事務(wù)中任何語句讀取的鍵值插入新行。
范圍鎖處于與事務(wù)中執(zhí)行的每個語句的搜索條件相匹配的鍵值范圍之內(nèi)。這樣可以阻止其他事務(wù)更新或插入任何行,從而限定當前事務(wù)所執(zhí)行的任何語句。這意味著如果再次執(zhí)行事務(wù)中的任何語句,則這些語句便會讀取同一組行。在事務(wù)完成之前將一直保持范圍鎖。這是限制最多的隔離級別,因為它鎖定了鍵的整個范圍,并在事務(wù)完成之前一直保持范圍鎖。因為并發(fā)級別較低,所以應(yīng)只在必要時才使用該選項。該選項的作用與在事務(wù)內(nèi)所有 SELECT 語句中的所有表上設(shè)置 HOLDLOCK 相同。
需要注意的地方:
1. 一次只能設(shè)置一個隔離級別選項,而且設(shè)置的選項將一直對那個連接始終有效,直到顯式更改該選項為止。事務(wù)中執(zhí)行的所有讀取操作都會在指定的隔離級別的規(guī)則下運行,除非語句的 FROM 子句中的表提示為表指定了其他鎖定行為或版本控制行為。
2. 事務(wù)隔離級別定義了可為讀取操作獲取的鎖類型。針對 READ COMMITTED 或 REPEATABLE READ 獲取的共享鎖通常為行鎖,盡管當讀取引用了頁或表中大量的行時,行鎖可以升級為頁鎖或表鎖。如果某行在被讀取之后由事務(wù)進行了修改,則該事務(wù)會獲取一個用于保護該行的排他鎖,并且該排他鎖在事務(wù)完成之前將一直保持。例如,如果 REPEATABLE READ 事務(wù)具有用于某行的共享鎖,并且該事務(wù)隨后修改了該行,則共享行鎖便會轉(zhuǎn)換為排他行鎖。
3. 在事務(wù)進行期間,可以隨時將事務(wù)從一個隔離級別切換到另一個隔離級別,但有一種情況例外。即在從任一隔離級別更改到 SNAPSHOT 隔離時,不能進行上述操作。否則會導(dǎo)致事務(wù)失敗并回滾。但是,可以將在 SNAPSHOT 隔離中啟動的事務(wù)更改為任何其他隔離級別。
4. 將事務(wù)從一個隔離級別更改為另一個隔離級別之后,便會根據(jù)新級別的規(guī)則對更改后讀取的資源執(zhí)行保護。在更改前讀取的資源將繼續(xù)按照以前級別的規(guī)則受到保護。例如,如果某個事務(wù)從 READ COMMITTED 更改為 SERIALIZABLE,則在該事務(wù)結(jié)束前,更改后所獲取的共享鎖將一直處于保留狀態(tài)。
5. 如果在存儲過程或觸發(fā)器中發(fā)出 SET TRANSACTION ISOLATION LEVEL,則當對象返回控制時,隔離級別會重設(shè)為在調(diào)用對象時有效的級別。例如,如果在批處理中設(shè)置 REPEATABLE READ,并且該批處理調(diào)用一個將隔離級別設(shè)置為 SERIALIZABLE 的存儲過程,則當該存儲過程將控制返回給該批處理時,隔離級別就會恢復(fù)為 REPEATABLE READ。