it-swarm.com.de

Warum erhalte ich bei INSERT ein Problem mit der Snapshot-Isolation?

Gegeben zwei Tabellen

Elternteil

KeyID   GroupID   Name  Active

Kind

KeyID   ParentID  Name

Child.ParentID Ist FKed to Parent.KeyID

Wir fügen sowohl Parent als auch Child in eine einzelne Transaktion ein.

Wenn eine andere Parent Zeile aktualisiert wird (z. B. Active 1 -> 0), während die Transaktion aktiv ist, schlägt die ChildINSERT fehl mit:

Die Snapshot-Isolationstransaktion wurde aufgrund eines Aktualisierungskonflikts abgebrochen. Sie können die Snapshot-Isolation nicht verwenden, um direkt oder indirekt auf die Tabelle 'dbo.Child' in der Datenbank 'Test' zuzugreifen, um die Zeile zu aktualisieren, zu löschen oder einzufügen, die von einer anderen Transaktion geändert oder gelöscht wurde. Wiederholen Sie die Transaktion oder ändern Sie die Isolationsstufe für die Update/Delete-Anweisung.

Nach allem, was ich anhand von Warum erhalte ich die Meldung "Snapshot-Isolationstransaktion wegen Aktualisierungskonflikt abgebrochen"? Dies ist wahrscheinlich auf einen vollständigen Scan zur Überprüfung des Fremdschlüssels zurückzuführen.

In der Tat ermöglicht das Entfernen des Fremdschlüssels, dass ChildINSERT wie erwartet abgeschlossen wird.

Trotzdem scheint keine Anzahl von nicht gruppierten Indizes für den Fremdschlüssel in der Tabelle Child zur Lösung dieses Problems beizutragen, sodass ich nicht weiß, was zu tun ist.

Wir haben RCSI für diese Datenbank aktiviert und die Transaktion wird im Snapshot-Isolationsmodus ausgeführt.

Zusätzliche Details

Ich habe festgestellt, dass dieses Problem auftritt, wenn die Einfügung in Child größer als eine bestimmte Anzahl von Zeilen ist. Zu diesem Zeitpunkt wechselt der Abfrageoptimierer von einer Nested Loops (Left Semi Join) zu einer Merge Join (Left Semi Join).

Entschuldigung, dass nicht die Tatsache berücksichtigt wurde, dass mehrere untergeordnete Datensätze für einen einzelnen übergeordneten Datensatz eingefügt wurden.

Arbeitseinsatz (20 untergeordnete Datensätze): Working insert

Fehlerhafte Einfügung (50 untergeordnete Datensätze): Failing insert

Sproc einfügen ist ungefähr so:

CREATE PROCEDURE dbo.[usp_InsertRecords] (
    @journal dbo.ParentType READONLY,
    @journalItems dbo.ChildType READONLY,
    @tenantId INT
) AS
BEGIN
    INSERT INTO dbo.Parent(GroupID, Name, Active, TenantId)
        SELECT GroupID, Name, Active, @tenantId FROM @journal

    DECLARE @JournalId INT = convert(int,scope_identity());

    INSERT INTO dbo.Child(ParentID, Name, TenantId)
        SELECT @JournalId, Name, @tenantId 
        FROM @journalItems j2

END
GO

Und gleichzeitiges Update wäre so etwas wie:

UPDATE dbo.Parent Set Active = 0 WHERE KeyID = 1234 -- row not being inserted
11
joshschreuder

Fügen Sie der Anweisung INSERT einen Hinweis OPTION (LOOP JOIN) hinzu.

Oder verwenden Sie eine Planführung (oder einen Abfragespeicher), um die Semi-Join-Planform der verschachtelten Schleifen zu erzwingen.

Möglicherweise funktioniert OPTION (FAST 1) auch.

Der Punkt besteht darin, einen Semi-Join zum Zusammenführen zu vermeiden, bei dem viele (möglicherweise alle) Zeilen der referenzierten Tabellen von der aktuellen Transaktion berührt werden. Wenn eine übergeordnete Zeile mit einer Änderung (einschließlich Erstellung) gefunden wird, ein Aktualisierungskonfliktfehler wird ausgelöst .

6
Paul White 9