1. 程式人生 > >【轉】SQL Server 事務隔離級別詳解

【轉】SQL Server 事務隔離級別詳解

SQL 事務隔離級別

概述

     隔離級別用於決定如果控制併發使用者如何讀寫資料的操作,同時對效能也有一定的影響作用。

步驟

事務隔離級別通過影響讀操作來間接地影響寫操作;可以在回話級別上設定事務隔離級別也可以在查詢(表級別)級別上設定事務隔離級別。
事務隔離級別總共有6個隔離級別:
READ UNCOMMITTED(未提交讀,讀髒),相當於(NOLOCK)
READ COMMITTED(已提交讀,預設級別)
REPEATABLE READ(可以重複讀),相當於(HOLDLOCK)
SERIALIZABLE(可序列化)
SNAPSHOT(快照)
READ COMMITTED SNAPSHOT(已經提交讀隔離)
對於前四個隔離級別:READ UNCOMMITTED<READ COMMITTED<REPEATABLE READ<SERIALIZABLE
隔離級別越高,讀操作的請求鎖定就越嚴格,鎖的持有時間久越長;所以隔離級別越高,一致性就越高,併發性就越低,同時效能也相對影響越大.

獲取事務隔離級別(isolation level)

DBCC USEROPTIONS 

設定隔離

複製程式碼
設定回話隔離
SET TRANSACTION ISOLATION LEVEL <ISOLATION NAME>
--注意:在設定回話隔離時(REPEATABLE READ)兩個單詞需要用空格間隔開,但是在表隔離中可以粘在一起(REPEATABLEREAD)

設定查詢表隔離
SELECT ....FROM <TABLE> WITH (<ISOLATION NAME>) 
複製程式碼

1.READ UNCOMMITTED

READ UNCOMMITTED:未提交讀,讀髒資料
預設的讀操作:需要請求共享鎖,允許其他事物讀鎖定的資料但不允許修改.
READ UNCOMMITTED:讀操作不申請鎖,執行讀取未提交的修改,也就是允許讀髒資料,讀操作不會影響寫操作請求排他鎖.

 建立測試資料

複製程式碼
IF OBJECT_ID('Orders','U') IS NOT NULL DROP TABLE Orders 
GO
CREATE TABLE Orders
(ID INT NOT NULL,
Price FLOAT NOT NULL
);
INSERT INTO Orders VALUES(10,10.00),(11,11.00),(12,12.00),(13,13.00),(14,14.00);
GO
SELECT ID,Price FROM Orders 
複製程式碼

新建回話1將訂單10的價格加1

複製程式碼
BEGIN TRANSACTION
UPDATE Orders 
SET Price=Price+1
WHERE ID=10

SELECT ID,Price FROM Orders 
WHERE ID=10
複製程式碼

在另一個回話2中執行查詢操作

複製程式碼
首先不新增隔離級別,預設是READ COMMITTED,由於資料之前的更新操作使用了排他鎖,所以查詢一直在等待鎖釋放*/
SELECT ID,Price FROM Orders 
WHERE ID=10
---將查詢的隔離級別設定為READ UNCOMMITTED允許未提交讀,讀操作之前不請求共享鎖。
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT ID,Price FROM Orders 
WHERE ID=10;
--當然也可以使用表隔離,效果是一樣的
SELECT ID,Price FROM Orders WITH (NOLOCK)
WHERE ID=10
複製程式碼

假設在回話1中對操作執行回滾操作,這樣價格還是之前的10,但是回話2中則讀取到的是回滾前的價格11,這樣就屬於一個讀髒操作

ROLLBACK TRANSACTION

2.READ COMMITTED

READ COMMITTED(已提交讀)是SQL SERVER預設的隔離級別,可以避免讀取未提交的資料,隔離級別比READ UNCOMMITTED未提交讀的級別更高;
該隔離級別讀操作之前首先申請並獲得共享鎖,允許其他讀操作讀取該鎖定的資料,但是寫操作必須等待鎖釋放,一般讀操作讀取完就會立刻釋放共享鎖。

