Ist READ UNCOMMITTED / NOLOCK in dieser Situation sicher?

Ich weiß, dass Snapshot-Isolation dieses Problem beheben würde, aber ich frage mich, ob NOLOCK in diesem speziellen Fall sicher ist, damit ich den Overhead vermeiden kann.

Ich habe einen Tisch, der so aussieht:

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) 

Es gibt keine Updates für den Tisch. Löschungen können auftreten, aber sie sollten nie mit dem SELECT konkurrieren, weil sie das andere, älteres Ende des Tisches beeinflussen. Einfügungen sind regelmäßig und Seitenaufteilungen zum (Id, Date) Index sind sehr häufig.

Ich habe eine Deadlock-Situation zwischen einem Standard-INSERT und einem SELECT, das wie folgt aussieht:

 select top 1 Date, Value from Data where Id = @p0 order by Date desc 

da das INSERT eine Sperre für Cx (date, Id, Wert) und dann Ix (Id, Date) erhält, aber das SELECT eine Sperre für Ix (Id, Date) und dann Cx (date, Id; Value) erhält. Dies ist, weil die SELECT zuerst sucht auf Ix und dann verbindet sich mit einem Such auf Cx.

Das Austauschen des geclusterten und nicht gruppierten Index würde diesen Zyklus brechen, aber es ist keine akzeptable Lösung, da er Zyklen mit anderen (komplexeren) SELECTs einführen würde.

Wenn ich NOLOCK zum SELECT hinzufüge, kann es in diesem Fall schief gehen? Kann es zurückkehren:

  1. Mehr als eine Reihe, obwohl ich nach TOP 1 gefragt habe?
  2. Keine Zeilen, obwohl einer existiert und begangen wurde?
  3. Am schlimmsten ist eine Reihe, die die WHERE-Klausel nicht erfüllt?

Ich habe viel über dieses online gelesen, aber die nur Reproduktionen von Über- oder Unterzählung Anomalien, die ich gesehen habe ( eins , zwei ) beinhalten einen Scan. Hier geht es nur darum Jeff Atwood hat einen Beitrag über die Verwendung von NOLOCK, die eine gute Diskussion generiert hat. Ich interessierte mich besonders für einen Kommentar von Rick Townsend:

Zweitens, wenn Sie schmutzige data lesen, ist das Risiko, das Sie laufen, das Lesen der völlig falschen Zeile. Wenn zum Beispiel Ihr select einen Index liest, um Ihre Zeile zu finden, ändert die Aktualisierung den memoryort der Zeilen (zB: aufgrund einer Seitenaufteilung oder einer Aktualisierung auf den gruppierten Index), wenn Ihre Auswahl die eigentliche datazeile liest , es ist entweder nicht mehr da, oder eine andere reihe ganz!

Ist das nur mit Einsätzen möglich und keine Updates? Wenn ja, dann schätze ich sogar meine sucht auf einem Insert-only Tisch könnte gefährlich sein.


Aktualisieren:

Ich versuche herauszufinden, wie Snapshot-Isolation funktioniert . Es scheint, Zeilen-basiert, wo transactions die Tabelle lesen (ohne gemeinsame Sperre!), Finden Sie die Zeile, die sie interessiert sind, und dann sehen, ob sie eine alte Version der Zeile aus dem Versions-Shop in tempdb erhalten müssen.

Aber in meinem Fall wird keine Zeile mehr als eine Version haben, also scheint der Versions-Shop eher sinnlos. Und wenn die Zeile ohne gemeinsame Sperre gefunden wurde, wie ist es anders, nur mit NOLOCK?

Mit NOLOCK oder READ UNCOMMITTED bedeutet, dass Sie eine Garantie für Konsistenz aufgeben. Periode.

Wenn Sie Konsistenz brauchen, tun Sie nicht schmutziges Lesen. Ihre gesamte Erklärung beruht auf undokumentiertem Verhalten, das sich in zukünftigen Releases und, weit schlechter, auf spezifische Zugriffspläne, die Sie für Ihre Abfrage erwarten, ändern kann. Der Abfrage-Optimierer ist frei, jeden Plan zu wählen, den es sieht, und jede Annahme, die Sie machen, kann es in der Produktion gebrochen werden. So ist wieder auf Platz eins: mach nicht schmutzig liest, wenn du nicht bereit bist, den Konsequenzen zu begegnen.

Nicht sicher, ob dies zutrifft, ist nicht klar, was Sie versuchen, mit Ihrer Abfrage / Tabelle zu erreichen, aber vielleicht kann dieser Artikel helfen: Verwenden von Tabellen als Warteschlangen .

Aktualisiert
Wo ein NOLOCK gelesen wird, würde ein inkonsistenter Zustand lesen (zB einen abgestandenen, nicht gruppierten Indexschlüssel lesen und ihn auf eine fehlende Zeile im gruppierten Index verfolgen), ein Schnappschuss, der gelesen wurde, würde die "fehlende" Zeile im Versionslager finden. Für stabile data ist ein Schnappschuss gelesen identisch mit einem Nolock lesen. Die Magie des Versions-Shops kommt ins Spiel, wenn data geändert werden (ungebundene Updates), da der Schnappschuss lautet in den Versions-Shop und findet die 'alten' Werte (stabil und konsistent), wo die Nolock lesen würde gehen haywire und Chase pointers in La La Land.

Sie sollten in diesem Fall mit dem NOLOCK sicher sein. Ein weiterer Gedanke: Hinzufügen von Wert als enthaltene Spalte auf dem Ix-Index sollte die search auf Cx beseitigen.

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