SQL Server 事務的隔離級別
來源:程序員人生 發布時間:2014-01-11 11:02:12 閱讀次數:3215次
事務的隔離級別
SQL Server通過在鎖資源上使用不同類型的鎖來隔離事務。為了開發安全的事務,定義事務內容以及應在何種情況下回滾至關重要,定義如何以及在多長時間內在事務中保持鎖定也同等重要。這由隔離級別決定。應用不同的隔離級別,SQL Server賦予開發者一種能力,讓他們為每一個單獨事務定義與其他事務的隔離程度。事務隔離級別的定義如下:
是否在讀數據的時候使用鎖
讀鎖持續多長時間
在讀數據的時候使用何種類型的鎖
讀操作希望讀已經被其他事務排他鎖住的數據時,怎么辦?在這種情況下,SQL Server可以:
一直等到其他事務釋放鎖
讀沒有提交的數據
讀數據最后提交后的版本
ANSI 99定義了4種事務隔離級別,SQL Server 2005能夠完全支持這些級別:
未提交讀 在讀數據時不會檢查或使用任何鎖。因此,在這種隔離級別中可能讀取到沒有提交的數據。
已提交讀 只讀取提交的數據并等待其他事務釋放排他鎖。讀數據的共享鎖在讀操作完成后立即釋放。已提交讀是SQL Server的默認隔離級別。
可重復讀 像已提交讀級別那樣讀數據,但會保持共享鎖直到事務結束。
可序列化 工作方式類似于可重復讀。但它不僅會鎖定受影響的數據,還會鎖定這個范圍。這就阻止了新數據插入查詢所涉及的范圍,這種情況可以導致幻像讀。
此外,SQL Server還有兩種使用行版本控制來讀取數據的事務級別(本章后文將詳細檢驗這些隔離級別)。行版本控制允許一個事務在數據排他鎖定后讀取數據的最后提交版本。由于不必等待到鎖釋放就可進行讀操作,因此查詢性能得以大大增強。這兩種隔離級別如下:
已提交讀快照 它是一種提交讀級別的新實現。不像一般的提交讀級別,SQL Server會讀取最后提交的版本并因此不必在進行讀操作時等待直到鎖被釋放。這個級別可以替代提交讀級別。
快照 這種隔離使用行版本來提供事務級別的讀取一致性。這意味著在一個事務中,由于讀一致性可以通過行版本控制實現,因此同樣的數據總是可以像在可序列化級別上一樣被讀取而不必為防止來自其他事務的更改而被鎖定。
無論定義什么隔離級別,對數據的更改總是通過排他鎖來鎖定并直到事務結束時才釋放。
很多情況下,定義正確的隔離級別并不是一個簡單的決定。作為一種通用的規則,要選擇在盡可能短的時間內鎖住最少數據,但同時依然可以為事務提供它所需的安全程度的隔離級別。
已提交讀
在SQL Server 2005中,已提交讀隔離級別是建立連接時的默認隔離級別。這個級別存在兩種類型:已提交讀和已提交讀快照隔離級別。應用哪種類型由數據庫選項定義。已提交讀級別會在讀數據之前等待,直到阻塞鎖被釋放。已提交讀快照級別會在數據被其他事務阻塞時使用行版本控制來讀數據最后一次提交的版本。
使用已提交讀級別:
BEGIN TRAN
SELECT
FirstName, LastName, EmailAddress
FROM
Person.Contact
WHERE
ContactID = 1
返回EmailAddress為gustavo0@adventure-works.com的聯系人Gustavo Achong。
現在假設另一事務在事務打開狀態下更改了EmailAddress。打開第二個查詢窗口并執行以下批來UPDATE EmailAddress,但不提交事務:
USE AdventureWorks;
BEGIN TRAN
UPDATE
Person.Contact
SET
EmailAddress = 'uncommitted@email.at'
WHERE
ContactID = 1
這個UPDATE 語句會正常運行。一行受到了影響,即使數據在這個事務還沒有運行完之前已被查詢窗口1中的事務讀取。因為已提交讀級別并不會在事務結束前保持用于SELECT語句的共享鎖。共享鎖會在數據讀取之后立即被SQL Server釋放。需要一致讀的時候這將是一個問題。我們將下面的"獲取一致的可重復讀操作"實現。
現在切換到查詢窗口1并嘗試再次讀數據:
SELECT
FirstName, LastName, EmailAddress
FROM
Person.Contact
WHERE
ContactID = 1
由于SELECT語句被阻塞,因此這個查詢并沒有結束。SQL Server會嘗試在ContactID= 1的鍵上獲取一個共享鎖,但是由于在查詢窗口2中的UPDATE語句對其有一個排他鎖,因此這個操作不可能完成。雖然查詢窗口2處于已提交讀級別(由于您沒有更改默認級別),但排他鎖依然存在。這個阻塞將持續存在,因為數據更改的排他鎖會一直保持直到事務結束。
切換到查詢窗口2,讓查詢窗口1中的查詢繼續運行。鍵入并執行以下SELECT語句檢查數據庫中的授權和等待的鎖。
可以看一個狀態為WAIT的共享鎖。這是查詢窗口1中運行的查詢。它在等待查詢窗口2中的查詢,后者在同樣的資源上有一個排他鎖。
在查詢窗口2中執行一個ROLLBACK TRAN語句來回滾UPDATE語句。然后切換回查詢窗口1。可以看到,查詢窗口1中的查詢完成了,并且其結果與以前的一樣。查詢窗口2中的事務結束的時候,鎖被釋放了,以至查詢窗口1中的查詢不再被阻塞。由于查詢窗口2中的事務回滾,因此查詢窗口1中得到的結果是原來的數據。如果查詢窗口2中的事務被提交,則查詢窗口1中會得到新的數據作為結果。
在查詢窗口1中執行一個COMMIT TRAN語句并關閉所有的查詢窗口。
可以看出,在(默認)已提交讀級別中SQL Server會等到排他鎖釋放之后再進行讀操作,以此來獲取真正的提交數據。還可以看出,共享鎖會持續到數據被讀取之后,而排他鎖會持續到事務提交之后。在許多事務幾乎同時更改數據的時候這種行為可能會造成問題。在這些情況下,由于排他鎖造成的阻塞,讀數據會非常慢。但在有些情況下,使用最后提交的數據版本是恰當的。在這些情況下,可以將已提交讀級別更改為已提交讀快照級別。
如果要在窗口1讀取數據的話,可以使用這樣的方法:
SELECT
FirstName, LastName, EmailAddress
FROM
Person.Contact WITH (NOLOCK)
WHERE
ContactID = 1
讓它取消所有的鎖機制,那么排他鎖也不會影響到這句查詢。
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