我知道快照隔离可以解决这个问题,但我想知道在这种特殊情况下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,在这种情况下会出错吗?它可以返回:
- 不止一行,即使我问TOP 1?
- 没有行,即使存在并已提交?
- 最糟糕的是,一行不符合WHERE子句?
我已经做了很多的阅读有关本次网上,但过高或过低计数异常的唯一复制品我见过(one,two)涉及扫描。这只涉及寻求。 Jeff Atwood has a post关于使用NOLOCK产生了一个很好的讨论。我是由Rick汤森评论特别感兴趣:
其次,如果你读脏数据,运行 风险是读 完全错误的行。例如,如果 您选择读取索引来查找 你行,则更新改变 位置的行(例如:由于 页拆分或更新到 聚集索引),当你选择 去读取实际的数据行,它的 或者不再存在,或者完全不同的 行!
这是可能的只插入和没有更新?如果是这样,那么我想即使我在一张只有插入的桌子上寻找也是危险的。
更新:
我试图找出how snapshot isolation works。它似乎是基于行的,事务读取表(不带共享锁!),找到他们感兴趣的行,然后查看他们是否需要从tempdb中的版本存储中获取该行的旧版本。
但在我的情况下,没有行将有多个版本,所以版本存储似乎是没有意义的。如果找到没有共享锁的行,那么和使用NOLOCK有什么不同呢?
啊,OUTPUT子句非常酷,谢谢。我不需要它在这个特定的桌子上,但我可以考虑一些未来的代码,我可以使用它。 我明白你的观点。我已经体验过不同查询计划的影响 - 直到有足够的行才能使这两个查找比全面扫描更好,才会发生死锁。说实话,我已经倾向于快照隔离。但记录无证行为对我来说很有意思,即使我没有在生产中利用它。 :) – 2010-06-08 22:27:28
用另一个问题更新原来的帖子,谢谢。 – 2010-06-08 23:39:46
好吧,所以我同意我们不想“读取过时的非聚簇索引键并将其追加到聚簇索引中缺失的行”。问题是,这是否会在没有任何行的UPDATE的情况下实际发生?我所做的只是插入,因此只有元数据将被更新,而不是任何行中的数据。我以为SQL Server会有一些低级别的一致性机制来防止元数据损坏,即使使用NOLOCK(这是一个相当高层次的概念)..? – 2010-06-09 00:39:46