对于NOLOCK或非NOLOCK,这是个问题

Lim*_*mey 14 sql-server

这真的是一个讨论,而不是一个关于nolock的具体问题.

我最近接手了一个应用程序,几乎每个查询(并且有很多查询)都有nolock选项.现在我对SQL服务器很新(使用Oracle 10年了),但我发现这非常令人不安.因此,本周末我与一位经营着相当大的电子商务网站的朋友交谈(名字将被隐瞒以保护有罪的人),他说他必须对他所有的SQL服务器这样做,因为他总是会陷入僵局.

这只是SQL服务器的短暂下降吗?这只是数据库设计中的一个失败(我的不是第三级,但它的关闭)是否有人在那里运行没有nolocks的SQL服务器应用程序?这些是Oracle使用更多宏记录锁更好地处理的问题.

SQL服务器是否无法处理大负载?有没有比阅读未提交的数据更好的解决方法?我很想听听人们的想法.

谢谢

SQL*_*ace 13

SQL Server已snapshot isolation在SQL Server 2005中添加,这将使您仍然可以读取最新的正确值,而无需等待锁定.StackOverflow也在使用快照隔离.快照隔离级别与Oracle使用的大致相同,这就是为什么死机在Oracle机箱上不常见的原因.如果你启用它,请注意有足够的tempdb空间

来自图书在线

当READ_COMMITTED_SNAPSHOT数据库选项设置为ON时,read committed isolation使用行版本控制来提供语句级读取一致性.读操作仅需要SCH-S表级锁,不需要页锁或行锁.当READ_COMMITTED_SNAPSHOT数据库选项设置为OFF(这是默认设置)时,read committed isolation的行为与早期版本的SQL Server中的行为相同.两种实现都符合读取提交隔离的ANSI定义.


Nic*_*rey 11

如果有人说如果没有NOLOCK,他们的应用程序总会陷入僵局,那么他们的查询就会出现问题(很可能).一个僵局意味着,两笔交易不能因为资源竞争的进行,问题不能得到解决.一个例子:

考虑交易A和B.两者都在飞行中.事务A在表X中插入了一行,事务B在表Y中插入了一行,因此事务A在X上有一个独占锁,而事务B在Y上有一个独占锁.

现在,事务A需要针对表Y运行SELECT,而事务B需要针对表X运行SELECT.

这两个事务处于死锁状态:需要资源Y和B需要资源X.由于事务都不能继续进行直到另一个事务完成,因此无法解决这种情况:在另一个事务释放对资源的锁定之前,资源的事务需求都不会得到满足.争用中的资源(通过ROLLBACK或COMMIT,无关紧要.)

SQL Server识别出这种情况并选择一个事务或另一个事务作为死锁牺牲品,中止该事务并回滚,使另一个事务处于自由状态以继续其可能的完成.

死锁在现实生活中很少见(恕我直言).一个人纠正他们

  • 确保事务范围尽可能小,SQL服务器自动执行某些操作(SQL Server的默认事务范围是带有隐式COMMIT的单个语句),以及
  • 确保事务以相同的顺序访问资源.在上面的示例中,如果事务A和B都以相同的顺序锁定资源X和Y,则不会出现死锁.

超时

另一方面,当事务超过其等待时间并由于资源争用而回滚时,会发生超时.例如,事务A需要资源X.资源X被事务B锁定,因此事务A等待释放锁.如果在查询超时限制内未释放锁定,则中止等待事务并回滚.每个查询都有一个与之关联的查询超时(默认值为30秒,我相信),在此之后事务将被中止并回滚.查询超时可以设置为0,在这种情况下,SQL Server将让查询永远等待.

这可能就是他们所说的.根据我的经验,当大型批处理作业在单个事务中更新成千上万条记录时,这样的超时通常发生在大型数据库中,尽管它们可能因为事务变长而发生(在Query Abalyzer中连接到生产数据库,执行BEGIN) TRANSACTION,更新查询分析器中频繁命中的表中的单行,然后在不执行ROLLBACK或COMMIT TRANSACTION的情况下去吃午餐,看看生产DBA需要多长时间才能对你产生猿类.不要问我怎么样知道这个)

这种超时通常会导致完全无辜的SQL与各种NOLOCK提示

[ 提示:如果您要这样做,只需执行SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED作为存储过程中的第一个语句并完成它.]

这种方法的问题(NOLOCK/READ UNCOMMITTED)是你可以从其他事务中读取未提交的数据:不完整的东西或者可能在以后回滚的东西,因此你的数据完整性会受到限制.您可能会根据高水平的数据发送账单.

我的一般规则是,应尽可能避免使用表格提示.让SQL Server及其查询优化器完成他们的工作.

避免此类问题的正确方法是避免导致问题的事务类型(例如,一次插入一百万行).关系数据库SQL中隐含的锁定策略是围绕短范围的小事务设计的.锁的范围应小,持续时间短.想想"银行出纳员用押金更新某人的支票账​​户".作为基础用例.设计您的流程以在该模型中工作,您将会更加快乐.

不是在一个mondo insert语句中插入一百万行,而是在独立的块中进行工作并独立提交每个块.如果您的百万行插入在处理999,000行后死亡,则完成的所有工作都将丢失(更不用说回滚可以是ab*tch,并且在回滚期间表仍然被锁定.)如果在块中插入百万行每个1000行,在每个块之后提交,你避免了导致死锁的锁争用,因为锁将被获取和释放,并且事物将继续移动.如果在1000行的第999个块中向南移动,并且事务被中止并回滚,则仍然会插入998,000行; 你只丢了1000排工作.重启/重试更容易.

此外,锁定升级发生在大型事务中.为了提高效率,随着事务持有的锁数增加,锁会升级到越来越大的范围.如果单个事务在表中插入/更新/删除单行,则会得到行锁.继续这样做,一旦该事务对该表持有的行锁数达到阈值,SQL Server将升级锁定策略:行锁将被合并并转换为较小数量的页锁,从而增加了锁定.从那时起,单行的插入/删除/更新将锁定表中的该页面.一旦页面锁定的数量达到其阈值,页面锁定将再次合并,锁定策略将升级为表锁:事务现在锁定整个表,并且在事务提交或回滚之前,其他任何人都无法播放.

是否可以避免在功能上避免使用NOLOCK/READ UNCOMMITTED完全取决于进入底层数据库的流程的性质(以及拥有它的组织的文化).

我自己,尽量避免使用它.

希望这可以帮助.