スナップショット分離がこの問題を解決することはわかっていますが、オーバーヘッドを回避できるように、この特定のケースで 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 に追加すると、この場合に問題が発生する可能性はありますか? 返品できますか:
- TOP 1を頼んだのに複数行?
- 行が存在し、コミットされているにもかかわらず、行がありませんか?
- 最悪なのは、WHERE 句を満たさない行ですか?
私はこれについてオンラインで多くのことを読みましたが、私が見た過大または過少の異常の唯一の再現 ( 1 つ、2 つ) はスキャンを伴います。これには、シークのみが含まれます。Jeff AtwoodのNOLOCK の使用に関する投稿があり、良い議論が生まれました。Rick Townsend のコメントに特に興味を持ちました。
第 2 に、ダーティ データを読み取ると、まったく間違った行を読み取るリスクが生じます。たとえば、select が行を見つけるためにインデックスを読み取る場合、select が実際のデータ行を読み取るときに、更新によって行の場所が変更されます (たとえば、ページ分割またはクラスター化インデックスへの更新が原因で)。 、もう存在しないか、まったく別の行です!
これは、挿入のみで更新なしで可能ですか? もしそうなら、挿入専用テーブルでのシークでさえ危険である可能性があると思います。
アップデート:
スナップショット分離がどのように機能するかを理解しようとしています。トランザクションがテーブルを読み取り (共有ロックなしで!)、関心のある行を見つけて、tempdb のバージョン ストアから古いバージョンの行を取得する必要があるかどうかを確認する、行ベースのようです。
しかし、私の場合、複数のバージョンを持つ行はないため、バージョン ストアはかなり無意味に思えます。また、行が共有ロックなしで見つかった場合、NOLOCK を使用した場合とどう違うのでしょうか?