新建回話1將訂單10的價格加1,此時回話1的排他鎖鎖住了訂單10的值

複製程式碼
BEGIN TRANSACTION
UPDATE Orders 
SET Price=Price+1
WHERE ID=10

SELECT ID,Price FROM Orders 
WHERE ID=10
複製程式碼

在回話2中執行查詢,將隔離級別設定為READ COMMITTED

複製程式碼
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SELECT ID,Price FROM Orders 
WHERE ID=10
---由於READ COMMITTED需要申請獲得共享鎖,而鎖與回話1的排他鎖衝突,回話被堵塞,

----在回話1中執行事務提交
COMMIT TRANSACTION
/*由於回話1事務提交,釋放了訂單10的排他鎖,此時回話2申請共享鎖成功查到到訂單10的價格為修改後的價格11,READ COMMITTED由於是已提交讀隔離級別,所以不會讀髒資料.
*/
複製程式碼

重置資料

UPDATE Orders 
SET Price=10
WHERE ID=10

注意:但是由於READ COMMITTED讀操作一完成就立即釋放共享鎖,讀操作不會在一個事務過程中保持共享鎖,也就是說在一個事務的的兩個查詢過程之間有另一個回話對資料資源進行了更改,會導致一個事務的兩次查詢得到的結果不一致,這種現象稱之為不可重複讀.

3.REPEATABLE READ

REPEATABLE READ(可重複讀):保證在一個事務中的兩個讀操作之間,其他的事務不能修改當前事務讀取的資料,該級別事務獲取資料前必須先獲得共享鎖同時獲得的共享鎖不立即釋放一直保持共享鎖至事務完成,所以此隔離級別查詢完並提交事務很重要。

在回話1中執行查詢訂單10,將回話級別設定為REPEATABLE READ

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRANSACTION
SELECT ID,Price FROM Orders 
WHERE ID=10

新建回話2修改訂單10的價格

UPDATE Orders 
SET Price=Price+1
WHERE ID=10
---由於回話1的隔離級別REPEATABLE READ申請的共享鎖一直要保持到事務結束,所以回話2無法獲取排他鎖,處於等待狀態

在回話1中執行下面語句,然後提交事務

SELECT ID,Price FROM Orders 
WHERE ID=10
COMMIT TRANSACTION

回話1的兩次查詢得到的結果一致,前面的兩個隔離級別無法得到一致的資料,此時事務已提交同時釋放共享鎖,回話2申請排他鎖成功,對行執行更新

REPEATABLE READ隔離級別保證一個事務中的兩次查詢到的結果一致,同時保證了丟失更新
丟失更新:兩個事務同時讀取了同一個值然後基於最初的值進行計算,接著再更新,就會導致兩個事務的更新相互覆蓋。
例如酒店訂房例子,兩個人同時預定同一酒店的房間,首先兩個人同時查詢到還有一間房間可以預定,然後兩個人同時提交預定操作,事務1執行number=1-0,同時事務2也執行number=1-0最後修改number=0,這就導致兩個人其中一個人的操作被另一個人所覆蓋,REPEATABLE READ隔離級別就能避免這種丟失更新的現象,當事務1查詢房間時事務就一直保持共享鎖直到事務提交,而不是像前面的幾個隔離級別查詢完就是否共享鎖,就能避免其他事務獲取排他鎖。

 4.SERIALIZABLE

SERIALIZABLE(可序列化),對於前面的REPEATABLE READ能保證事務可重複讀,但是事務只鎖定查詢第一次執行時獲取的資料資源(資料行),而不能鎖定查詢結果之外的行,就是原本不存在於資料表中的資料。因此在一個事務中當第一個查詢和第二個查詢過程之間,有其他事務執行插入操作且插入資料滿足第一次查詢讀取過濾的條件時,那麼在第二次查詢的結果中就會存在這些新插入的資料,使兩次查詢結果不一致,這種讀操作稱之為幻讀。
為了避免幻讀需要將隔離級別設定為SERIALIZABLE

