20 Stimmen

Es wurde ein Deadlock in der PL/pgSQL-Funktion festgestellt.

Ich habe ein Deadlock-Problem bei einer PL/pgSQL-Funktion in meiner PostgreSQL-Datenbank. Bitte finden Sie die SQL-Anweisung im Code-Block (nur ein Beispiel):

BEGIN
UPDATE accounts SET balance = 0 WHERE acct_name like 'A%';
UPDATE accounts SET balance = balance + 100 WHERE acct_name like '%A';
EXCEPTION WHEN OTHERS THEN RAISE NOTICE SQLERRM;
END;

Ich habe festgestellt, dass der Deadlock auftrat, als diese Anweisung ausgeführt wurde. Aber ich bin mir nicht sicher, ob es andere Anweisungen gab, die versuchten, diese Tabelle zur gleichen Zeit zu aktualisieren (weil ich keine in meinem Protokollierungssystem gefunden habe).

Also, ist es möglich, dass der Deadlock innerhalb dieser Anweisung auftrat? Soweit ich weiß, wenn wir die gesamte Anweisung mit BEGIN/END blockieren. Es wird sich um dieselbe Transaktion handeln und sollte nicht von sich selbst gesperrt werden.

0 Stimmen

Hast du Trigger auf Konten? Verwendest du auch explizite Sperrungen?

1 Stimmen

Standardmäßig kann eine Transaktion Änderungen beobachten, die von anderen Transaktionen verpflichtet wurden. Weitere Informationen finden Sie unter Transaktionsisolierung in der PostgreSQL-Dokumentation.

0 Stimmen

@strkol Ja, das habe ich, aber die Aussage in diesem Trigger ist nicht mit dieser Tabelle verbunden. Für das explizite Sperren gilt auch ja.

30voto

Erwin Brandstetter Punkte 530399

Es gibt definitiv ein anderes Verfahren, das um dieselben Ressourcen konkurriert. Das ist die Natur eines Deadlocks. Eine Funktion wie die, die du anzeigst, kann sich niemals selbst blockieren. Siehe Kommentar von @kgrittn unten, der ein Experte für Nebenläufigkeit in PostgreSQL ist.

Deine Version von PostgreSQL fehlt. Moderne Versionen werfen eine detaillierte Fehlermeldung. Beide Prozesse, die um Ressourcen konkurrieren, sind detailliert aufgeführt, wenn die Standard-Logging-Einstellungen aktiviert sind. Überprüfe deine db logs.

Die Tatsache, dass du den Fehler abfängst, kann verhindern, dass Postgres dir alle Details gibt. Entferne den EXCEPTION Block aus deiner PL/pgSQL-Funktion, falls du die Informationen nicht im db-Log erhältst, und versuche es erneut.

Um Deadlocks zu lindern, kannst du eine Reihe von Dingen tun. Wenn alle deine Clients auf Ressourcen in einer synchronisierten Reihenfolge zugreifen, können keine Deadlocks auftreten. Im Handbuch wird die grundlegende Strategie zur Lösung der meisten Fälle im Kapitel über Deadlocks beschrieben.


Was Version 8.3 betrifft: Erwägen Sie ein Upgrade auf eine aktuellere Version. Insbesondere sollte diese Verbesserung in Version 8.4 für Sie interessant sein (Zitat aus den Versionshinweisen):

Bei der Meldung eines Deadlocks alle beteiligten Abfragen im Server-Log angeben (Itagaki Takahiro)

Außerdem wird Version 8.3 ihr Ende des Lebens im Februar 2013 haben. Sie sollten in Erwägung ziehen, ein Upgrade durchzuführen.

Eine Deadlock-Situation, die VACUUM betrifft, sollte in 8.3.1 behoben worden sein. Behoben in 8.3.1.

0 Stimmen

Ich habe postgres 8.3.6. Dieser Satz dient nur als Beispiel. Tatsächlich verwende ich das Einfügen von Protokollen in eine andere Tabelle anstelle von RAISE NOTICE.

0 Stimmen

Normalerweise sollte die Fehlermeldung Ihnen die benötigten Informationen geben. Ich habe meiner Antwort etwas hinzugefügt.

0 Stimmen

Vielen Dank für Ihren Rat. Ich werde weiterhin herausfinden, welche anderen Prozesse versucht haben, etwas mit dieser Tabelle zu tun.

2voto

jimmy chen Punkte 27

Sie würden kein Deadlock-Problem bekommen, wenn Sie commit hinzufügen, um exklusive Sperren freizugeben.

BEGIN
UPDATE Konten SET Saldo = 0 WHERE acct_name like 'A%';
COMMIT;  
UPDATE Konten SET Saldo = Saldo + 100 WHERE acct_name like '%A';
AUSNAHME WENN ANDERE DANN RAISE NOTICE SQLERRM;
END;

1 Stimmen

Die Prozedur in plpgsql läuft in einem einzelnen Transaktions-Commit-Block, daher gibt es keine Commit-Befehle innerhalb von Prozeduren.

-1voto

jimmy chen Punkte 27

In PostgreSQL bedeutet begin, dass du eine Batch-Transaktion startest.

Dein erstes Update sperrt Zeilen für Konten WHERE acct_name like 'A%'; Diese Zeilen sind ausschließlich nach dem ersten Update gesperrt.

Das zweite Update versucht genau die gleichen Zeilen wie das erste Update zu öffnen, um sie zu aktualisieren scheitert, weil das erste Update noch NICHT committed wurde.

Daher führte das zweite Update zu einem Deadlock und wurde zurückgerollt.

2 Stimmen

Im PL/pgSQL BEGIN markiert lediglich den Anfang eines Code-Blocks. Es entspricht nicht dem BEGIN / COMMIT in reinem SQL.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X