数据库写查询语句的时候,为了提升查询性能,往往会在查询的表后面加一个nolock,或者是with(nolock),其目的就是查询的时候是不锁定表,从而提高查询速度的目的。但如果同一时间有多个用户访问同一资源的时候,如果并发用户对该资源做了修改。则会对其他用户访问该数据造成数据不一致的情况。主要一个用户对一个资源做了修改,此时另外一个用户正好读取了这条被修改的记录,然后,第一个用户放弃修改,数据回到修改之前,这两个不同的结果就是脏读。一个用户的一个操作是一个事务,这个事务分两次读取同一条记录,如果第一次读取后,有另外用户修改了这个数据,然后第二次读取的数据正好是其它用户修改的数据,这样造成两次读取的记录不同,如果事务中锁定这条记录就可以避免。指用户读取一批记录的情况,用户两次查询同一条件的一批记录,第一次查询后,有其它用户对这批数据做了修改,方法可能是修改,删除,新增,第二次查询时,会发现第一次查询的记录条目有的不在第二次查询结果中,或者是第二次查询的条目不在第一次查询的内容中。NOLOCK 语句执行时不发出共享锁,允许脏读 ,等于 READ UNCOMMITTED事务隔离级别 。nolock确实在查询时能提高速度,但它并不是没有缺点的,起码它会引起脏读、只适用于select查询语句。在一些不需要考虑脏读的场合会用到,例如当用户在论坛发广告贴时删除其所有发帖,这个查询就不怕脏读,全删,或者漏一个正在发的都不是问题。为了演示两个事务死锁的情况,我们下面的测试都需要在SQL Server Management Studio中打开两个查询窗口。保证事务不被干扰。 1、 没有提交的事务,NOLOCK 和 READPAST处理的策略:CREATE TABLE t1 (c1 int IDENTITY(1,1), c2 int)select count(*) from t1 WITH(NOLOCK)select count(*) from t1 WITH(READPAST) 查询窗口一的命令没有提交事务,所以 READPAST 不会计算没有提交事务的这一条记录,这一条被锁住了,READPAST 看不到;而NOLOCK则可以看到被锁住的这一条记录。 select count(*) from t1 就会看到这个执行很久不能执行完毕,因为这个查询遇到了一个死锁。清除掉这个测试环境,需要在查询窗口一中再执行如下语句:演示二:对被锁住的记录,NOLOCK 和 READPAST处理的策略CREATE TABLE t2 (UserID int , NickName nvarchar(50))insert t2(UserID,NickName) values(1,'lucas')insert t2(UserID,NickName) values(2,'fuckcpp')update t2 set NickName = 'fuckcpp.net' where UserID = 2 select * from t2 WITH(NOLOCK) where UserID = 2select * from t2 WITH(READPAST) where UserID = 2查询窗口二中, NOLOCK 对应的查询结果中我们看到了修改后的记录,READPAST对应的查询结果中我们没有看到任何一条记录。这种情况下就可能发生脏读1、数据量特别大的表,牺牲数据时效性来提升性能是可以考虑的;2、允许出现脏读现象的业务逻辑,反之一些数据完整性要求比较严格的场景就不合适了,像电商、金融方面等。3、数据不经常修改的表,这样会省掉锁定表的时间来加快查询速度。4、当使用NoLock时,它允许阅读那些已经修改但是还没有交易完成的数据。因此如果有需要考虑transaction事务数据的实时完整性时,使用WITH (NOLOCK)就要好好考虑一下。 IT技术分享社区
|