it-swarm.com.de

Was sind die Hauptursachen für Deadlocks und können sie verhindert werden?

Kürzlich hat eine unserer ASP.NET-Anwendungen einen Datenbank-Deadlock-Fehler angezeigt, und ich wurde aufgefordert, den Fehler zu überprüfen und zu beheben. Es gelang mir herauszufinden, dass die Ursache für den Deadlock eine gespeicherte Prozedur war, die eine Tabelle innerhalb eines Cursors rigoros aktualisierte.

Dies ist das erste Mal, dass ich diesen Fehler sehe und nicht weiß, wie ich ihn effektiv verfolgen und beheben kann. Ich habe alle mir bekannten Möglichkeiten ausprobiert und schließlich festgestellt, dass die zu aktualisierende Tabelle keinen Primärschlüssel hat! Zum Glück war es eine Identitätssäule.

Ich fand später den Entwickler, der die Datenbank für die Bereitstellung skriptgesteuert hat, durcheinander. Ich habe einen Primärschlüssel hinzugefügt und das Problem wurde gelöst.

Ich fühlte mich glücklich und kehrte zu meinem Projekt zurück und recherchierte, um den Grund für diesen Stillstand herauszufinden ...

Anscheinend war es eine kreisförmige Wartebedingung, die den Deadlock verursachte. Updates dauern ohne Primärschlüssel offenbar länger als mit Primärschlüssel.

Ich weiß, dass es keine genau definierte Schlussfolgerung ist, deshalb poste ich hier ...

  • Ist der fehlende Primärschlüssel das Problem?
  • Gibt es andere Bedingungen, die einen anderen Deadlock verursachen als (gegenseitiger Ausschluss, Halten und Warten, keine Vorauszahlung und zirkuläres Warten)?
  • Wie verhindere und verfolge ich Deadlocks?
57
CoderHawk

das Verfolgen von Deadlocks ist das einfachere von beiden:

Standardmäßig werden Deadlocks nicht in das Fehlerprotokoll geschrieben. Sie können SQL veranlassen, Deadlocks mit den Ablaufverfolgungsflags 1204 und 3605 in das Fehlerprotokoll zu schreiben.

Schreiben Sie Deadlock-Informationen in das SQL Server-Fehlerprotokoll: DBCC TRACEON (-1, 1204, 3605)

Schalten Sie es aus: DBCC TRACEOFF (-1, 1204, 3605)

Unter "Fehlerbehebung bei Deadlocks" finden Sie eine Erläuterung des Ablaufverfolgungsflags 1204 und der Ausgabe, die Sie beim Einschalten erhalten. https://msdn.Microsoft.com/en-us/library/ms178104.aspx

Prävention ist schwieriger, im Wesentlichen muss man auf Folgendes achten:

Codeblock 1 sperrt Ressource A und dann Ressource B in dieser Reihenfolge.

Codeblock 2 sperrt Ressource B und dann Ressource A in dieser Reihenfolge.

Dies ist die klassische Bedingung, bei der ein Deadlock auftreten kann. Wenn das Sperren beider Ressourcen nicht atomar ist, kann der Codeblock 1 A sperren und vorbelegt werden. Dann blockiert Codeblock 2 B, bevor A die Verarbeitungszeit zurückerhält. Jetzt hast du einen Deadlock.

Um diesen Zustand zu verhindern, können Sie Folgendes tun

Codeblock A (Pseudocode)

Lock Shared Resource Z
    Lock Resource A
    Lock Resource B
Unlock Shared Resource Z
...

Codeblock B (Pseudocode)

Lock Shared Resource Z
    Lock Resource B
    Lock Resource A
Unlock Shared Resource Z
...

nicht zu vergessen, A und B freizuschalten, wenn Sie damit fertig sind

dies würde das Deadlocking zwischen Codeblock A und Codeblock B verhindern

Aus Datenbanksicht bin ich mir nicht sicher, wie ich diese Situation verhindern soll, da Sperren von der Datenbank selbst behandelt werden, d. H. Zeilen-/Tabellensperren beim Aktualisieren von Daten. Ich habe gesehen, dass die meisten Probleme auftreten, wenn Sie Ihre innerhalb eines Cursors gesehen haben. Cursor sind notorisch ineffizient, vermeiden Sie sie, wenn möglich.

39
BlackICE

meine Lieblingsartikel zum Lesen und Lernen von Deadlocks sind: Simple Talk - Aufspüren von Deadlocks und SQL Server Central - Verwenden von Profiler zum Auflösen von Deadlocks . Sie geben Ihnen Proben und Ratschläge, wie Sie mit dem Saugen einer Situation umgehen können.

Kurz gesagt, um ein aktuelles Problem zu lösen, würde ich die beteiligten Transaktionen kürzer machen, den nicht benötigten Teil aus ihnen herausnehmen, mich um die Reihenfolge der Verwendung der Objekte kümmern, sehen, welche Isolationsstufe tatsächlich benötigt wird, und nicht unnötig lesen Daten...

Aber lesen Sie die Artikel besser, sie werden in Ratschlägen viel besser sein.

24
Marian

Manchmal kann ein Deadlock durch Hinzufügen einer Indizierung behoben werden, da die Datenbank einzelne Datensätze und nicht die gesamte Tabelle sperren kann, sodass Konflikte und die Möglichkeit von Blockierungen verringert werden.

Zum Beispiel in InnoDB :

