装载 三篇 关于WITH (NOLOCK)的使用,都写得不错,
-->1.http://www.cnblogs.com/hsapphire/archive/2010/07/29/1787878.html
大家在写查询时,为了性能,往往会在表后面加一个nolock,或者是with(nolock),其目的就是查询是不锁定表,从而达到提高查询速度的目的。
什么是并发访问:同一时间有多个用户访问同一资源,并发用户中如果有用户对资源做了修改,此时就会对其它用户产生某些不利的影响,例如:
1:脏读,一个用户对一个资源做了修改,此时另外一个用户正好读取了这条被修改的记录,然后,第一个用户放弃修改,数据回到修改之前,这两个不同的结果就是脏读。
2:不可重复读,一个用户的一个操作是一个事务,这个事务分两次读取同一条记录,如果第一次读取后,有另外用户修改了这个数据,然后第二次读取的数据正好是其它用户修改的数据,这样造成两次读取的记录不同,如果事务中锁定这条记录就可以避免。
3:幻读,指用户读取一批记录的情况,用户两次查询同一条件的一批记录,第一次查询后,有其它用户对这批数据做了修改,方法可能是修改,删除,新增,第二次查询时,会发现第一次查询的记录条目有的不在第二次查询结果中,或者是第二次查询的条目不在第一次查询的内容中。
为什么会在查询的表后面加nolock标识?为了避免并发访问产生的不利影响,SQL Server有两种并发访问的控制机制:锁、行版本控制,表后面加nolock是解决并发访问的方案之一。
1> 锁,每个事务对所依赖的资源会请求不同类型的锁,它可以阻止其他事务以某种可能会导致事务请求锁出错的方式修改资源。当事务不再依赖锁定的资源时,锁将被释放。
锁的类型:1:表类型:锁定整个表;2:行类型:锁定某个行;3:文件类型:锁定某个数据库文件;4:数据库类型:锁定整个数据库;5:页类型:锁定8K为单位的数据库页。
锁的分类还有一种分法,就是按用户和数据库对象来分:
1). 从数据库系统的角度来看:分为独占锁(即排它锁),共享锁和更新锁
1:共享 (S) :用于不更改或不更新数据的操作(只读操作),一般常见的例如select语句。
2:更新 (U) :用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。
3:排它 (X) :用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。
2). 从程序员的角度看:分为乐观锁和悲观锁。
1:乐观锁:完全依靠数据库来管理锁的工作。
2:悲观锁:程序员自己管理数据或对象上的锁处理。
一般程序员一看到什么锁之类,觉的特别复杂,对专业的DBA当然是入门级知识了。可喜的是程序员不用去设置,控制这些锁,SQLServer通过设置事务的隔离级别自动管理锁的设置和控制。锁管理器通过查询分析器分析待执行的sql语句,来判断语句将会访问哪些资源,进行什么操作,然后结合设定的隔离级别自动分配管理需要用到的锁。
2>:行版本控制:当启用了基于行版本控制的隔离级别时,数据库引擎 将维护修改的每一行的版本。应用程序可以指定事务使用行版本查看事务或查询开始时存在的数据,而不是使用锁保护所有读取。通过使用行版本控制,读取操作阻止其他事务的可能性将大大降低。也就是相当于针对所有的表在查询时都会加上nolock,同样会产生脏读的现象,但差别在于在一个统一管理的地方。说到了基于行版本控制的隔离级别,这里有必要说下隔离级别的概念。
隔离级别的用处:控制锁的应用,即什么场景应用什么样的锁机制。
最终目的:解决并发处理带来的种种问题。
隔离级别的分类:
1:未提交读,隔离事务的最低级别,只能保证不读取物理上损坏的数据;
2:已提交读,数据库引擎的默认级;
3:可重复读;
4:可序列化;隔离事务的最高级别,事务之间完全隔离。
小结:NOLOCK 语句执行时不发出共享锁,允许脏读 ,等于 READ UNCOMMITTED事务隔离级别 。nolock确实在查询时能提高速度,但它并不是没有缺点的,起码它会引起脏读。
nolock的使用场景(个人观点):
1:数据量特别大的表,牺牲数据安全性来提升性能是可以考虑的;
2:允许出现脏读现象的业务逻辑,反之一些数据完整性要求比较严格的场景就不合适了,像金融方面等。
3:数据不经常修改的表,这样会省于锁定表的时间来大大加快查询速度。
综上所述,如果在项目中的每个查询的表后面都加nolock,这种做法并不科学,起码特别费时间,不如行版本控制来的直接有效。而且会存在不可预期的技术问题。应该有选择性的挑选最适合的表来放弃共享锁的使用。
最后说下nolock和with(nolock)的几个小区别:
1:SQL05中的同义词,只支持with(nolock);
2:with(nolock)的写法非常容易再指定索引。
跨服务器查询语句时 不能用with (nolock) 只能用nolock
同一个服务器查询时 则with (nolock)和nolock都可以用
比如
- SQL code
-
select * from [IP].a.dbo.table1 with (nolock) 这样会提示用错误select * from a.dbo.table1 with (nolock) 这样就可以
-->2.http://www.cnblogs.com/henw/archive/2011/07/22/2113580.html
缺点:
1.会产生脏读
2.只适用与select查询语句
优点:
1.有些文件说,加了WITH (NOLOCK)的SQL查询效率可以增加33%。
2.可以用于inner join 语句
脏读: 一个用户对一个资源做了修改,此时另外一个用户正好读取了这条被修改的记录,然后,第一个用户放弃修改,数据回到修改之前,这两个不同的结果就是脏读。
详细内容:
要提升SQL的查询效能,一般来说大家会以建立索引(index)为第一考虑。其实除了index的建立之外,当我们在下SQL Command时,在语法中加一段WITH (NOLOCK)可以改善在线大量查询的环境中数据集被LOCK的现象藉此改善查询的效能。
不过有一点千万要注意的就是,WITH (NOLOCK)的SQL SELECT有可能会造成Dirty Read(脏读)。
例如:
FROM EMPLOYEE WITH (NOLOCK)
JOIN WORKING_GROUP WITH (NOLOCK)
ON EMPLOYEE.UserID = WORKING_GROUP.UserID
除了简单的SELECT之外,有JOIN的SELECT语法也是可以使用的。但是DELETE、INSERT、UPDATE这些需要transaction的指令就不行了…
有些文件说,加了WITH (NOLOCK)的SQL查询效率可以增加33%。
加了WITH (NOLOCK)即告诉SQL Server,我们的这段SELECT指令无需去考虑目前table的transaction lock状态,因此效能上会有明显的提升,而且数据库系统的Lock现象会有明显的减少(包含Dead Lock)。
有 一点要特别注意,因为WITH (NOLOCK)不考虑目前table的transaction lock,因此当有某些资料正处于多个phase交易(例如跨多个table的transaction交易-->如提款系统),WITH (NOLOCK)会让目前处理交易process的数据被忽略…
讲白话一点,也就是说当使用NoLock时,它允许阅读那些已经修改但是还没有交易完成的数据。因此如果有需要考虑transaction事务数据的实时完整性时,使用WITH (NOLOCK)就要好好考虑一下。
如果不需考虑transaction,WITH (NOLOCK)或许是个好用的参考。
注1:WITH ( < table_hint > )
指定由查询优化器使用的表扫描、一或多个索引,
或由查询优化器利用此数据表以及为此语句使用锁定模式。
注2:WITH (NOLOCK)相当于READ UNCOMMITTED
-->3.http://www.dotblogs.com.tw/ricochen/archive/2011/04/15/22758.aspx
在論壇上看到一則關於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卻也帶來另外一個更麻煩的問題,還請小心服用。