複製程式碼
IF OBJECT_ID('Orders','U') IS NOT NULL DROP TABLE Orders 
GO
CREATE TABLE Orders
(ID INT NOT NULL PRIMARY KEY,
Price FLOAT NOT NULL,
type INT NOT NULL
);
INSERT INTO Orders VALUES(10,10.00,1),(11,11.00,1),(12,12.00,1),(13,13.00,1),(14,14.00,1);
GO
複製程式碼

在回話1中執行查詢操作,並將事務隔離級別設定為REPEATABLE READ(先測試一下前面更低級別的隔離)

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRANSACTION 
SELECT ID,Price,type FROM Orders
WHERE TYPE=1

在回話2中執行插入操作

INSERT INTO Orders VALUES(15,15.00,1)

返回回話1重新執行查詢操作並提交事務

SELECT ID,Price,type FROM Orders
WHERE TYPE=1
COMMIT TRANSACTION

結果回話1中第二次查詢到的資料包含了回話2新插入的資料,兩次查詢結果不一致(驗證之前的隔離級別不能保證幻讀)

重新插入測試資料

複製程式碼
IF OBJECT_ID('Orders','U') IS NOT NULL DROP TABLE Orders 
GO
CREATE TABLE Orders
(ID INT NOT NULL PRIMARY KEY,
Price FLOAT NOT NULL,
type INT NOT NULL
);
INSERT INTO Orders VALUES(10,10.00,1),(11,11.00,1),(12,12.00,1),(13,13.00,1),(14,14.00,1);
GO
複製程式碼

接下來將回話級別設定為SERIALIZABLE,在回話1中執行查詢操作,並將事務隔離級別設定為SERIALIZABLE

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION 
SELECT ID,Price,type FROM Orders
WHERE TYPE=1

在回話2中執行插入操作

INSERT INTO Orders VALUES(15,15.00,1)

返回回話1重新執行查詢操作並提交事務

SELECT ID,Price,type FROM Orders
WHERE TYPE=1
COMMIT TRANSACTION

兩次執行的查詢結果相同

重置所有打開回話的預設隔離級別

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

5.SNAPSHOT

SNAPSHOT快照:SNAPSHOT和READ COMMITTED SNAPSHOT兩種隔離(可以把事務已經提交的行的上一版本儲存在TEMPDB資料庫中)
SNAPSHOT隔離級別在邏輯上與SERIALIZABLE類似
READ COMMITTED SNAPSHOT隔離級別在邏輯上與 READ COMMITTED類似
不過在快照隔離級別下讀操作不需要申請獲得共享鎖,所以即便是資料已經存在排他鎖也不影響讀操作。而且仍然可以得到和SERIALIZABLE與READ COMMITTED隔離級別類似的一致性;如果目前版本與預期的版本不一致,讀操作可以從TEMPDB中獲取預期的版本。

如果啟用任何一種基於快照的隔離級別,DELETE和UPDATE語句在做出修改前都會把行的當前版本複製到TEMPDB中,而INSERT語句不需要在TEMPDB中進行版本控制,因為此時還沒有行的舊資料

無論啟用哪種基於快照的隔離級別都會對更新和刪除操作產生效能的負面影響,但是有利於提高讀操作的效能因為讀操作不需要獲取共享鎖;

5.1SNAPSHOT

SNAPSHOT 在SNAPSHOT隔離級別下,當讀取資料時可以保證操作讀取的行是事務開始時可用的最後提交版本
同時SNAPSHOT隔離級別也滿足前面的已提交讀,可重複讀,不幻讀;該隔離級別實用的不是共享鎖,而是行版本控制
使用SNAPSHOT隔離級別首先需要在資料庫級別上設定相關選項

在開啟的所有查詢視窗中執行以下操作

