SQL查询优化,nolock问题

在作过的不少项目中,发现你们无论对什么表,逢select一定加上nolock(或者with(nolock)),好像已是制度化的一种东西。有领导高人解释说加上nolock能够提升查询速度,不影响对数据表的其余并发操做。  
可是真有必要每一个查询都加nolock吗?我的认为加不加nolock仍是值得咱们根据实际状况斟酌一番的(至少须要知其然而后知其因此然吧)。下面就来简单分析一下加不加nolock以及加了nolock对实际查询的一些影响。
1、重要概念
(此处沉思5秒,安静回想经典数据库教科书里的一些重用概念。嗯......什么,你也想不全了?那好吧,别闲烦,道理是要讲的,书是不得不参考的(bs直接抄书的))
并发访问:同一时间有多个用户访问同一资源。若是并发用户中有其余用户同时对资源进行了修改,这样对同一数据的访问就会出现“所见不是所得”的状况,从而对其它用户产生某些不利的影响,包括:
1:脏读:有一个用户对某一个资源作了修改,此时另一个用户正好读取了这条被修改的记录,而后第一个用户又放弃了修改,数据还原到修改以前,这两个不一样的结果就是脏读。程序员

2:幻读:特指用户读取一批记录的状况。用户两次查询同一条件的一批记录,第一次查询后,有其它用户对这批数据作了修改,方法多是insert,update或delete,第二次查询时,用户会发现第一次查询的记录条目有的不在第二次查询结果中,或者是第二次查询的条目不在第一次查询的内容中,形成先后查询结果的不一致。
3:不可重复读:系统中某一个用户的一个操做是一个事务,这个事务分两次读取同一条记录。若是第一次读取后,正好有另一个用户修改了这条记录,而后第二次读取的正好是以前进行修改记录的那位用户的数据,这样就有可能形成两次读取的数据不一样。固然若是咱们在事务中锁定这条记录就能够避免。sql


2、如何消除并发访问的不利影响
如前所述,既然并发访问会形成这么多不利影响,咱们又该如何解决呢?估计通常程序员的下意识反应就是像咱们在控制多线程并发编程的时候同样,加锁,lock一下,over。没错,还真不能说你说的不对!真是聪明又幸福的程序员啊!
其实在ms的Sql Server中,有两种并发访问的控制机制:锁和行版本控制,关于并发控制,ms的阐述和解决方案真是详细而周到。你不得不pf咱们的ms是多么的亲妈啊,真的什么都帮咱们想好而且作好了。
先分析一下数据库的锁。
小抄一段参考书上的:数据库

一、锁:“每一个事务对所依赖的资源会请求不一样类型的锁,它能够阻止其余事务以某种可能会致使事务请求锁出错的方式修改资源。当事务再也不依赖锁定的资源时,锁将被释放”。 从数据库系统的角度来看:咱们能够把锁分为共享锁、独占锁(排它锁)和更新锁:
(1)、共享 (S) :用于不更改或不更新数据的操做(只读操做),好比咱们常见的select语句等。
(2)、更新 (U) :用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。
(3)、排它 (X) :用于数据修改操做,例如insert、update或delete。确保不会同时对同一资源进行多重更新。编程

对于如此看似简单其实重要繁琐的东西,固然不能让庞大的程序员群体去设置或控制它们。Sql Server经过设置事务的隔离级别自动管理锁的设置和控制。锁管理器经过查询分析器分析待执行的sql语句,进而来判断这些sql语句将会访问哪些资源,进行什么操做,而后结合设定的隔离级别自动分配管理须要用到的锁。安全

下面接着来了解一下行版本控制。
二、行版本控制:
还用想吗?小抄一下:
(1)、简介
“ 行版本控制的隔离是Sql Server 2005一个新的隔离框架。使用行版本控制的隔离能够在大量并发的状况下,显著减小所得产生,而且与nolock相比,它又能够显著下降肮脏读,幻影,丢失更新等现象的发生(READ_COMMITTED_SNAPSHOT)。当在基于行版本控制的隔离下运行的事务读取数据时,读取操做不会获取正被读取的数据上的共享锁(S 锁),所以不会阻塞正在修改数据的事务。另外,锁定资源的开销随着所获取的锁的数量的减小降至最低。使用行版本控制的已提交读隔离和快照隔离能够提供副本数据的语句级或事务级读取一致性”。
(2)、原理
“Sql Server 2005的行版本控制原理上很简单(不说不知道,笔者注),就是在库表中每一行的记录上都悄悄的增长了一个类时间戳列(行版本列)。当使用行版本控制的隔离时,Sql Server 2005 Database Engine 向使用行版本控制操做数据的每一个事务分配一个事务序列号 (XSN)。事务在执行 BEGIN TRANSACTION 语句时启动。可是,事务序列号在执行 BEGIN TRANSACTION 语句后的第一次读/写操做时开始增长。事务序列号在每次分配时都增长1。当事务执行时,Sql Server根据行版本列,来提供的行的相应版本。而Sql Server将维护全部在数据库中执行的数据修改的逻辑副本(版本)。特定的事务每次修改行时,数据库引擎 实例都存储之前提交的 tempdb 中行的图像版本。每一个版本都标记有进行此更改的事务的事务序列号。已修改行的版本使用连接列表连接在一块儿。最新的行值始终存储在当前的数据库中并连接至版本存储区 tempdb 中存储的版本。(修改大型对象 (LOB) 时,只有已更改的片断才会复制到 tempdb 中的版本存储区,  对于短时间运行的事务,已修改行的版本将可能保存在缓冲池中,而不会写入 tempdb 数据库的磁盘文件中。若是只是临时须要副本行,它将只是简单地从缓冲池中删除而不会引起 I/O 开销。)”
(3)、优点
使用行版本控制的隔离级别具备如下优势:
  a、读取操做检索一致的数据库快照;
  b、select语句在读取操做过程当中不锁定数据(读取器不阻塞编写器,编写器也不阻塞读取器);
  c、select语句能够在其余事务更新行时访问最后提交的行值,而不阻塞应用程序;
  d、死锁的数量减小;
  e、事务所需的锁的数量减小,这减小了管理锁所需的系统开销;
       f、锁升级的次数减小。
