數據庫中的事物有ACID(原子性,一致性,隔離性,持久性)四個特性。其中隔離性是用來處理併發執行的事務之間的數據訪問控制。SqlServer中提供了幾種不一樣級別的隔離類型。sql
概念 數據庫
Read UnCommitted併發
當前事務能夠讀取其餘事務已修改但還未提交的數據。若是其餘事務進行數據Rollback,當前事務就會出現髒讀,數據錯誤。spa
Read Committedcode
當前事務只能讀取其餘事務修改而且已提交的數據。這樣避免了髒讀。可是若是其餘事務在當前事務兩次讀之間對數據修改,會致使當前事務兩次相同讀取操做獲得的結果不同。server
Repeatable Readblog
在Read Committed基礎上,當前事務在對相同數據的屢次讀之間,其餘事務不能對數據進行修改。所以也就不會出現Read Committed中的屢次讀取數據不一致的狀況。可是該類型下還會有一個問題,在兩次數據讀取之間,其餘事務仍是能夠插入新的數據的,這樣可能致使當前事務兩次讀取的數據數量不同。事務
Serializiablerem
這個算是終極隔離級別了,在Repeatable Read的基礎上,當前事務的兩次數據讀取之間,其餘事務不能插入會出如今當前事務數據讀取操做Where條件所表示範圍內的數據。這樣當前數據的兩次相同的數據操做就不會返回不一樣數目的數據了。get
新版的SQLServer中還有Snapshot隔離級別,之後有機會再研究。
實現原理
Sql Server中,事務隔離是經過鎖的機制來實現的。算是咱們所說的悲觀併發的處理方式。
Read UnCommitted
這種類型下,Sql Server不會對數據加任何鎖(寫操做除外)。所以數據的讀寫就沒有任何限制了。
Read Committed
這是Sql Server的默認事務隔離級別。其餘事務進行寫操做時,會對數據加排他鎖知道事務結束;當前事務對數據進行讀操做時,會對數據加共享鎖(共享鎖與排他鎖衝突),所以若是有事務修改了數據沒有提交,當前事務則沒法獲取到共享鎖,而沒法讀取數據。這樣解決髒讀的問題。由於該級別下獲取的共享鎖在數據讀取到後即失效,所以會出現重複讀帶來的問題。
Repeatable Read
在Read Committed級別的基礎上,該級別下,讀數據時的共享鎖的有效期延長到了事務結束,所以這期間其餘事務沒法拿到排他鎖對數據進行修改。所以解決了重複讀的問題。
Serializiable
在Repeatable Read基礎上,該級別讀數據的時候會針對所讀數據的範圍(而不是隻對讀取到的數據)加共享鎖。所以其餘事務沒法在該範圍下插入數據。
存在問題
1. Repeatable Read和Serializiable解決了數據一致性的問題,可是犧牲了數據的併發訪問能力。
2. Repeatable Read和Serializiable延長了共享鎖到事務結束,這樣會致使多個事務都拿到共享鎖,但當它們都想獲取數據的排他鎖進行修改時,會造成死鎖。由於互相都在等待對方釋放共享鎖。這種狀況下的解決方案是若是在事務中須要對數據進行修改,改成獲取更新鎖(同一時間只有一個事務能拿到相同數據的更新鎖)。這樣就不會出現死鎖的狀況了。
SQL
在Sql Server中,能夠經過SET TRANSACTION ISOLATION LEVEL命令來更改事務隔離類型。設置後統一數據庫鏈接下會一直使用該隔離類型。可是若是在存儲過程當中設置隔離類型,存儲過程在返回的時候,隔離類型恢復爲調用存儲過程以前的狀態。所以在存儲過程當中設置的隔離類型只對該存儲過程執行過程有效。
驗證
Read UnCommitted
有以下數據:
ID Name Age
1 Je 3
在事務1中執行以下SQL
BEGIN TRAN SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED UPDATE Account SET AGE = 4 WHERE ID = 1 //此處沒有提交事務
在事務2中再執行以下SQL
BEGIN TRAN SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED SELECT * FROM Account WHERE ID = 1 COMMIT
會獲得以下結果
ID Name Age
1 Je 4
所以該隔離級別下,能讀到未提交的修改。
Read Committed
將上面的代碼中的隔離級別改成READ COMMITTED。獲得的結果爲事務2在讀取數據時爲一直處於等待狀態。
執行以下代碼查看鎖的狀態。
SELECT * FROM sys.dm_tran_locks where resource_type != 'DATABASE'
會發現事務2一直在等待獲取共享鎖。此時在事務1中執行COMMIT命令,事務2則能夠繼續執行,而且能拿到事務1提交的數據。
ID Name Age
1 Je 4
接下來按順序執行若是代碼
首先重置ID爲1的數據的Age爲3.
事務1
BEGIN TRAN SET TRANSACTION ISOLATION LEVEL READ COMMITTED SELECT * FROM Account WHERE ID = 1 WAITFOR DELAY '00:00:10' SELECT * FROM Account WHERE ID = 1 COMMIT
事務2
BEGIN TRAN SET TRANSACTION ISOLATION LEVEL READ COMMITTED UPDATE Account SET AGE = 4 WHERE ID = 1 COMMIT GO
事務1會獲得以下結果:
ID Name Age
1 Je 3
ID Name Age
1 Je 4
兩次讀的結果不同
Repeatable Read
上面的代碼若是設置爲Repeatable Read隔離類型,獲得以下結果(先將行數據重置爲age=3):
ID Name Age
1 Je 3
ID Name Age
1 Je 3
兩次讀操做的age值都爲3,由於在事務1完成以前,事務2的寫操做被block
若是執行以下代碼:
事務1
BEGIN TRAN SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; SELECT * FROM Account WHERE ID >= 1; WAITFOR DELAY '00:00:10'; SELECT * FROM Account WHERE ID >= 1; COMMIT
事務2
BEGIN TRAN SET TRANSACTION ISOLATION LEVEL REPEATABLE READ INSERT INTO Account values('Je2',3) COMMIT GO
事務1最終會獲得以下結果
ID Name Age
1 Je 3
ID Name Age
1 Je 3
2 Je2 3
兩次讀結果的數量不同。
Serializiable
若是將事務隔離級別設爲Serializiable獲得結果是
ID Name Age
1 Je 3
ID Name Age
1 Je 3
若是在事務1執行期間查看鎖的狀況。會發現事務1得到的鎖的類型爲RangeS-S。即範圍鎖。