我知道快照隔离可以解决这个问题,但我想知道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 (Date,Id;Value)上的锁,然后是Ix (Id,Date),而SELECT获取的是Ix (Id,Date)上的锁,然后是Cx (Date,Id;Value)。这是因为SELECT首先在Ix上查找,然后加入到Cx上的查找。
交换聚集索引和非聚集索引将打破此循环,但这不是一个可接受的解决方案,因为它将引入具有其他(更复杂)SELECT的循环。
如果我将NOLOCK添加到SELECT,在这种情况下会出错吗?它可以返回:
多行,即使我要求前1行?
没有行,即使存在行并且已经提交?
最糟糕的是,行不满足WHERE子句?
我在网上读了很多关于这方面的文章,但我所见过的计数过多或计数不足的异常情况的唯一复制品(
一个
两个
)涉及扫描。这只涉及到寻道。杰夫·阿特伍德
有一篇文章
关于使用NOLOCK的讨论引起了很好的讨论。我特别感兴趣的是Rick Townsend的评论:
其次,如果您读取脏数据,那么您将面临读取完全错误的行的风险。例如,如果您的select读取一个索引来查找您的行,那么update将更改行的位置(例如:由于页面拆分或对聚集索引的更新),当您的select读取实际数据行时,它要么不再存在,要么完全不同的行!
是否可以只使用插入,而不使用更新?如果是这样的话,我想即使是我在一个只插入的表上的查找也可能是危险的。
更新:
我在试着弄清楚
快照隔离的工作原理
(
https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2005/administrator/ms345124(v=sql.90%29)
..。它似乎是基于行的,事务读取表(没有共享锁!),找到它们感兴趣的行,然后查看是否需要从tempdb中的版本存储中获取该行的旧版本。
但在我的例子中,所有行都不会有一个以上的版本,所以版本存储看起来相当没有意义。如果找到没有共享锁的行,与只使用NOLOCK有什么不同?
发布于 2010-06-09 06:14:41
使用NOLOCK或READ UNCOMMITTED意味着您放弃了任何一致性保证。句号。
如果你需要一致性,不要做脏读。您的整个解释依赖于未记录的行为,在未来的版本中可能会发生更改,更糟糕的是,在
特定的访问计划
正如您对查询所期望的那样。查询优化器可以自由选择它认为合适的任何计划,并且您所做的任何假设都可能在生产中被打破。所以回到原点:如果你没有准备好面对后果,就不要做肮脏的阅读。
不确定这是否适用,也不清楚您试图通过查询/表实现什么,但也许这篇文章可能会有所帮助:
将表用作队列
..。
更新
其中NOLOCK读取将读取不一致的状态(例如,读取过时的非聚集索引键并将其追逐到聚集索引中缺少的行),快照读取将在版本存储中找到“缺少的”行。对于稳定的数据,快照读取与nolock读取相同。每当数据发生变化(未提交的更新)时,版本存储的魔力就开始发挥作用,因为快照读取进入版本存储并找到‘旧’值(稳定和一致),其中nolock读取将变得混乱,并将指针追逐到lala land。
发布于 2010-06-09 05:56:31
在这种情况下,使用NOLOCK应该是安全的。另一个想法是:在Ix索引上添加一个包含列的值应该可以消除在Cx上的查找。
create nonclustered index Ix on Data (Id, Date) include (Value)https://stackoverflow.com/questions/3001531
复制相似问题