← Datenbankadministration für Sysadmins
Schemaänderungen im laufenden Betrieb: Sperrstufe, Riegel und Rückweg
Der ALTER TABLE lief in 200 Millisekunden. Die Anwendung war 40 Minuten nicht benutzbar, und im Protokoll steht kein einziger Fehler. Beides passt zusammen: Die Sperre, die eine Strukturänderung nimmt, blockiert auch gewöhnliche Leseabfragen — und ohne Zeitgrenze wartet sie unbegrenzt auf eine fremde Transaktion. Dieses Modul behandelt Sperrstufen je Unterbefehl, den Riegel, der aus Stillstand einen Fehlschlag macht, und das Muster, mit dem eine Spalte ohne Ausfall ersetzt wird.
Lehrtext · 11 Abschnitte · zuletzt geprüft: 2026-09-12
Eine Schemaänderung ist eine Auslieferung, kein Befehl
ALTER TABLE sieht aus wie eine Zeile und verhält sich wie ein Wartungsfenster. Der Unterschied zu einer Dateiänderung auf einem Anwendungsserver ist, dass die Datenbank einen Zustand hat, den alle teilen: Während die Struktur wechselt, kann niemand sie in zwei Fassungen gleichzeitig sehen.
Daraus folgt die Grundspannung dieses Moduls. Eine Auslieferung läuft nie in einem Augenblick ab — es gibt immer eine Phase, in der alte und neue Anwendungsfassung gleichzeitig laufen. Das Schema muss in dieser Phase beiden genügen. Wer es in einem Schritt umstellt, erzwingt einen Stillstand; wer es in mehreren Schritten tut, muss jeden Schritt rückwärtskompatibel halten.
Welche Sperre der Befehl wirklich nimmt
Die Dokumentation zu ALTER TABLE beginnt mit einer Voreinstellung, die man kennen muss, bevor man irgendeine Unterform nachschlägt.
ACCESS EXCLUSIVE-Sperre wird erlangt, sofern nicht ausdrücklich anders vermerkt. Werden mehrere Unterbefehle angegeben, ist die erlangte Sperre die strengste, die einer der Unterbefehle verlangt.Der zweite Satz ist der, der in der Praxis überrascht. Wer drei harmlose Änderungen in einem Befehl zusammenfasst, weil das übersichtlicher aussieht, bekommt die strengste Sperre von allen dreien — für die Dauer aller drei. Getrennte Befehle nehmen jeweils nur, was sie brauchen, und geben früher wieder frei.
- Voreinstellung
ACCESS EXCLUSIVE— die strengste Stufe, sofern die Dokumentation zur Unterform nichts anderes sagt.SET STATISTICS, Attributoptionen- Nur
SHARE UPDATE EXCLUSIVE. Blockiert keine Leser und keine Schreiber. ADD FOREIGN KEY- Nur
SHARE ROW EXCLUSIVE— und zwar zusätzlich auch auf der referenzierten Tabelle. Die zweite Sperre wird bei der Planung regelmäßig übersehen. - Die meisten anderen
ADD CONSTRAINT-Formen ACCESS EXCLUSIVE.
Die Sperre, die wirklich alles trifft
Unter den Sperrmodi ist einer besonders, und ALTER TABLE nimmt ihn voreingestellt. ACCESS EXCLUSIVE steht laut Konfliktmatrix der Dokumentation mit allen Modi in Konflikt — auch mit ACCESS SHARE, dem Modus, den ein ganz gewöhnliches SELECT erwirbt.
Das heißt: Solange diese Sperre gehalten wird, geht auf dieser Tabelle nicht nur kein Schreibvorgang mehr, sondern keine einzige Leseabfrage. Eine Schemaänderung ist damit nicht ein Eingriff in die Schreibseite, sondern ein vollständiger Stillstand der Tabelle.
pg_locks erwähnt sie ausdrücklich, gibt ihre Reihenfolge aber nicht preis. Die praktische Folge — dokumentiert ist sie in dieser Form nicht, beobachtbar aber zuverlässig — ist, dass sich hinter einer wartenden ACCESS EXCLUSIVE-Anforderung weitere Anfragen aufstauen. Aus einer Migration, die „nur ein paar Sekunden dauert“, wird dann ein Stillstand von der Länge der längsten gerade laufenden Transaktion.Warum die Migration 40 Minuten Ausfall erzeugte, obwohl sie 200 Millisekunden lief
Szenario
Ein ALTER TABLE ... ADD COLUMN wurde um 10:00 Uhr ausgeführt. Der Befehl selbst brauchte 200 Millisekunden. Die Anwendung war von 10:00 bis 10:40 Uhr nicht benutzbar.
Anforderungen
- Eine Auswertungsabfrage lief seit 09:22 Uhr auf derselben Tabelle
- Kein
lock_timeoutgesetzt - Keine Fehlermeldung im Protokoll — alles wartete nur
Schritte
- Die Auswertung hielt
ACCESS SHARE. Das steht mitACCESS EXCLUSIVEin Konflikt, also musste die Migration warten. - Sie wartete, bis die Auswertung um 10:40 Uhr fertig war — unbegrenzt, weil nichts sie abbrach.
- Während dieser 40 Minuten stauten sich die Anfragen der Anwendung hinter der wartenden Anforderung.
- Um 10:40 Uhr lief die Migration in 200 Millisekunden durch, und alles wurde wieder normal — weshalb die Nachbetrachtung den Befehl für unschuldig hielt.
Merksatz: Die Dauer einer Schemaänderung ist nicht ihre Ausführungszeit, sondern ihre Wartezeit plus ihre Ausführungszeit. Die erste hängt an fremden Transaktionen und ist ohne Riegel unbegrenzt.
`lock_timeout`: der Riegel, der aus Stillstand einen Fehlschlag macht
Gegen genau dieses Muster gibt es eine Einstellung, und sie ist voreingestellt aus.
lock_timeout bricht jede Anweisung ab, die länger als die angegebene Zeit auf eine Sperre auf einer Tabelle, einem Index, einer Zeile oder einem anderen Datenbankobjekt wartet. Die Zeitgrenze gilt für jeden Sperrerwerb einzeln. Der Wert null — die Voreinstellung — schaltet sie ab. Anders als statement_timeout kann diese Zeitüberschreitung nur beim Warten auf Sperren eintreten.Damit wird aus einem unbegrenzten Stillstand ein schneller, sichtbarer Fehlschlag. Die Migration bricht nach wenigen Sekunden ab, die Anwendung bleibt erreichbar, und der Auslieferungsprozess meldet einen Fehler statt eines Hängers. Der zweite Versuch kann dann in einem Moment laufen, in dem keine lange Abfrage im Weg steht.
statement_timeout ungleich null, ist es sinnlos, lock_timeout auf denselben oder einen größeren Wert zu setzen — die Anweisungs-Zeitüberschreitung würde immer zuerst greifen. Zweitens: Beide in postgresql.conf zu setzen wird nicht empfohlen, weil es alle Sitzungen beträfe. Der richtige Ort ist die Migrationssitzung selbst.Praktisch heißt das: Ein Auslieferungsskript setzt lock_timeout auf wenige Sekunden, bevor es die erste Schemaänderung absetzt — und nimmt in Kauf, dass es dadurch öfter fehlschlägt. Genau das ist der Gewinn: Ein Fehlschlag kostet einen zweiten Anlauf, ein Stillstand kostet den Dienst.
Zweistufig statt einstufig
Die teuerste Zutat vieler Schemaänderungen ist nicht die Strukturänderung, sondern die Prüfung des Bestands. Eine neue Bedingung verlangt normalerweise, dass jede vorhandene Zeile sie erfüllt — und das heißt, die ganze Tabelle zu lesen, während die Sperre gehalten wird.
Die Dokumentation bietet dafür einen zweistufigen Weg an. Mit der Option NOT VALID wird dieser potenziell langwierige Durchlauf übersprungen. Die Bedingung gilt trotzdem für alle nachfolgenden Einfüge- und Änderungsvorgänge — die schlagen ab sofort fehl, wenn sie sie verletzen. Die Datenbank nimmt nur nicht an, dass die Bedingung für alle bestehenden Zeilen gilt, bis sie mit VALIDATE CONSTRAINT bestätigt wurde.
- Schritt 1:
ADD CONSTRAINT ... NOT VALID - Kurz, weil kein Tabellendurchlauf. Ab sofort sind neue und geänderte Zeilen geprüft.
- Zwischenzeit
- Bestand bereinigen, soweit nötig. Die Anwendung läuft weiter, neue Verstöße können nicht mehr entstehen.
- Schritt 2:
VALIDATE CONSTRAINT - Der Durchlauf über den Bestand — mit einer schwächeren Sperre als die einstufige Variante.
Die Kollision, die niemand einplant
Es gibt einen Sperrmodus, der weder Leser noch Schreiber stört und trotzdem regelmäßig Auslieferungen zum Scheitern bringt. Er heißt SHARE UPDATE EXCLUSIVE, und die Dokumentation beschreibt seinen Zweck in einem Satz: Dieser Modus schützt eine Tabelle gegen gleichzeitige Schemaänderungen und Aufräumläufe.
Genommen wird er unter anderem vom Aufräumen und Analysieren — also von dem, was der Autovacuum-Daemon selbsttätig und ohne Ankündigung tut — und vom nebenläufigen Indexaufbau. Entscheidend ist, dass er laut Konfliktmatrix mit sich selbst in Konflikt steht: Zwei Vorgänge dieses Modus können nicht gleichzeitig auf derselben Tabelle laufen.
- Autovacuum läuft, Migration startet
- Die Migration wartet, bis der Aufräumlauf fertig ist. Auf einer sehr großen Tabelle kann das Stunden bedeuten — und die Migration hält währenddessen die Anfragen hinter sich auf.
- Migration läuft, Autovacuum will starten
- Der Aufräumlauf wird übersprungen. Passiert das wiederholt, bläht die Tabelle auf, ohne dass jemand einen Fehler sieht.
log_autovacuum_min_duration nicht auf „aus“ steht, wird laut Dokumentation auch dann ein Eintrag geschrieben, wenn ein Aufräumvorgang wegen einer konfliktierenden Sperre übersprungen wurde. Ohne diese Einstellung ist die Kollision unsichtbar — man sieht nur eine Tabelle, die seit Monaten wächst, und eine Migration, die manchmal ungewöhnlich lange braucht.Praktisch heißt das: Eine Auslieferung auf eine große, stark geänderte Tabelle gehört nicht in ein Zeitfenster, in dem erfahrungsgemäß aufgeräumt wird — und ein Blick in die Sitzungsübersicht vor dem Start beantwortet die Frage, ob gerade ein Aufräumlauf auf dem Zielobjekt arbeitet. Das ist derselbe Blick, den auch die Sperr-Zeitgrenze ersetzt; nur erfährt man mit ihm den Grund und nicht nur das Scheitern.
Erweitern, umstellen, zusammenziehen
Das Muster, das eine Auslieferung ohne Stillstand überhaupt erst möglich macht, hat drei Schritte und dauert mehrere Releases. Es ist kein Trick, sondern die Konsequenz daraus, dass alte und neue Anwendungsfassung eine Zeit lang gleichzeitig laufen.
- Erweitern
- Das Neue hinzufügen, ohne das Alte anzutasten. Neue Spalte nullbar, neue Tabelle leer, neuer Index zusätzlich. Die alte Anwendungsfassung merkt nichts.
- Umstellen
- Die Anwendung ausliefern, die auf das Neue schreibt — und, solange nötig, auch noch auf das Alte. Bestandsdaten im Hintergrund nachziehen, in Portionen statt in einem Zug.
- Zusammenziehen
- Erst wenn keine laufende Anwendungsfassung das Alte mehr braucht: das Alte entfernen. Das ist der einzige Schritt, der nicht mehr rückwärtskompatibel ist.
Der Preis steht dem Gewinn deutlich gegenüber. Zwischen Schritt eins und drei liegen Wochen, in denen das Schema beides trägt: doppelte Spalten, doppelte Schreibpfade, Bestandsdaten in zwei Zuständen. Wer den dritten Schritt vergisst — und das passiert oft, weil er nichts Sichtbares bringt —, sammelt dauerhaft Altlasten, die niemand mehr zuzuordnen weiß.
Kurzcheck
Welcher der drei Schritte ist der einzige, der einen Rückweg unmöglich macht?
- Zusammenziehen — das Entfernen des Alten
- Erweitern — die neue Spalte ließe sich nicht mehr entfernen
- Umstellen — die doppelten Schreibpfade lassen sich nicht zurücknehmen
Treffer. Richtig. Erweitern und Umstellen lassen sich zurücknehmen, weil das Alte noch da ist. Sobald es entfernt ist, kann eine ältere Anwendungsfassung nicht mehr laufen.
Die Migration, die auf einen Menschen wartet
Ein Auslieferungsskript, das seine Schemaänderungen in eine Transaktion klammert, tut grundsätzlich das Richtige — die Änderungen werden dann gemeinsam wirksam oder gar nicht. Es erzeugt aber eine neue Gefahr, wenn es in der Mitte stehen bleibt.
Solange die Transaktion offen ist, hält sie alle bisher erlangten Sperren. Bleibt das Skript an einer Rückfrage hängen, an einem abgebrochenen Terminal oder an einer Netzunterbrechung, hält es eine ACCESS EXCLUSIVE-Sperre auf einer Produktivtabelle — auf unbestimmte Zeit.
idle_in_transaction_session_timeout beendet laut Dokumentation jede Sitzung, die innerhalb einer offenen Transaktion länger als angegeben untätig auf eine Client-Anfrage wartet. Voreingestellt ist sie null, also aus. Die Dokumentation nennt ausdrücklich beide Gründe für ihren Einsatz: Erstens halten untätige Sitzungen dann keine Sperren mehr unangemessen lange. Zweitens verhindert eine offene Transaktion selbst dann, wenn sie keine nennenswerten Sperren hält, das Aufräumen kürzlich verstorbener Zeilenversionen, die möglicherweise nur für sie sichtbar sind — längeres Untätigsein trägt also zur Aufblähung der Tabellen bei.Damit schließt sich der Kreis zu zwei anderen Modulen: Dieselbe offene Transaktion, die im Vorfall als Blockierer erscheint, ist im Wartungsmodul die Ursache einer Tabelle, die trotz laufendem Aufräumen weiter aufbläht. Es ist ein Befund mit drei Gesichtern.
Der Rückweg gehört zur Änderung
Bei Anwendungscode ist der Rückweg die vorige Fassung. Bei Schemaänderungen ist er selten so einfach, und die Unterscheidung, die dafür zählt, ist dieselbe wie im Grundlagenmodul: Was lässt sich ohne Datenverlust zurücknehmen?
- Umkehrbar ohne Verlust
- Eine nullbare Spalte hinzufügen, einen Index anlegen, eine Bedingung als
NOT VALIDergänzen, ein Standardwert setzen. - Umkehrbar mit Aufwand
- Eine Spalte umbenennen — zurückbenennen geht, aber alles, was den neuen Namen schon benutzt, bricht.
- Nicht umkehrbar
- Eine Spalte oder Tabelle entfernen, einen Datentyp verengen, Zeilen im Rahmen der Migration löschen. Der Rückweg führt hier über eine Sicherung, nicht über einen Befehl.
Daraus folgt eine praktische Regel für das Auslieferungsfenster: Der zusammenziehende Schritt gehört nicht in dieselbe Auslieferung wie der erweiternde. Zwischen beiden muss mindestens ein Zeitraum liegen, in dem sich zeigt, dass die neue Fassung trägt.
Was das Skript mitbringen muss
Aus allem Vorherigen ergibt sich eine kurze Liste dessen, was in einem Migrationsskript stehen sollte, bevor die erste Strukturänderung kommt.
- Eine Sperr-Zeitgrenze, gesetzt in der Sitzung und nicht in der Serverkonfiguration — damit ein Wartefall zum Fehlschlag wird statt zum Stillstand.
- Eine klare Transaktionsklammer. Mehrere zusammengehörige Änderungen gehören in eine Transaktion; ein Werkzeugaufruf, der bei einem Fehler zurückrollt und einen von null verschiedenen Rückgabewert liefert, macht das Scheitern für die Automatisierung sichtbar.
- Getrennte Befehle statt zusammengefasster, wo die Unterbefehle verschiedene Sperrstufen brauchen — sonst gilt die strengste für alle.
- Keine Datenmigration im selben Zug. Das Umkopieren großer Bestände gehört in portionierte Läufe außerhalb der Strukturänderung, sonst hält eine Transaktion stundenlang Sperren.
- Ein benannter Rückweg — und wenn er „aus der Sicherung“ lautet, gehört das vor der Auslieferung geschrieben, nicht danach herausgefunden.
Die Reihenfolge, die das zusammenhält
Schritt zwei ist der billigste und wird am häufigsten übersprungen. Ein Blick in die Sitzungsübersicht vor der Auslieferung beantwortet die Frage, ob die Migration überhaupt eine Chance hat, ihre Sperre sofort zu bekommen — oder ob sie sich hinter eine Auswertung stellt, die noch eine halbe Stunde läuft.
itsm-operations — hier ging es allein um das, was die Datenbank dabei tut.Jetzt anwenden
Diesen Stoff gibt es als Modul mit bewerteten Entscheidungs-Checks — dieselbe Einführung, danach die Übungen.
Zum Modul →Quellen & Aktualität7 Primärquellen · zuletzt geprüft:
- 01postgresql.org/docs/current/sql-altertable.html
- 02postgresql.org/docs/current/explicit-locking.html
- 03postgresql.org/docs/current/view-pg-locks.html
- 04postgresql.org/docs/current/runtime-config-client.html
- 05postgresql.org/docs/current/indexes-intro.html
- 06postgresql.org/docs/current/app-psql.html
- 07postgresql.org/docs/current/routine-vacuuming.html