Sql server 在这种情况下,READ UNCOMMITTED/NOLOCK安全吗?

Sql server 在这种情况下,READ UNCOMMITTED/NOLOCK安全吗?,sql-server,sql-server-2005,deadlock,read-committed-snapshot,Sql Server,Sql Server 2005,Deadlock,Read Committed Snapshot,我知道快照隔离可以解决这个问题,但我想知道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 (

我知道快照隔离可以解决这个问题,但我想知道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 top 1 Date, Value from Data where Id = @p0 order by Date desc
该表从未更新过。删除可能会发生,但它们不应与SELECT冲突,因为它们会影响表的另一端。插入是常规的,并且到(Id,Date)索引的页面拆分非常常见

标准插入和选择之间出现死锁情况,如下所示:

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 top 1 Date, Value from Data where Id = @p0 order by Date desc
因为INSERT先获取Cx(日期,Id;值)的锁,然后获取Ix(Id,日期),但SELECT先获取Ix(Id,日期)的锁,然后获取Cx(日期,Id;值)。这是因为SELECT首先在Ix上搜索,然后加入Cx上的搜索

create nonclustered index Ix on Data (Id, Date) include (Value)
交换聚集索引和非聚集索引将打破这个循环,但这不是一个可接受的解决方案,因为它会引入其他(更复杂)选择的循环

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

  • 不止一排,即使我要求排名前1
  • 没有行,即使行已存在并已提交
  • 最糟糕的是,一行不满足WHERE子句
  • 我在网上读了很多这方面的文章,但我所看到的(,)过多或过少计数异常的唯一复制品涉及扫描。这只涉及寻求。杰夫·阿特伍德(Jeff Atwood)关于使用诺洛克(NOLOCK)的讨论引发了热烈的讨论。我对Rick Townsend的评论特别感兴趣:

    其次,如果读取脏数据,则 你要冒的风险是看报纸 完全错排了。例如,如果 您的选择读取要查找的索引 您的行,然后更新将更改 行的位置(例如:由于 页面拆分或对 聚集索引),当您选择 去读取实际数据行,它是 要么已经不在了,要么是另一个世界 一起划船

    这是否可以仅使用插入而不使用更新?如果是这样的话,那么我想即使是我在只插入表上的搜索也可能是危险的


    更新:

    我在想办法。它似乎是基于行的,事务在其中读取表(没有共享锁!),找到它们感兴趣的行,然后查看是否需要从tempdb中的版本存储中获取该行的旧版本


    但在我的例子中,没有一行有多个版本,因此版本存储似乎毫无意义。如果发现该行没有共享锁,那么只使用NOLOCK有什么不同?

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

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

    使用NOLOCK或readuncommitted意味着放弃对一致性的任何保证。句号

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

    不确定这是否适用,也不清楚您试图通过查询/表实现什么,但本文可能会有所帮助:

    更新

    如果NOLOCK读取将读取不一致的状态(例如,读取过时的非聚集索引键并将其追踪到聚集索引中缺失的行),则快照读取将在版本存储中找到“缺失”行。对于稳定数据,快照读取与nolock读取相同。每当数据发生更改(未提交的更新)时,版本存储的魔力就会发挥作用,因为快照读取会进入版本存储并找到“旧”值(稳定且一致),nolock读取会在其中失控,并将指针追踪到lala land。

    能否详细说明您认为安全的原因,鉴于上述担忧?此外,我还考虑过在索引中包含非键列,但实际上它们相当宽(不止一个),并且会占用大量空间。而且复制可能会迫使更多有用的东西从缓存中出来。啊,OUTPUT子句非常酷,谢谢。我不需要在这个特定的表中使用它,但我可以想出一些未来可以使用它的代码。我同意你的观点。我已经体验过不同查询计划的影响-死锁不会发生,直到有足够的行使两次搜索比完全扫描更好。老实说,我已经倾向于快照隔离。但记录这种未记录的行为对我来说很有趣,即使我在生产中没有利用它。:)好的,所以我同意我们不想“读取一个过时的非聚集索引键,并将其追踪到聚集索引中丢失的行”。问题是,在没有任何行更新的情况下,是否会发生这种情况?我所做的只是插入,所以只更新元数据,而不更新任何行中的数据。我原以为SQL Server会有一些低级一致性机制来防止元数据损坏,即使在使用NOLOCK(这是一个相当高级的概念)时也是如此。NOLOCK将从任何行集中读取一致的行。它不能保证的是同一个表的不同行集之间的一致性(即非群集与群集)、行集内的一致性(如丢失行或重复行)以及重复读取同一行之间的一致性(如自连接)。“使用NOLOCK或READ UNCOMMIT意味着放弃任何一致性保证。句点。”另一种分阶段解决问题的方法是“正确实施的限制是什么”。在极端情况下,答案可能是“行为未定义”,在这种情况下,损坏数据库将在规范范围内。我确信SQL的规范