我知道快照隔离可以解决这个问题,但是我想知道在这种特殊情况下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(日期,Id;值)然后Ix(Id,日期)的锁定,但SELECT获取Ix(Id,Date)上的锁定然后Cx(日期,Id;值) 。这是因为SELECT首先寻找Ix然后加入到Cx上的搜索。
交换聚簇索引和非聚簇索引会破坏此循环,但这不是一个可接受的解决方案,因为它会引入其他(更复杂的)SELECT循环。
如果我将NOLOCK添加到SELECT中,在这种情况下会出错吗?可以回复:
我已经在线阅读了很多这方面的内容,但我看到的(one,two)过度或不足数异常的复制涉及扫描。这只涉及寻求。 Jeff Atwood has a post关于使用NOLOCK进行了很好的讨论。我对Rick Townsend的评论特别感兴趣:
其次,如果你读了脏数据,那么 你跑的风险就是阅读 完全错误的排。例如,如果 您的选择读取要查找的索引 你的行,然后更新更改 行的位置(例如:由于a 页面拆分或更新 聚集索引),当你选择 去读实际的数据行,就是这样 要么不再存在,要么不同 一共排!
这是否可以仅使用插入,而且没有更新?如果是这样,那么我想即使是我对仅插入表的搜索也可能是危险的。
更新
我想弄明白how snapshot isolation works。它似乎是基于行的,其中事务读取表(没有共享锁!),找到他们感兴趣的行,然后看看他们是否需要从tempdb中的版本存储中获取旧版本的行。
但在我的情况下,没有行会有多个版本,因此版本存储似乎毫无意义。如果找到没有共享锁的行,那么仅使用NOLOCK有什么不同?
答案 0 :(得分:7)
使用NOLOCK或READ UNCOMMITTED意味着您放弃任何一致性保证。周期。
如果您需要一致性,请不要进行脏读。您的整个解释依赖于未记录的行为,可能会在将来的版本中发生变化,更糟糕的是依赖于您希望查询的特定访问计划。查询优化器可以自由选择它认为合适的任何计划,并且您在生产中可能会破坏您所做的任何假设。所以回到原点:如果你不准备面对后果,不要做脏读。
不确定这是否适用,不清楚您尝试使用查询/表格实现的目标,但也许这篇文章可能有所帮助:Using tables as Queues。
<强>更新强>
如果NOLOCK读取将读取不一致状态(例如,读取陈旧的非聚集索引键并将其追逐到聚簇索引中的缺失行),则快照读取将在版本存储中找到“缺失”行。对于稳定数据,快照读取与nolock读取相同。每当数据发生变化(未提交更新)时,版本存储的神奇之处就会发挥作用,因为快照读取进入版本存储并找到“旧”值(稳定且一致),其中nolock读取将变得混乱并追逐指针lala land。
答案 1 :(得分:1)
在这种情况下你应该对NOLOCK安全。另外一个想法:在Ix索引上添加Value作为包含列应该消除对Cx的搜索。
create nonclustered index Ix on Data (Id, Date) include (Value)