(4)、行版本控制小结:
当启用了基于行版本控制的隔离级别时,数据库引擎将维护修改的每一行的版本。应用程序能够指定事务使用行版本查看事务或查询开始时存在的数据,而不是使用锁保护全部读取。经过使用行版本控制,读取操做阻止其余事务的可能性将大大下降,也就是至关于针对全部的表在查询时都会加上nolock。虽然一样会产生脏读的现象,但差异在于咱们不用每次查询都加上nolock,行版本控制策略默认的一个设置就帮咱们搞定了。
BTW,既然说到了基于行版本控制的隔离级别,不得不说下隔离级别。隔离级别,怎么说呢?您别不怀好意地笑,抄书ing:
<1>、用处:控制锁的应用,即什么场景应用什么样的锁机制,解决并发处理带来的种种问题;;
<2>、分类:多线程

  a、未提交读(UnCommitted Read):悲观,至关于(nolock;隔离事务的最低级别,只能保证不读取物理上损坏的数据。
b、已提交读(Read Committed):悲观,数据库引擎的缺省模式,读操做共享锁时间一直到读取结束。
c、可重复读(Repeatable Read):悲观,读操做共享锁时间比已提交读模式更长,一直到事务结束。
d、可序列化(Serializable):悲观,至关于(HoldLock),最严谨。
e、已提交读快照(Read Committed Snapshot):乐观,2005新增,基于行版本控制,全部读操做不受其余锁的影响,历史数据保存更短,Temp空间更少,支持分布式。
Alter Database 数据库名称 Set Read_Committed_Snapshot On
f、快照(Snapshot):乐观,2005新增,基于行版本控制,全部读操做不受其余锁的影响,历史数据保存更长,Temp空间更多,不支持分布式。
Alter Database 数据库名称 Set Allow_Snapshot_Isolation On并发

 <3>、查看当前隔离模式和行版本控制状态 (2005)框架


DBCC UserOptions
Select name, snapshot_isolation_state, snapshot_isolation_state_desc, is_read_committed_snapshot_on From sys.databases分布式

 三、小结
根据前面的分析,咱们知道,Sql Server 2005控制并发访问已经有了两种有效的途径;nolock语句执行时不发出共享锁,容许脏读 ,等于READ UNCOMMITTED事务隔离级别,从这种意义上来说,nolock确实在查询的时候能提升速度。但如今咱们再来问一下本身,nolock须要加吗,不须要加吗?真的须要加吗,真的不须要加吗??您能再确定点回答吗?性能

3、nolock的适用场景(下面的几点彻底是我的意见,能够54。)
一、“持久化”的表:也就是数据不会常常变更的表,好比咱们熟知的省、市、县和航空公司、机场等等。它们的共同特征就是至少从目前来看,这些数据长时间不会有任何改变。其实从长远来看,甚至一个很是成熟的公司的部门表也能够做为这类数据来处理,可是和部门有关系的员工表就不能够;
二、容许脏读的一些业务逻辑:这个没什么好说的,客户需求决定了你不在这上面“较真”。好比咱们要查询某个业务部门某一个季度或某一年的业绩统计,须要了解大概状况就能够了。这种情形下,查询nolock多少次都无所谓。
三、存储了海量数据的表:这个毫无疑问,数据量大,重要性越强,访问也就越多,并发操做影响到的记录也就可能越大,所谓“树大招风”,不过如此。咱们给查询加上nolock能够大大提高性能和用户体验,固然,它是以牺牲数据一致性和安全性来提高性能的。
最后,经过以上分析,咱们得出结论,查询(尤为是海量数据)不加锁,毫无疑问,速度确确实实是提升了,可是咱们更应该有选择性的挑选最适合的表来使用nolock。由于咱们已经都知道,“对数据表的并发操做”极可能形成一些查询结果的困扰,好比咱们所熟知的“脏读“。设想一下吧,对于没有预期的一些查询(所谓”预期查询“,就是使用者认为先后查询结果不一致也是合理的,好比订单查询中一个订单的订单状态的变化致使先后结果不一致等等),由于”脏读“形成的”脏数据“先后查询结果不一致,一次两次也就罢了,可能使用的人觉得本身眼花了仍是怎么的。可是若是屡次或者大数据量地出现数据不匹配,确定会让不明因此的使用者困惑,心理素质好的会习惯性地把问题推给系统,内心素质很差的的还觉得本身误操做仍是怎么的,直接形成恐慌甚至怀疑本身rp。

主要示例:  SELECT  * FROM  t_BOS220000001 with(nolock)(有关键字with和没有关键字with都没有关系)nolock关键字紧跟着表名后面,  SELECT  * FROM  t_BOS220000001 (nolock)这样也能够或是  update table(nolock)set a='''' where id=1

 

本文转载自:http://youzhangcai.blog.163.com/blog/static/1668481842010111782534757/

相关文章
相关标签/搜索