一、事務隔離級別控制著事務的如下表現: 在該行上的排他鎖被釋放之前阻塞其他事務。 檢索在啟動語句或事務時存在的行的已提交版本。 讀取未提交的數據修改。 在該行上的排他鎖被釋放之前阻塞其他事務。 檢索在啟動語句或事務時存在的行的已提交版本。 讀取未提交的數據修改。 以上說明事務隔離級別主要針對讀操作來 ...
一、事務隔離級別控制著事務的如下表現:
- 讀取數據時是否占用鎖以及所請求的鎖類型。
- 占用讀取鎖的時間。
- 引用其他事務修改的行的讀操作是否:
-
- 在該行上的排他鎖被釋放之前阻塞其他事務。
- 檢索在啟動語句或事務時存在的行的已提交版本。
- 讀取未提交的數據修改。
以上說明事務隔離級別主要針對讀操作來說的。
二、臟讀、不可重覆讀、幻讀的區別:
提到事物隔離級別就不能不提這3個概念,可以說事務隔離級別就是為了避免這3種情況出現的。
臟讀:讀到了其他事務已修改但未提交的數據
不可重覆讀:同一事務中兩次查詢讀到的數據不同
幻讀:同一事務中兩次查詢讀到的記錄數不同
可能有人對幻讀和不可重覆讀的定義不太理解,兩者最大的區別實質上在於加鎖的不同,後邊會有講解。
三、ANSI/ISO標准定義了下列事務隔離級別,SQL Server資料庫引擎支持全部這4種隔離級別:
因此四種隔離級別與臟讀、幻讀、不可重覆讀的對應情況如下:
需要特別提醒的是:雖然Mysql、Oracle所支持的事務隔離級別也基本遵循ANSI標準,但卻有很大區別:
- Oracle只支持已提交讀和序列化讀。
-
Mysql預設的的可重覆讀隔離級別通過範圍鎖實現了避免幻讀。
四、除以上4種隔離級別外SQL Server還支持使用行版本控制的其他兩個事務隔離級別:
-
一個是預設的read committed隔離級別下的snapshot實現,嚴格來說並不算一個事務隔離級別,只是read committed的一個特殊形態。
-
一個是全新的事務隔離級別----快照隔離級別。
兩者的開啟方式為:
1、如果要開啟SNAPSHOT事務隔離級別,需要預先設置ALLOW_SNAPSHOT_ISOLATION為ON,且目前只能修改會話級別的事務隔離級別。
ALTER DATABASE [dbname] SET ALLOW_SNAPSHOT_ISOLATION ON; --需要單用戶模式下修改,因為要加庫級別的獨占鎖。
然後執行如下語句修改事務隔離級別:(修改後只在會話級別生效,無法修改全局級別的事務隔離級別)
SET TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SNAPSHOT | SERIALIZABLE }
2、使用READ_COMMITTED_SNAPSHOT,則直接執行下列ALTER語句修改,是在預設的READ COMMITTED隔離級別下修改的,此隔離級別修改後永久生效,使用dbcc useroptions查看可以看到事務隔離級別被全局的修改成了read committed snapshot。
ALTER DATABASE [dbname] SET READ_COMMITTED_SNAPSHOT ON;
兩者的區別在於:
READ_COMMITTED_SNAPSHOT是指Select語句總是讀取最新的已提交的數據,即如果有DML事務正在執行,那麼select語句不會被阻塞而是讀取這些DML事務預先生成的前鏡像,這種讀只會在表上加Sch-S鎖,其他的行鎖頁鎖全部沒有。DML數據一旦提交,再次執行Select語句就會立馬讀到新的數據。
SNAPSHOT隔離級別與上述的區別在於,如果你在同一個事務內執行兩次相同的select語句,那麼即便在這兩次select語句之間發生了數據更改且提交,兩次讀到的數據也是一樣的。
用官網的一句話來描述兩者區別就是:READ_COMMITTED_SNAPSHOT提供語句級的一致性,SNAPSHOT事務隔離級別提供事務級的一致性。
五、全部6種隔離級別的加鎖模式:
開始說過事務隔離級別主要就是控制讀操作加什麼鎖,鎖占用多長時間的的,因此只有搞清各事務隔離級別下的加鎖機制才能徹底搞清事務隔離級別的概念和他們的不同。
1.未提交讀
未提交讀不對讀取的數據加鎖,因此不會造成阻塞,也會有臟讀出現,相當於為select語句添加了with nolock選項。2.已提交讀
已提交讀對讀取的數據正常加鎖,但是不等事務結束才釋放鎖,而是讀完一個頁就會釋放,因此可能出現重覆讀和幻讀。這是SQL Server和Oracle的預設事務隔離級別。Oracle的實現更加完美,對Oracle資料庫來說,select操作不加鎖,因此不會出現讀阻塞寫,但SQL Server有可能。
3.已提交讀快照
SQL Server特有的隔離級別,主要是為了匹配Oracle的已提交讀實現的功能,在此隔離級別下,讀操作只會對錶加一個Sch-S鎖,因此讀操作不會引發在阻塞,但是會加大tempdb的使用量。
4.快照
同上,讀操作也只加Sch-S鎖,唯一區別在於實現的一致性讀是事務級別的,即快照在tempdb中保留的時間更長。
這裡猜測已提交讀快照隔離級別下快照的撤銷是DML事務結束後,而快照讀隔離級別下快照的撤銷是select事務結束後。
5.可重覆讀
可重覆讀加的鎖與已提交讀完全一致,區別在於只有在整個事務完成後才會釋放鎖,而不是讀完一個頁就釋放,此種加鎖方式也避免了不可重覆讀,因為事務期間其他DML無法獲取資源上的鎖。
6.序列化讀
序列化讀加的鎖與已提交讀有區別,此隔離級別下讀操作對索引鍵加的是鍵範圍鎖,而不是普通的S、U、X、IS、IU、IX等。
鍵範圍鎖的機制基本與Mysql中的範圍鎖一致,主要是為了防止幻讀,其機制在於select操作不但會將讀到的鍵值鎖定,還會將上下鍵值的範圍也鎖定。
舉例如下:
有主鍵為1,5,8,9,10的記錄,select ... where col between 3 and 7;會使用鍵範圍鎖將5這條記錄鎖定,除此之外還會用一個鍵範圍鎖將346這幾個虛幻的記錄也鎖定,這樣就不能在讀取操作期間插入數據了,可以防止幻讀。
Ps:對於序列化加的鍵範圍鎖是否是我上邊所說的那麼精確,還需要具體實驗,這裡只是根據官網猜測會使用多餘的一個鍵範圍鎖鎖定可能造成幻讀的記錄(總的鍵範圍鎖數目為n+1個,n為滿足查詢條件的行數),具體實驗方法參見我的另一篇博客,有興趣的可以試試。
http://www.cnblogs.com/leohahah/p/7059852.html
參考文檔:
https://docs.microsoft.com/zh-cn/sql/t-sql/statements/set-transaction-isolation-level-transact-sql
https://msdn.microsoft.com/zh-cn/library/jj856598(v=sql.120).aspx