接上文SQL SERVER的鎖機制(二)——概述(鎖的兼容性與可以鎖定的資源)
五、鎖與事務隔離級別
事務隔離級別簡單的說,就是當激活事務時,控制事務內因SQL語句產生的鎖定需要保留多入,影響范圍多大,以防止多人訪問時,在事務內發生數據查詢的錯誤。設置事務隔離級別將影響整條連接。
SQL Server 數據庫引擎支持所有這些隔離級別:
· 未提交讀(隔離事務的最低級別,只能保證不讀取物理上損壞的數據)
· 已提交讀(數據庫引擎的默認級別)
· 可重復讀
· 可序列化(隔離事務的最高級別,事務之間完全隔離)
SQL Server 還支持使用行版本控制的兩個事務隔離級別。一個是已提交讀隔離的新實現,另一個是新事務隔離級別(快照)。
設置語句如下:
SET TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SNAPSHOT | SERIALIZABLE } [ ; ] |
(一)未提交讀
未提交讀是最低的事務隔離級別,允許讀取其他事務已經修改但未提交的數據行。SQL SERVER 當此事務等級進行嘗試讀取數據時,不會放置共享鎖,直接讀取數據,所以忽略已存在的互斥鎖。換句話說,即使該資源已經受到了獨占鎖的保護,當使用未提交讀隔離級別時,此數據還是可以被讀取,加快查詢速度,但是會讀取到別人未修改的數據,所以此種讀取被稱為臟讀。此種隔離級別適合不在乎數據變更的查詢場景。此隔離級別與SELECT 語句搭配 NOLOCK 所起到的效果相同
未提交讀示例:
--1.--1.創建測試表
create table tbUnRead
(ID INT,
name nvarchar(20)
)
--2新增記錄
insert tbUnRead
select 1,'Tom'
union
select 2,'Jack'
--3開啟事務,並進行更新
begin tran
update tbUnRead
set name='Jack_upd'
where ID=2
---4查詢事務數量(由於沒有回滾或提交事務)
SELECT @@TRANCOUNT
事務查詢結果如下:
--5打開另一條連接,設置事務隔離級別為(未提交讀)
set Transaction isolation level read uncommitted
--6查詢數據,查詢到的數據是修改之后的數據。
select * from tbUnRead where ID=2
如下圖:
(二)已提交讀
已提交讀是SQL SERVER 默認的事務隔離級別。當事務正在讀取數據時,SQL SERVER 會放置共享鎖以防止其他事務修改數據,當數據讀取完成之后,會自動釋放共享鎖,其他事務可以進行數據修改。因為共享鎖會同時封鎖封鎖語句執行,所以在事務完成數據修改之前,是無法讀取該事務正在修改的數據行。因此此隔離級別可以防止臟讀。
在SQL SERVER 2005以上版本中,如果設置READ_COMMITTED_SNAPSHOT為ON,則已提交讀的事務全使用數據行版本控制的隔離下讀取數據。讀取操作不會獲取正被讀取的數據上的共享鎖(S 鎖),因此不會阻塞正在修改數據的事務。同時,由於減少了所獲取的鎖的數量,因此最大程度地降低了鎖定資源的開銷。使用行版本控制的已提交讀隔離和快照隔離旨在提供副本數據的語句級或事務級讀取一致性。
示例一:設置READ_COMMITTED_SNAPSHOT為OFF
--1.創建測試表
create table tbUnRead
(ID INT,
name nvarchar(20)
)
--2新增記錄
insert tbUnRead
select 1,'Tom'
union
select 2,'Jack'
--3開啟事務,並進行更新
begin tran
update tbUnRead
set name='Jack_upd'
where ID=2
---4查詢事務數量(由於沒有回滾或提交事務)
SELECT @@TRANCOUNT
--5打開另一條連接,設置事務隔離級別為(已提交讀)
set Transaction isolation level read committed
--6查詢數據,由於當前事務沒有提交,所以無法查詢數據
select * from tbUnRead where ID=2
6查詢數據的結果 如下圖:
示例二:設置READ_COMMITTED_SNAPSHOT為ON
use master
go
---創建測試數據庫
create database read_committed_SNAPSHOT_Test
go
---激活數據行版本控制
alter database read_committed_SNAPSHOT_Test set read_committed_SNAPSHOT on
go
use read_committed_SNAPSHOT_Test
go
--1.創建測試表
create table tbReadLevel
(ID INT,
name nvarchar(20)
)
--2新增記錄
insert tbReadLevel
select 1,'測試'
go
select ID,name as "修改前數據" from tbReadLevel
如下圖:
go
--3開啟事務,並進行更新
begin tran
update tbReadLevel
set name='Jack_upd'
where ID=1
---4查詢事務數量(由於沒有回滾或提交事務)
SELECT @@TRANCOUNT
--5打開另一條連接,設置事務隔離級別為(已提交讀)
--查詢數據,查詢到的數據是上一次提交的數據
select * from tbReadLevel where ID=1
5的查詢結果如下圖:
(三)可重復讀
可重復讀事務隔離級別在事務過程中,所有的共享鎖均保留到事務結束,而不是讀取結束就釋放,這與已提交讀的行為截然不同,雖然在事務過程中,重復查詢相同記錄時不受其他事務的影響,但可能由於鎖定數據過久,而導致其他人無法處理數據,影響並發率,更嚴重的可能提高發生死鎖的機率。
總之,如果使用可重復讀隔離級別讀取數據,數據讀出之后,其他事務只能對此范圍中的數據進行讀取或新增,但不可以進行修改,直到讀取事務完成。因此,使用此隔離級別需要謹慎小心,根據實際情況進行設置。
示例:
--1.創建測試表
create table tbUnRead
(ID INT,
name nvarchar(20)
)
--2新增記錄
insert tbUnRead
select 1,'Tom'
union
select 2,'Jack'
--3設置事務隔離級別為(可重復讀)
set Transaction isolation level REPEATABLE READ
--4開啟事務,並進行更新
begin tran
--5查詢數據
select * from tbUnRead where ID=2
---6查詢事務數量(沒有回滾或提交事務)
SELECT @@TRANCOUNT
5與6的執行結果如下圖
---7開啟另一條連接,查詢數據與修改數據
---事務雖然沒有完成,但可以查詢到之前的數據
select * from tbUnRead where ID=2
Go
---8,修改數據,由於事務沒有完成,所以無法進行修改
update tbUnRead
set name='Jack_upd'
where ID=2
go
--7、8的執行結果如下,可以查詢數據,但無法更新數據,如下圖。
(四)快照
快照隔離級別是SQL SERVER 2005之后版本新增的隔離級別,開啟之后,允許事務過程中讀取操作不受異動影響,事務中任一語句所讀取的數據,均予事務激活時,就已經完成提交,符合事務一致性的數據行版本。所以只能查核事務激活之前已經完成提交的數據,也就是說可以查詢已經完成提交的數據行快照集,但看不見已激活的事務正在進行修改的數據行。當使用快照隔離級別讀取數據時不會要求對數據進行鎖定,如果所讀取的記錄正在被某事務進行修改,它也會讀取此記錄之前已經提交的數據。故當某記錄被事務進行修改時,SQL SERVER的TEMPDB數據庫會存儲最近提交的數據行,以供快照隔離級別的事務讀取數據時使用。將Allow_SNAPSHOT_isolation設為ON,事務就會設置快照隔離級別。
use master
go
---創建測試數據庫(快照)
create database SNAPSHOT_Test
go
---激活數據行版本控制
alter database SNAPSHOT_Test set Allow_SNAPSHOT_isolation on
go
use SNAPSHOT_Test
go
--1.創建測試表
create table tbReadLevel
(ID INT,
name nvarchar(20)
)
--2新增記錄
insert tbReadLevel
select 1,'測試'
union
select 2,'快照測試'
go
select ID,name as "修改前數據"
from tbReadLevel
go
--3開啟事務,並進行更新
begin tran
update tbReadLevel
set name='Jack_upd_快照'
where ID=1
---4查詢事務數量(沒有回滾或提交事務)
SELECT @@TRANCOUNT
--2、4的執行結果,如下圖。
--5打開另一條連接,設置事務隔離級別為(快照)
set Transaction isolation level SNAPSHOT
--6查詢數據,查詢的數據是上一次提交的數據
select * from tbReadLevel where ID=1
(五)可序列化
可序列化是事務隔離級別中最高的級別,為最嚴謹的隔離級別,因為它會鎖定整個范圍的索引鍵,使事務與其他事務完全隔離。在現行事務完成之前,其他事務不能插入新的數據行,其索引鍵值存在於現行事務所讀取的索引鍵范圍之中。此隔離級別與Select 搭配holdlock效果一樣。
示例:
--1.創建測試表
create table tbUnRead
(ID INT,
name nvarchar(20)
)
--2新增記錄
insert tbUnRead
select 1,'Tom'
union
select 2,'Jack'
--3設置事務隔離級別為(可序列化)
set Transaction isolation level SERIALIZABLE
--5開啟事務,並進行更新
begin tran
select * from tbUnRead where ID=2
---6查詢事務數量(沒有回滾或提交事務)
SELECT @@TRANCOUNT
5、6執行結果如下圖。
---7,開啟另一條連接,查詢數據,可以查詢到之前的數據
select * from tbUnRead where ID=2
---8,修改數據,無法修改數據
update tbUnRead
set name='Jack_upd'
where ID=2
--新增數據,無法插入數據
insert tbUnRead
select 3,'May'