ALTER DATABASE TEST SET ALLOW_SNAPSHOT_ISOLATION ON;

重置測試資料

複製程式碼
IF OBJECT_ID('Orders','U') IS NOT NULL DROP TABLE Orders 
GO
CREATE TABLE Orders
(ID INT NOT NULL PRIMARY KEY,
Price FLOAT NOT NULL,
type INT NOT NULL
);
INSERT INTO Orders VALUES(10,10.00,1),(11,11.00,1),(12,12.00,1),(13,13.00,1),(14,14.00,1);
GO
複製程式碼 複製程式碼
在回話1中開啟事務,將訂單10的價格加1,並查詢跟新後的價格
BEGIN TRANSACTION
UPDATE Orders 
SET Price=Price+1
WHERE ID=10

SELECT ID,Price,type FROM Orders
WHERE ID=10
---查詢到更新後的價格為11

---在回話2中將隔離級別設定為SNAPSHOT,並開啟事務(此時查詢也不會因為回話1的排他鎖而等待,依然可以查詢到資料)
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
BEGIN TRANSACTION
SELECT ID,Price,type FROM Orders
WHERE ID=10

---查詢到的結果還是回話1修改前的價格,由於回話1在預設的READ COMMITTED隔離級別下執行,SQL SERVER必須在更新前把行的一個副本複製到TEMPDB資料庫中
--在SNAPSHOT級別啟動事務會請求行版本

---現在在回話1中執行提交事務,此時訂單10的價格為11
COMMIT TRANSACTION

---再次在回話二中查詢訂單10的價格並提交事務,結果還是10,因為事務要保證兩次查詢的結果相同

SELECT ID,Price,type FROM Orders
WHERE ID=10

COMMIT TRANSACTION

---此時如果在回話2中重新開啟一個事務,查詢到的訂單10的價格則是11
BEGIN TRANSACTION
SELECT ID,Price,type FROM Orders
WHERE ID=10

COMMIT TRANSACTION

/*SNAPSHOT隔離級別保證操作讀取的行是事務開始時可用的最後已提交版本,由於回話1的事務未提交,所以訂單10的最後提交版本還是修改前的價格10,所以回話2讀取到的價格是回話2事務開始前的已提交版本價格10,當回話1提交事務後,回話2重新新建一個事務此時事務開啟前的價格已經是11了,所以查詢到的價格是11,同時SNAPSHOT隔離級別還能保證SERIALIZABLE的隔離級別*/
複製程式碼

5.2READ COMMITTED SNAPSHOT

READ COMMITTED SNAPSHOT也是基於行版本控制,但是READ COMMITTED SNAPSHOT的隔離級別是讀操作之前的最後已提交版本,而不是事務前的已提交版本,有點類似前面的READ COMMITTED能保證已提交讀,但是不能保證可重複讀,不能避免幻讀,但是又比 READ COMMITTED隔離級別多出了不需要獲取共享鎖就可以讀取資料

要啟用READ COMMITTED SNAPSHOT隔離級別同樣需要修改資料庫選項,在回話1,回話2中執行以下操作(執行下面的操作當前連線必須是資料庫的唯一連線,可以通過查詢已連線當前資料庫的程序,然後KILL掉那些程序,然後再執行該操作,否則可能無法執行成功)

複製程式碼
ALTER DATABASE TEST SET READ_COMMITTED_SNAPSHOT ON

IF OBJECT_ID('Orders','U') IS NOT NULL DROP TABLE Orders 
GO
CREATE TABLE Orders
(ID INT NOT NULL PRIMARY KEY,
Price FLOAT NOT NULL,
type INT NOT NULL
);
INSERT INTO Orders VALUES(10,10.00,1),(11,11.00,1),(12,12.00,1),(13,13.00,1),(14,14.00,1);
GO

-----在回話1中開啟事務,將訂單10的價格加1,並查詢跟新後的價格,並保持事務一直處於開啟狀態
BEGIN TRANSACTION
UPDATE Orders 
SET Price=Price+1
WHERE ID=10

