[SQL SERVER][Performance]小心使用With NoLock
在論壇上看到一則關於With NoLock發問,我不確定發問者是否常態使用NoLock,
但以前經驗告訴我要謹慎使用NoLock,以下就讓我娓娓道來..
2003年微軟有篇關於NoLock的記載,標題如下
NOLOCK 最佳化工具提示可能會造成暫時性的損毀錯誤在 SQL Server 錯誤記錄檔中
當時我相當在意圈選中的文字。
當你使用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
現在我來模擬一下並行效果
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()
結果:
Script1中沒有任何的新增或刪除,但查詢總筆數竟然會得到不同的值,
這些情形就是我前面提到的重複讀取或遺漏讀取..等,
雖然Nolock大部分可以避開Blocking(封鎖)問題,
但Nolock卻也帶來另外一個更麻煩的問題,還請小心服用。
參考
並行效果
資料表提示 (Transact-SQL)