SQLserver鎖和事務隔離級別的比較與使用

2021-09-08 02:41:01 字數 4282 閱讀 1188

物件

①     鎖:每條sql

語句②     隔離:事務鎖①

併發問題

丟失更新

未確認的讀取(髒讀)

不一致的分析(非重複讀):多次讀取相同的資料(行)不一致(其他使用者更改update

)幻像讀:多次讀取有不存在和新增的資料(其他使用者插入insert

或刪除delete

)隔離級別

隔離級別 髒讀

不可重複讀取 幻像

說明未提交讀(read uncommitted)是

是 是

如果其他事務更新,不管是否提交,立即執行

提交讀(read committed預設)

否 是

是 讀取提交過的資料。如果其他事務更新沒提交,則等待

可重複讀(repeatable read)否

否 是

查詢期間,不允許其他事務update

可序列讀(serializable)否

否 否

查詢期間,不允許其他事務insert

或delete

提交讀

假設存在表a

,如下所示a1

a2 a3

11 21

31 12

22 32

開啟查詢分析器並開啟兩個連線,分別輸入如下兩個事務:

--事務

ⅰset transaction isolation level read committed

begin tran

update a set a2 = 20 where a1 = 11

waitfor delay '00:00:10'

rollback tran

--事務

ⅱset transaction isolation level read committed

select * from a where a1 = 11

如果先執行事務ⅰ

,然後緊接著執行事務

ⅱ,則事務

ⅱ要等待

10秒鐘(乙個連線在修改資料塊時別的連線也不能查詢這個資料塊,直到解鎖。反之亦然:讀的時候不能寫和修改)。

如果把事務ⅱ

改為如下

set transaction isolation level read uncommitted

select * from a where a1 = 11

那麼事務ⅱ

不需等待,立即執行(可以看出

read uncommitted

事務select

不對資料發出共享鎖)

鎖:(這裡主要講解共享鎖和排他鎖兩種經常用到的鎖)

共享鎖主要是為了共享讀(select

),如果存在事務(乙個或多個)擁有對錶中資料(關於鎖資料的多少,視鎖的粒度而定)的共享鎖,不允許對鎖定的資料進行更新(update)

(從鎖的角度講,即不允許事務獲取排他鎖,要等到所有的共享鎖都釋放掉)。反之,如果事務對資料已經具有排他鎖(只能有乙個),其他的事務就不能對鎖定的資料獲取共享鎖和排他鎖(即排他鎖與共享鎖不能相容,更多資訊請檢視鎖相容性),在此特別強調一下鎖定的資料,因為有的資料上講解到「

乙個連線寫的時候,另乙個連線可以寫

」,實際上寫的這種情況是各個連線的讀寫的資料不是相同的行,也就是說各個連線鎖定的資料不同。

根據以上分析,我們總結為六個字為「

共享讀,排他寫」。

了解了鎖的情況之後,又涉及到乙個問題。事務究竟要保持鎖多久呢?

一般來說,共享鎖的鎖定時間與事務的隔離級別有關,如果隔離級別為read committed

的預設級別,只在讀取

(select)

的期間保持鎖定,即在查詢出資料以後就釋放了鎖;如果隔離級別為更高的repeatable read

或serializable

,直到事務結束才釋放鎖。另說明,如果select

語句中指定了

holdlock

提示,則也要等到事務結束才釋放鎖。

排他鎖直到事務結束才釋放。

做出了以上分析,現在我們可能會存在這樣的疑問,到底在執行sql

語句的時候發出什麼樣的鎖呢,這就由事務的隔離級別決定了。一般情況,讀語句(select)

發出共享鎖,寫語句(update,insert,delete)

發出排他鎖。但是,如果這樣不能滿足我們的要求怎麼辦呢,有沒有更多選擇呢,別急,sqlserver

為我們提供了鎖定提示的概念。

鎖定提示對sql

語句進行特別指定,這個指定將覆蓋事務的隔離級別。下面對各個鎖定提示分別予以介紹(更多資料請檢視sqlserver

的聯機幫助),筆者做出了以下分類。

型別1①     readuncommitted:不發出鎖

②     readcommitted:發出共享鎖,保持到讀取結束