--查詢到的價格是11
SELECT ID,Price,type FROM Orders
WHERE ID=10

---在回話2中開啟事務查詢訂單10並一直保持事務處於開啟狀態(此時由於回話1還未提交事務,所以回話2中查詢到的還是回話1執行事務之前儲存的行版本)
BEGIN TRANSACTION
SELECT ID,Price,type FROM Orders
WHERE ID=10
--查詢到的價格還是10

---在回話1中提交事務
COMMIT TRANSACTION 

---在回話2中再次執行查詢訂單10的價格,並提交事務
SELECT ID,Price,type FROM Orders
WHERE ID=10
COMMIT TRANSACTION 
--此時的價格為回話1修改後的價格11,而不是事務之前已提交版本的價格,也就是READ COMMITTED SNAPSHOT隔離級別在同一事務中兩次查詢的結果不一致.
複製程式碼

關閉所有連線,然後開啟一個新的連線,禁用之前設定的資料庫快照隔離級別選項

ALTER DATABASE TEST SET ALLOW_SNAPSHOT_ISOLATION OFF;

ALTER DATABASE TEST SET READ_COMMITTED_SNAPSHOT OFF;

總結

理解了事務隔離級別有助於理解事務的死鎖。

相關推薦

SQL Server 事務隔離級別

SQL 事務隔離級別 概述      隔離級別用於決定如果控制併發使用者如何讀寫資料的操作,同時對效能也有一定的影響作用。 步驟 事務隔離級別通過影響讀操作來間接地影響寫操作;可以在回話級別上設定事務隔離級別也可以在查詢(表級別)級別上設定事務隔離級別。事務隔離級別總共有6個隔離級別:READ UNC

SQL Server 事務隔離級別

完成 sql 事務 create 事務隔離 測試數據 span read type off SQL 事務隔離級別 概述 隔離級別用於決定如果控制並發用戶如何讀寫數據的操作,同時對性能也有一定的影響作用。 步驟 事務隔離級別通過影響讀操作來間接地影響寫操作;可以在回

走馬觀花SQL SERVER 2012 新增函式-01.字串函式CONCAT

SQLSERVER 2012新增了兩個字串函式CONCAT和FORMAT。首先介紹一下CONCAT,CONCAT函式的作用是可以返回多個字串拼接後的結果。簡單示例:select CONCAT('a','b') as R1 /* R1 ---- ab */ select 'a'

SQL Server 事務隔離級別

目前 lte log har 獲取 span 單用戶模式 最大 logs 參考文檔: https://docs.microsoft.com/zh-cn/sql/t-sql/statements/set-transaction-isolation-level-transact

原創新說Mysql事務隔離級別

引言 大家在面試中一定碰到過 說說事務的隔離級別吧? 老實說,事務隔離級別這個問題,無論是校招還是社招,面試官都愛問!然而目前網上很多文章,說句實在話啊,我看了後我都懷疑作者弄懂沒!因為他們對可重複讀(Repeatable Read)和序列化(serializable)的解析實在是看的我一頭霧水! 再加上

Sql Server 事務隔離級別的檢視及更改

根據自身 Sql Server 的情況來自定義 事務隔離級別,將會更加的滿足需求,或提升效能。例如,對於邏輯簡單的 Sql Server,完全可以使用 read uncommitted 模式,來減少死鎖,減少堵塞, 提升效能和響應。對於此種應用場景應該是蠻多的,但是卻沒有一個全域性設定,你妹呀! 這個功能

C語言文件操作

pri void rfi 識別 archive format 隨機 stat 文本文 轉自:http://www.cnblogs.com/likebeta/archive/2012/06/16/2551780.html C語言中沒有輸入輸出語句,所有的輸入輸出功能都用

MySQL事務隔離級別