Wenn Sie keine für Ihre Anweisung geeigneten Indizes haben und MySQL die gesamte Tabelle scannen muss, um die Anweisung zu verarbeiten, wird jede Zeile der Tabelle gesperrt, wodurch alle Einfügungen anderer Benutzer in die Tabelle blockiert werden. Es ist wichtig, gute Indizes zu erstellen, damit Ihre Abfragen nicht unnötig viele Zeilen scannen.

Eine andere gängige Lösung besteht darin, die Transaktionskonsistenz zu deaktivieren, wenn sie nicht benötigt wird, oder auf andere Weise Ihre Isolationsstufe zu ändern, z. B. einen lang laufenden Job zum Berechnen von Statistiken ... eine genaue Antwort reicht im Allgemeinen aus, Sie brauche keine genauen Zahlen, da sie sich unter dir ändern. Wenn der Vorgang 30 Minuten dauert, möchten Sie nicht, dass alle anderen Transaktionen in diesen Tabellen gestoppt werden.

...

Die Verfolgung hängt von der verwendeten Datenbanksoftware ab.

16
Joe

Nur um sich auf dem Cursor zu entwickeln. es ist in der Tat sehr schlecht. Es sperrt die gesamte Tabelle und verarbeitet dann die Zeilen nacheinander.

Es ist am besten, Zeilen wie ein Cursor mit einer while-Schleife zu durchlaufen

In der while-Schleife wird für jede Zeile in der Schleife eine Auswahl durchgeführt, und die Sperre erfolgt jeweils nur für eine Zeile. Der Rest der Daten in der Tabelle kann kostenlos abgefragt werden, wodurch die Wahrscheinlichkeit eines Deadlocks verringert wird.

Außerdem ist es schneller. Sie wundern sich, warum es überhaupt Cursor gibt.

Hier ist ein Beispiel für diese Art von Struktur:

DECLARE @LastID INT = (SELECT MAX(ID) FROM Tbl)
DECLARE @ID     INT = (SELECT MIN(ID) FROM Tbl)
WHILE @ID <= @LastID
    BEGIN
    IF EXISTS (SELECT * FROM Tbl WHERE ID = @ID)
        BEGIN
        -- Do something to this row of the table
        END

    SET @ID += 1  -- Don't forget this part!
    END

Wenn Ihr ID-Feld spärlich ist, möchten Sie möglicherweise eine separate Liste von IDs abrufen und diese durchlaufen:

DECLARE @IDs TABLE
    (
    Seq INT NOT NULL IDENTITY PRIMARY KEY,
    ID  INT NOT NULL
    )
INSERT INTO @IDs (ID)
    SELECT ID
    FROM Tbl
    WHERE 1=1  -- Criteria here

DECLARE @Rec     INT = 1
DECLARE @NumRecs INT = (SELECT MAX(Seq) FROM @IDs)
DECLARE @ID      INT
WHILE @Rec <= @NumRecs
    BEGIN
    SET @ID = (SELECT ID FROM @IDs WHERE Seq = @Seq)

    -- Do something to this row of the table

    SET @Seq += 1  -- Don't forget this part!
    END
7

Das Fehlen eines Primärschlüssels ist nicht das Problem. Zumindest für sich. Erstens benötigen Sie keine Primärdaten, um Indizes zu haben. Zweitens verursacht eine Tabellensperre selbst dann keinen Deadlock, wenn Sie Tabellenscans durchführen (was passieren muss, wenn Ihre bestimmte Abfrage keinen Index verwendet). Ein Schreibprozess würde auf einen Lesevorgang warten, und ein Leseprozess würde dies tun Warten Sie auf ein Schreiben, und natürlich müssten die Lesevorgänge überhaupt nicht aufeinander warten.

Zusätzlich zu den anderen Antworten spielt die Transaktionsisolationsstufe eine Rolle, da wiederholbares Lesen und Serialisieren dazu führen, dass Lesesperren bis zum Ende der Transaktion gehalten werden. Das Sperren einer Ressource führt nicht zu einem Deadlock. Es verschlossen zu halten. Schreibvorgänge halten ihre Ressource immer bis zum Ende der Transaktion gesperrt.

Meine bevorzugte Strategie zur Verhinderung von Sperren ist die Verwendung der "Schnappschuss" -Funktionen. Die Funktion "Festgeschriebenen Snapshot lesen" bedeutet, dass beim Lesen keine Sperren verwendet werden! Und wenn Sie mehr Kontrolle als "Festgeschrieben lesen" benötigen, gibt es die Funktion "Isolationsstufe für Schnappschüsse". Diese ermöglicht eine serialisierte Transaktion (hier mit MS-Begriffen), ohne die anderen Spieler zu blockieren.

Schließlich kann eine Klasse von Deadlocks durch Verwendung einer Aktualisierungssperre verhindert werden. Wenn Sie den Lesevorgang lesen und halten (HOLD oder Repeatable Read) und ein anderer Prozess dasselbe tut, versuchen beide, dieselben Datensätze zu aktualisieren, und Sie haben einen Deadlock. Wenn jedoch beide eine Aktualisierungssperre anfordern, wartet der zweite Prozess auf den ersten, während andere Prozesse die Daten mithilfe gemeinsam genutzter Sperren lesen können, bis die Daten tatsächlich geschrieben wurden. Dies funktioniert natürlich nicht, wenn einer der Prozesse weiterhin eine gemeinsame HOLD-Sperre anfordert.

6
Gerard ONeill