wie Anwendungslogik statt Datenzeilen koordiniert wird
Datenbanksperren schützen üblicherweise konkrete Datenzeilen oder Tabellen vor gleichzeitigem, inkonsistentem Zugriff. Advisory Locks verfolgen einen anderen Zweck: Sie nutzen dieselbe robuste Locking-Infrastruktur der Datenbank, um beliebige Anwendungslogik zu koordinieren, ohne dass diese Sperre an eine konkrete Zeile oder Tabelle gebunden ist. Ein typischer Einsatzfall ist verteiltes Locking für Cron-Jobs oder Hintergrundprozesse, die auf mehreren Servern laufen könnten, aber niemals gleichzeitig denselben Vorgang ausführen dürfen.
Inhaltsverzeichnis
- 1. Der Unterschied zu klassischen Zeilen- und Tabellensperren
- 2. Das Grundprinzip: eine beliebige Kennung als Koordinationspunkt
- 3. Praktischer Anwendungsfall: Cron-Jobs, die nicht doppelt laufen dürfen
- 4. Session-Level-Locks: manuelle Freigabe und Verbindungsbindung
- 5. Transaction-Level-Locks: automatische Freigabe am Transaktionsende
- 6. Blockierende vs. nicht-blockierende Varianten richtig einsetzen
- 7. Häufige Fehler beim praktischen Einsatz von Advisory Locks
- 8. Aktive Advisory Locks im Betrieb beobachten und debuggen
- 9. Wann Advisory Locks die falsche Wahl sind
- 10. Zusammenfassung
- 11. FAQ
1. Der Unterschied zu klassischen Zeilen- und Tabellensperren
Eine klassische Sperre in einer relationalen Datenbank ist immer an ein konkretes Datenobjekt gebunden: eine Zeile, eine Seite oder eine ganze Tabelle. Diese Sperren entstehen implizit durch normale DML-Anweisungen wie UPDATE oder explizit durch SELECT FOR UPDATE, und ihr Zweck ist es, konkurrierende Änderungen an denselben Daten zu koordinieren, damit die Konsistenz der gespeicherten Werte erhalten bleibt.
Ein Advisory Lock ist dagegen vollständig von konkreten Daten entkoppelt. Er besteht lediglich aus einer numerischen oder textuellen Kennung, die von der Anwendung frei gewählt wird, und trägt selbst keinerlei semantische Bedeutung für die Datenbank. Die Datenbank garantiert nur, dass zu jedem Zeitpunkt höchstens ein Prozess einen exklusiven Advisory Lock mit derselben Kennung halten kann, unabhängig davon, wofür diese Kennung inhaltlich steht.
2. Das Grundprinzip: eine beliebige Kennung als Koordinationspunkt
Weil ein Advisory Lock keine Verbindung zu konkreten Tabellenzeilen hat, kann er für jede Art von Koordinationsproblem genutzt werden, das die Anwendung selbst definiert: das Verhindern doppelter Batch-Verarbeitung, die Serialisierung eines bestimmten Geschäftsvorgangs über mehrere Serverinstanzen hinweg, oder ein einfacher, verteilter Mutex für eine kritische Initialisierungsroutine, die nur einmal laufen darf.
Die Kennung selbst wird üblicherweise aus einem stabilen, fachlich sprechenden Wert abgeleitet, etwa durch Hashing eines Job-Namens oder einer Mandanten-ID auf eine ganze Zahl, damit dieselbe Kennung bei jedem Aufruf konsistent reproduzierbar ist. Zwei unterschiedliche Prozesse, die denselben logischen Vorgang koordinieren wollen, müssen sich dabei nur auf dieselbe Ableitungslogik für die Kennung einigen, nicht auf ein gemeinsames Datenschema.
-- Advisory Lock über eine numerische Kennung anfordern (Syntax exemplarisch)
SELECT pg_try_advisory_lock(hashtext('nightly-report-job'));
-- Alternative in anderen Systemen über eine benannte Sperre
SELECT GET_LOCK('nightly-report-job', 10);
-- zweiter Parameter: maximale Wartezeit in Sekunden
3. Praktischer Anwendungsfall: Cron-Jobs, die nicht doppelt laufen dürfen
Ein klassisches Problem in verteilten Systemen ist ein zeitgesteuerter Job, der auf mehreren, redundant ausgelegten Servern eingeplant ist, aber inhaltlich nur einmal pro Ausführungszeitpunkt laufen darf, etwa ein nächtlicher Report-Export oder eine Abrechnungsbatch. Ohne Koordination würde jeder Server den Job unabhängig starten, was zu doppelter Verarbeitung, inkonsistenten Ergebnissen oder doppelt verschickten E-Mails führen kann.
Ein Advisory Lock löst dieses Problem elegant, ohne eine separate Koordinationsinfrastruktur wie einen dedizierten verteilten Lock-Service zu benötigen: Jeder Server versucht zu Beginn des Jobs, denselben Advisory Lock zu erwerben. Nur der Server, der die Sperre tatsächlich erhält, führt den Job aus, alle anderen erkennen den Fehlschlag sofort und beenden sich, ohne zu blockieren oder eine eigene Fehlerbehandlung für einen doppelten Lauf implementieren zu müssen.
-- Nicht-blockierender Versuch, den Job-Lock zu erwerben
DO $$
BEGIN
IF pg_try_advisory_lock(hashtext('billing-export-2026-08-08')) THEN
-- Nur dieser Prozess führt den Export aus
PERFORM run_billing_export();
PERFORM pg_advisory_unlock(hashtext('billing-export-2026-08-08'));
ELSE
RAISE NOTICE 'Export läuft bereits auf einer anderen Instanz';
END IF;
END $$;
4. Session-Level-Locks: manuelle Freigabe und Verbindungsbindung
Ein Session-Level Advisory Lock bleibt so lange bestehen, bis er entweder explizit über einen Unlock-Befehl freigegeben wird oder die Datenbankverbindung, die ihn erworben hat, beendet wird. Diese Bindung an die Verbindung, nicht an eine einzelne Transaktion, macht Session-Level-Locks geeignet für Koordinationsaufgaben, die über mehrere Transaktionen hinweg gelten sollen, etwa das Sperren eines gesamten mehrstufigen Batch-Vorgangs, der intern mehrere Commits ausführt.
Der wichtigste operative Punkt dabei: Wird die Verbindung durch einen Absturz oder eine unsauber terminierte Anwendung unterbrochen, ohne dass explizit entsperrt wurde, gibt die Datenbank den Lock automatisch frei, sobald sie das Verbindungsende erkennt. Dieses Verhalten verhindert einen dauerhaft hängenden Lock, erfordert aber, dass Connection-Pooling-Schichten korrekt konfiguriert sind, damit eine Verbindung nicht fälschlich als aktiv weiterverwendet wird, während der ursprachliche Prozess bereits beendet ist.
5. Transaction-Level-Locks: automatische Freigabe am Transaktionsende
Ein Transaction-Level Advisory Lock wird automatisch freigegeben, sobald die aktuelle Transaktion committet oder zurückgerollt wird, unabhängig davon, ob explizit entsperrt wurde. Diese Variante eignet sich für Koordinationsprobleme, die exakt an die Lebensdauer einer einzelnen Transaktion gebunden sind, etwa das Serialisieren eines kritischen Abschnitts innerhalb einer einzelnen Geschäftsoperation, ohne das Risiko, einen manuellen Unlock-Aufruf zu vergessen.
Der Vorteil gegenüber Session-Level-Locks liegt in der geringeren Fehleranfälligkeit: Ein vergessener oder durch eine Exception übersprungener Unlock-Aufruf kann bei Session-Level-Locks zu einem dauerhaft blockierten Koordinationspunkt führen, während ein Transaction-Level-Lock durch das ohnehin garantierte Transaktionsende zuverlässig aufgeräumt wird, selbst wenn die Anwendung einen Fehlerfall nicht sauber behandelt.
-- Transaction-Level Advisory Lock: automatische Freigabe bei COMMIT/ROLLBACK
BEGIN;
SELECT pg_advisory_xact_lock(hashtext('reindex-catalog'));
-- kritischer Abschnitt
UPDATE catalog_state SET reindex_running = true;
-- ... Verarbeitung ...
COMMIT;
-- Lock wird spätestens hier automatisch freigegeben
6. Blockierende vs. nicht-blockierende Varianten richtig einsetzen
Die meisten Datenbanksysteme bieten sowohl eine blockierende Variante an, die wartet, bis die Sperre verfügbar wird, als auch eine nicht-blockierende Variante, die sofort mit einem Erfolgs- oder Fehlschlag-Wert zurückkehrt. Für den Cron-Job-Anwendungsfall ist fast immer die nicht-blockierende Variante die richtige Wahl, weil ein Prozess, der die Sperre nicht erhält, den Job schlicht überspringen soll, statt ungenutzt zu warten und Ressourcen zu binden.
Blockierende Varianten eignen sich dagegen für Fälle, in denen mehrere Prozesse tatsächlich nacheinander denselben kritischen Abschnitt durchlaufen sollen, etwa eine sequentielle Verarbeitungspipeline mit mehreren Arbeitern. Wird hier fälschlich die nicht-blockierende Variante verwendet, brechen Arbeiter bei Konkurrenz sofort ab, statt korrekt in der Warteschlange zu bleiben, was die beabsichtigte Serialisierung unterläuft.
7. Häufige Fehler beim praktischen Einsatz von Advisory Locks
Ein verbreiteter Fehler ist die Kollision von Kennungen: Wird derselbe Zahlenraum für unterschiedliche, inhaltlich unabhängige Koordinationszwecke genutzt, etwa weil zwei Teams denselben Hashing-Ansatz ohne gemeinsame Absprache verwenden, kann es zu Kollisionen kommen, bei denen ein Lock fälschlich als belegt erscheint, obwohl er fachlich für einen anderen Zweck gedacht war. Ein klarer Namensraum, etwa durch ein Präfix vor dem Hashing, vermeidet dieses Problem zuverlässig.
Ein zweiter häufiger Fehler ist die Verwechslung von Session- und Transaction-Level-Locks in Umgebungen mit Connection Pooling: Wird eine Verbindung nach Freigabe an den Pool zurückgegeben, ohne dass ein Session-Level-Lock explizit entsperrt wurde, bleibt der Lock aus Sicht der Datenbank aktiv, obwohl die Anwendung ihn logisch als beendet betrachtet, bis eine neue Anfrage zufällig dieselbe gepoolte Verbindung wiederverwendet und den Lock unbewusst weiterhält.
8. Aktive Advisory Locks im Betrieb beobachten und debuggen
Weil Advisory Locks keine Verbindung zu konkreten Tabellenzeilen haben, tauchen sie nicht in denselben Diagnosewerkzeugen auf wie klassische Zeilensperren. Die meisten Systeme bieten dafür eine eigene Systemsicht oder Funktion, die aktuell gehaltene Advisory Locks samt ihrer Kennung und der haltenden Sitzung auflistet, was bei der Fehlersuche nach einem vermeintlich hängenden Koordinationspunkt der erste Anlaufpunkt sein sollte.
Für produktive Systeme empfiehlt sich zusätzlich ein Timeout oder eine maximale Wartezeit bei blockierenden Varianten, damit ein fehlerhaft hängender Lock nicht zu einer unbegrenzt wartenden Prozesskette führt. Kombiniert mit einem Logging, das jeden Erwerb und jede Freigabe eines Advisory Locks mit Zeitstempel protokolliert, lässt sich ein hängender Koordinationspunkt im Nachhinein zuverlässig auf seinen ursprünglichen Prozess zurückverfolgen.
-- Aktuell gehaltene Advisory Locks anzeigen (Syntax exemplarisch)
SELECT locktype, objid, pid, granted
FROM pg_locks
WHERE locktype = 'advisory';
9. Wann Advisory Locks die falsche Wahl sind
Advisory Locks sind kein Ersatz für klassische Zeilensperren oder Unique Constraints, wenn es tatsächlich um die Konsistenz gespeicherter Daten geht. Soll verhindert werden, dass zwei Prozesse denselben Datensatz gleichzeitig widersprüchlich ändern, ist ein reguläres Locking-Konzept auf Datenebene, etwa optimistisches Locking mit einer Versionsspalte oder SELECT FOR UPDATE, die korrektere und robustere Lösung, weil die Datenbank diese Garantien dann direkt an den betroffenen Daten durchsetzt.
Advisory Locks eignen sich dagegen genau dort, wo kein konkretes Datenobjekt existiert, das gesperrt werden könnte, die Koordination aber trotzdem eine zuverlässige, verteilte Infrastruktur benötigt, wie es bei rein prozess- oder job-bezogener Koordination der Fall ist. Diese klare Abgrenzung nach Anwendungsfall, nicht nach technischer Bequemlichkeit, verhindert einen Missbrauch von Advisory Locks als generisches Ersatz-Locking für eigentlich datenbezogene Probleme.
| Aspekt | Session-Level Lock | Transaction-Level Lock | Praxis-Hinweis |
|---|---|---|---|
| Freigabe | Manuell oder bei Verbindungsende | Automatisch bei COMMIT/ROLLBACK | Transaction-Level ist fehlertoleranter |
| Bindung | An die Datenbankverbindung | An die aktuelle Transaktion | Wichtig bei Connection Pooling |
| Typischer Einsatz | Mehrstufige Batch-Prozesse | Einzelne kritische Operation | Nach Lebensdauer des Vorgangs wählen |
| Risiko bei Fehler | Vergessener Unlock hängt dauerhaft | Wird beim Transaktionsende aufgeräumt | Session-Level braucht disziplinierten Code |
| Blockierend vs. nicht | Beide Varianten verfügbar | Beide Varianten verfügbar | Nicht-blockierend für Cron-Jobs |
| Datenbezug | Keiner, freie Kennung | Keiner, freie Kennung | Kein Ersatz für Zeilensperren |
Mironsoft
Datenbank-Optimierung, Query-Tuning und Migrationen
SQL-Abfragen, die bei Wachstum immer langsamer werden?
Wir analysieren und optimieren SQL-Datenbanken unabhängig vom eingesetzten System, planen sichere Migrationen und Schema-Änderungen und bringen Teams Query-Optimierung praxisnah bei.
Query-Optimierung
Langsame Abfragen analysieren und mit Indizes und Explain-Plänen gezielt beschleunigen.
Migrations-Planung
Schema-Änderungen und Datenmigrationen sicher und ohne Downtime umsetzen.
Team-Schulung
SQL-Grundlagen und Performance-Denken praxisnah im Entwicklerteam verankern.
10. Zusammenfassung
Advisory Locks: Das Wichtigste auf einen Blick
Grundidee
Advisory Locks nutzen die Locking-Infrastruktur der Datenbank, um Anwendungslogik statt Datenzeilen zu koordinieren.
Hauptanwendung
Verteiltes Locking für Cron-Jobs oder Hintergrundprozesse, die nicht gleichzeitig auf mehreren Servern laufen dürfen.
Session vs. Transaction
Session-Level braucht manuelle Freigabe und hängt an der Verbindung, Transaction-Level räumt sich am Transaktionsende automatisch auf.
Abgrenzung
Kein Ersatz für Zeilensperren oder Unique Constraints bei echten Datenkonsistenz-Problemen.