默認 多少 bcf 結構 有一個 個數 ref tle eat 轉載自: MySQL事務隔離級別詳解 SQL標準定義了4類隔離級別,包括了一些具體規則,用來限定事務內外的哪些改變是可見的,哪些是不可見的。低級別的隔離級一般支持更高的並發處理,並擁有更低的系統開銷。Read

C++拷貝構造函數

簡單的 之間 其他 創建 變量 tac 動態分配空間 data 產生 一. 什麽是拷貝構造函數 首先對於普通類型的對象來說,它們之間的復制是很簡單的,例如: int a = 100; int b = a; 而類對象與普通對象不同,類對象內部結構一般較為復雜,

事務隔離級別

增刪改 col 復讀 直接 提交 新的 bsp OS 不可 一、讀未提交。(A事務能夠讀取到B事務對數據的增刪改操作) 該事務級別會出現臟讀問題。 二、讀已提交。(該事務級別不會出現臟讀問題) 只要一個事務A提交了,那麽事務A中對數據庫表的增刪改操作,都會直接

java的動態代理機制

bar 同時 @override returns 復制 exce ins com hello 在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴註入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎

mysql 事務隔離級別

bat con enc ron 問題 修改方法 復讀 get 很多 事物的 隔離級別,說簡單非常簡單(新手也能說出 是個隔離級別 和 影響),說男也非常難。(很多 有幾年編程 經驗的程序員依舊搞不清楚) 廢話不多少 直接開始: 事務的隔離級別 是用來描述

Oracle 11g Dataguard 參數

異步模式 正常 10g enable ffi sys 過程 tnsnames async 轉自:https://www.jb51.net/article/52269.htm 這篇文章主要介紹了Oracle 11g Dataguard參數詳解,包含了獨立參數、主庫參數、備

Mybatis Generator最完整配置

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration   PUBLIC "-//mybatis.org//DTD MyBatis Generator Confi

XML和Schema名稱空間---例項篇

上文把XML和Schema的名稱空間的一些相關內容進行了詳細介紹,下面通過例子來具體瞭解:     例一:重點理解名稱空間的相關概念。    下面的例子是一個XML Schema檔案,名為"note.xsd" <?xml version="1.0"?>&

XML和Schema名稱空間

XML和Schema具有無關平臺,技術廠商,簡單,規範統一等特點,極具開放性,所以使用極為廣泛,而且使用簡單,在XML和Schema和,個人認為比較不好理解的一點是其名稱空間問題,在這篇部落格裡詳細進行理解。     名稱空間是W3C推薦標準提供的一種統一命名XML文件中的元素

UML的九種圖例

UML圖中類之間的關係:依賴,泛化,關聯,聚合,組合,實現 類與類圖 1) 類(Class)封裝了資料和行為,是面向物件的重要組成部分,它是具有相同屬性、操作、關係的物件集合的總稱。 2) 在系統中,每個類具有一定的職責,職責指的是類所擔任的任務,即類要完成什麼樣的功能,要承擔什麼樣的義

Android OkHttp3簡介和使用

一 OKHttp簡介 OKHttp是一個處理網路請求的開源專案,Android 當前最火熱網路框架,由移動支付Square公司貢獻,用於替代HttpUrlConnection和Apache HttpClient(android API23 6.0裡已移除HttpClient)。 OKHttpGit

Linux定時任務Crontab命令

本文轉自:https://www.cnblogs.com/intval/p/5763929.html linux 系統則是由 cron (crond) 這個系統服務來控制的。Linux 系統上面原本就有非常多的計劃性工作,因此這個系統服務是預設啟動的。另 外, 由於使用者自己也可以

maven核心,pom.xml

  感謝如下博主: maven核心,pom.xml詳解 什麼是pom?    pom作為專案物件模型。通過xml表示maven專案,使用pom.xml來實現。主要描述了專案:包括配置檔案;開發者需要遵循的規則,缺陷管理系統,組織和licenses,專案的url,專案的依賴性,以及其他所有的專案