[SQL SERVER][Performance]小心使用With NoLock

[SQL SERVER][Performance]小心使用With NoLock

在論壇上看到一則關於With NoLock發問,我不確定發問者是否常態使用NoLock,

但以前經驗告訴我要謹慎使用NoLock,以下就讓我娓娓道來..

 

2003年微軟有篇關於NoLock的記載,標題如下

NOLOCK 最佳化工具提示可能會造成暫時性的損毀錯誤在 SQL Server 錯誤記錄檔中

 

image

當時我相當在意圈選中的文字。

 

當你使用NoLock時,你等於是告訴SQL Server 使用者不在意資料正確性和一致性,

假設某位使用者正在更新資料表,就會影響其他使用者查詢(with nolock)該資料的正確性和一致性,

那些查詢的使用者可能會遇到重複讀取相同資料、遺漏讀取或中途讀取..等狀況,

而我更在意頁面分割的動作

(頁面分割這裡不多敘述,但你可以參考2011年4月 RUN!PC我所發表的索引概念和設計)

我只和你說頁面分割是相當耗費系統資源的動作,

你絕對不希望使用者再查詢資料時,還得同時處理頁面分割動作,

所以無論如何請一定要減少頁面分割發生的頻率。

 

Note:

NoLock和ReadUnCommitted效果一樣。

 

create table mytest
(id int identity not null,
data uniqueidentifier default(newid()) not null 
)
--新增資料
declare @i int;set @i=1;
while @i<=30000
begin
insert mytest default values  
set @i=@i+1
end
--建立唯一叢集索引
create unique clustered index cidx on mytest( data )
--確認使用空間
exec sp_spaceused mytest
go

 

 

 

 

 

 

image

 

現在我來模擬一下並行效果

Connection1執行以下Script1

declare @totalrows int
,@currentnow int
,@errorcount tinyint
 
set @errorcount = 0
select @totalrows= count(1) from mytest 

	
while 1 = 1
begin
    --waitfor delay '00:00:00.200'
    select @currentnow= count(1) from mytest WITH(NOLOCK,INDEX = cidx )  
    if @totalrows <> @currentnow
    begin
        print '現在查詢總筆數= ' + cast( @currentnow as varchar(10) ) + 
        ' 差異筆數= ' + cast( @currentnow - @totalrows as varchar(10) )
        set @errorcount = @errorcount + 1
        if @errorcount >= 8
            break
    end
end

	

Connection2執行以下Script2

begin tran mytran
update mytest
    set data = newid()

 

 

 

 

 

結果:

image

 

Script1中沒有任何的新增或刪除,但查詢總筆數竟然會得到不同的值,

這些情形就是我前面提到的重複讀取或遺漏讀取..等,

雖然Nolock大部分可以避開Blocking(封鎖)問題,

但Nolock卻也帶來另外一個更麻煩的問題,還請小心服用。

 

參考

並行效果

資料表提示 (Transact-SQL)