③     repeatableread:發出共享鎖,保持到事務結束

④     serializable:發出共享鎖,保持到事務結束

型別2①     nolock:不發出鎖。等同於readuncommitted

②     holdlock:發出共享鎖,保持到事務結束。等同於serializable

③     xlock:發出排他鎖,保持到事務結束。

④     updlock:發出更新鎖,保持到事務事務結束。(更新鎖:不阻塞別的事物,允許別的事物讀資料(即更新鎖可與共享鎖相容),但他確保自上次讀取資料後資料沒有被更新)

⑤     readpast:發出共享鎖,但跳過鎖定行,它不會被阻塞。適用條件:提交讀的隔離級別,行級鎖,select

語句中。

型別3①     rowlock:行級鎖

②     paglock:頁級鎖

③     tablock:表鎖

④     tablockx:表排他鎖

講解完鎖後,下面結合乙個具體例項,具體看一下鎖的使用。

在很多系統中,經常會遇到這種情況,要保持乙個編號的唯一,如會計軟體中的憑證的編號。一種編號的處理是這樣的,把表中的最大編號儲存到表中,然後在這個編號上累加,形成新的編號。這個過程對併發處理要求非常高,下面我們就來模擬這個過程,看如何保持編號的唯一性。

新建一張表code

來儲存憑證的最大編號。字段如下:編號

:bh(numeric(18,0)),

憑證表名

pinzheng(varchar(50))

假設表中有這樣的一條記錄:

bhpinzheng

18000

會計憑證

新建乙個儲存過程來生成新的憑證編號,如下:

create procedure up_getbh  as

begin tran

declare @numnewbh numeric(18,0)

select  @numnewbh = bh from code  with (updlock,rowlock) where pinzheng = '會計憑證

' set @numnewbh = @numnewbh + 1

update code set  bh = @numnewbh where pinzheng = '會計憑證

'print @numnewbh

commit tran

go然後,開啟查詢分析器,並多開幾個連線(筆者開了8

個連線,模擬有

8個人同時併發,讀者可以開更多的連線進行試驗),把類似以下這樣的語句複製到每個連線視窗中,

declare @i numeric(18,0)

set @i = 1

while @i = 1

begin

if getdate() > '2004-07-22 14:23'  --設定乙個時間,到此時間同時執行

upgetbh

儲存過程

set @i = 0      

end 

exec up_getbh

然後,接連執行各個連線,到2004-7-22 14

:23 

這一刻,各個連線同時執行

up_getbh

。從執行結果可以看出連線順序出現

18001

開始個數字,並沒有重號或丟號的現象。

分析:由於up_getbh

中的select

語句使用了更新鎖,因更新鎖之間不能相容,所以各個連線要等到所有其他的連線釋放掉鎖才能執行,而更新鎖的釋放要等到事務結束,這樣就不會發生號出錯的現象了。

附:鎖的相容性表

現有的授權模式

SQLserver鎖和事務隔離級別

隔離級別 隔離級別 髒讀不可重複讀 幻象說明 未提交讀 read uncommitted 是是 是如果其他事務更新,不管是否提交,立即執行 提交讀 read committed預設 否是 是讀取提交過的資料。如果其他事務更新沒提交,則等待 可重複讀 repeatable read 否否 是查詢期間,...

SQLserver鎖和事務隔離級別的比較與使用

來自 物件 鎖 每條sql 語句 隔離 事務 鎖 併發問題 丟失更新 未確認的讀取 髒讀 不一致的分析 非重複讀 多次讀取相同的資料 行 不一致 其他使用者更改update 幻像讀 多次讀取有不存在和新增的資料 其他使用者插入insert 或刪除delete 隔離級別 隔離級別 髒讀 不可重複讀取 ...

SQL Server(十) 事務和鎖

一 事務的特性 1 原子性 事務必須是原子性的工作單元,要麼全部執行,要麼全部不執行。2 一致性 事務完成時,必須使所有資料保持一致性。3 隔離性 由併發事務所做的修改必須與其他併發事務所做的修改隔離。由於事務在開始時就會識別資料所處的狀態,以便發生錯誤時可以回滾操作,所以另乙個併發事務要麼修改它之...