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