[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)
原文:大专栏 [SQL SERVER][Performance]小心使用With NoLock