2

我知道快照隔离可以解决这个问题,但我想知道在这种特殊情况下NOLOCK是否安全,这样我可以避免开销。在这种情况下读取未提及/解锁安全吗?

我有一个表,看起来是这样的:

drop table Data 

create table Data 
(
    Id BIGINT NOT NULL, 
    Date BIGINT NOT NULL, 
    Value BIGINT, 
    constraint Cx primary key (Date, Id) 
) 

create nonclustered index Ix on Data (Id, Date) 

有表中没有更新,永远。删除可能发生,但他们不应该与SELECT竞争,因为它们会影响表的另一个较旧的末端。插入是常规的,页面拆分到(Id,Date)索引是非常普遍的。

我有一个标准的INSERT和SELECT看起来像这样之间的死锁情况:

select top 1 Date, Value from Data where Id = @p0 order by Date desc 

因为INSERT获取关于CX锁(日期,身份证;值),然后IX(ID,日期),但SELECT会获取Ix(Id,Date)和Cx(Date,Id; Value)上的锁定。这是因为SELECT首先在Ix上寻找,然后加入到Cx上寻找。

交换聚集索引和非聚集索引会打破这个循环,但它不是一个可接受的解决方案,因为它会引入具有其他(更复杂)SELECT的循环。

如果我将NOLOCK添加到SELECT,在这种情况下会出错吗?它可以返回:

  1. 不止一行,即使我问TOP 1?
  2. 没有行,即使存在并已提交?
  3. 最糟糕的是,一行不符合WHERE子句?

我已经做了很多的阅读有关本次网上,但过高或过低计数异常的唯一复制品我见过(onetwo)涉及扫描。这只涉及寻求。 Jeff Atwood has a post关于使用NOLOCK产生了一个很好的讨论。我是由Rick汤森评论特别感兴趣:

其次,如果你读脏数据,运行 风险是读 完全错误的行。例如,如果 您选择读取索引来查找 你行,则更新改变 位置的行(例如:由于 页拆分或更新到 聚集索引),当你选择 去读取实际的数据行,它的 或者不再存在,或者完全不同的 行!

这是可能的只插入和没有更新?如果是这样,那么我想即使我在一张只有插入的桌子上寻找也是危险的。


更新:

我试图找出how snapshot isolation works。它似乎是基于行的,事务读取表(不带共享锁!),找到他们感兴趣的行,然后查看他们是否需要从tempdb中的版本存储中获取该行的旧版本。

但在我的情况下,没有行将有多个版本,所以版本存储似乎是没有意义的。如果找到没有共享锁的行,那么和使用NOLOCK有什么不同呢?

回答

7

使用NOLOCK或READ UNCOMMITTED意味着您放弃任何一致性的保证。期。

如果你需要一致性,不要做脏读。您的整个解释依赖于未记录的行为,未来版本中可能会发生更改,更糟糕的是,您希望查询的的特定访问计划为。查询优化器可以自由选择任何它认为合适的计划,并且任何假设都可能在生产中被破坏。所以回到原点:如果你不准备面对后果,不要做脏读。

不知道这是否适用,不清楚你试图用你的查询/表达到什么,但也许这篇文章可能有所帮助:Using tables as Queues

更新
凡NOLOCK读会读不一致的状态(例如,读一个陈旧的非聚集索引键,它追缺失的行聚集索引)快照读会找到“失踪”在版本商店中排。对于稳定的数据,快照读取与nolock读取相同。每当数据发生变化(未提交更新)时,版本存储的魔力就会发挥作用,因为快照读取进入版本存储并发现nolock读取会失控并追逐指针的'旧'值(稳定且一致)啦啦土地。

+0

啊,OUTPUT子句非常酷,谢谢。我不需要它在这个特定的桌子上,但我可以考虑一些未来的代码,我可以使用它。 我明白你的观点。我已经体验过不同查询计划的影响 - 直到有足够的行才能使这两个查找比全面扫描更好,才会发生死锁。说实话,我已经倾向于快照隔离。但记录无证行为对我来说很有意思,即使我没有在生产中利用它。 :) – 2010-06-08 22:27:28

+0

用另一个问题更新原来的帖子,谢谢。 – 2010-06-08 23:39:46

+0

好吧,所以我同意我们不想“读取过时的非聚簇索引键并将其追加到聚簇索引中缺失的行”。问题是,这是否会在没有任何行的UPDATE的情况下实际发生?我所做的只是插入,因此只有元数据将被更新,而不是任何行中的数据。我以为SQL Server会有一些低级别的一致性机制来防止元数据损坏,即使使用NOLOCK(这是一个相当高层次的概念)..? – 2010-06-09 00:39:46

1

在这种情况下,您应该使用NOLOCK进行安全操作。另外一个想法是:在Ix索引中添加Value作为包含的列应该消除Cx上的查找。

create nonclustered index Ix on Data (Id, Date) include (Value) 
+0

鉴于上述担忧,您可否详细说明为什么您认为它是安全的?另外,我已经考虑在索引中包含非关键字列,但实际上它们相当广泛(其中不止一个),并且会占用大量空间。重复可能会将更多有用的东西从缓存中取出。 – 2010-06-08 22:08:59