Warum Datenbank-Deployments nicht wie Anwendungs-Deployments behandelt werden können

Stellen Sie sich vor: Sie betreiben einen E-Commerce-Shop an einem geschäftigen Nachmittag. Nutzer durchstöbern Produkte, legen Artikel in den Warenkorb und schließen Bestellungen ab. Währenddessen führt Ihr Datenbank-Team eine Migration durch, um eine Spalte discount_price zur Tabelle products hinzuzufügen. Plötzlich bricht die Geschwindigkeit der Seite ein. Produktsuchen laufen ins Leere. Bezahlvorgänge schlagen fehl. Nutzer beschweren sich in den sozialen Medien.

Was ist passiert? Die Datenbank hat die Tabelle products während der Schema-Änderung gesperrt, und jede Abfrage, die Produktdaten lesen oder schreiben wollte, musste warten. Die Anwendung selbst war in Ordnung. Die Server waren gesund. Aber die Datenbank war damit beschäftigt, sich selbst vor Datenkorruption während der Schema-Änderung zu schützen.

Dieses Szenario tritt in Teams auf, die Datenbank-Deployments genauso behandeln wie Anwendungs-Deployments. Der Unterschied ist grundlegend: Sie können eine Anwendung anhalten, durch eine neue Version ersetzen und in Sekunden neu starten. Eine Datenbank muss während der Änderung weiterhin Nutzer bedienen.

Wie Locks funktionieren und warum sie wehtun

Wenn Sie einen Befehl zum Ändern einer Tabellenstruktur ausführen, muss die Datenbank sicherstellen, dass kein anderer Vorgang während der Änderung dieselben Daten modifiziert. So gewährleisten Datenbanken Konsistenz. Um dies durchzusetzen, erwirbt die Datenbank eine Sperre (Lock) auf der Tabelle oder bestimmten Zeilen. Solange diese Sperre aktiv ist, muss jede andere Abfrage, die Daten auf derselben Tabelle lesen oder schreiben möchte, warten.

Manche Schema-Änderungen sind schnell. Das Hinzufügen einer Spalte mit einem Standardwert NULL in PostgreSQL kann beispielsweise in Millisekunden abgeschlossen werden, ohne Lesevorgänge zu blockieren. Andere Operationen sind jedoch nicht so freundlich. Das Erstellen eines Index auf einer großen Tabelle, das Ändern des Datentyps einer Spalte oder das Löschen einer Spalte kann die Tabelle für Minuten oder sogar Stunden sperren.

Betrachten Sie den Unterschied zwischen diesen beiden SQL-Anweisungen:

-- Sicher: Fügt eine nullable Spalte hinzu, dauert Millisekunden, kein Lock
ALTER TABLE products ADD COLUMN discount_price DECIMAL(10,2);

-- Gefährlich: Schreibt die gesamte Tabelle neu, sperrt große Tabellen für Minuten
ALTER TABLE products ALTER COLUMN price TYPE DECIMAL(12,2);

Die erste Anweisung fügt eine Spalte hinzu, die NULL sein kann, sodass die Datenbank nur Metadaten aktualisiert. Die zweite Anweisung ändert den Datentyp einer vorhandenen Spalte und zwingt die Datenbank, jede Zeile in der Tabelle neu zu schreiben. Während des Neuschreibens ist die Tabelle gesperrt, und alle Abfragen gegen products müssen warten.

Die eigentliche Gefahr ist der Kaskadeneffekt. Eine Abfrage, die auf eine Sperre wartet, verlangsamt nicht nur eine einzelne Funktion. Sie kann andere Abfragen blockieren, die von derselben Tabelle abhängen. In extremen Fällen reagiert die Anwendung überhaupt nicht mehr, weil alle Datenbank-Threads von Abfragen belegt sind, die auf Sperren warten. Nutzer sehen endlose Ladekreise oder Timeout-Fehler. Aus Nutzersicht ist die Anwendung ausgefallen. Die Datenbank ist nur damit beschäftigt, sich selbst zu schützen.

Nicht alle Schema-Änderungen sind gleich

Verschiedene Datenbanken handhaben Sperren unterschiedlich, und nicht alle Schema-Operationen haben das gleiche Risiko. Zu verstehen, welche Operationen sicher und welche gefährlich sind, ist entscheidend für die Planung von Datenbank-Deployments.

PostgreSQL kann eine Spalte mit einem NULL-Standardwert hinzufügen, ohne Lesevorgänge zu blockieren. MySQLs ONLINE DDL-Operationen können ohne Sperrung der Tabelle für gleichzeitige DML ausgeführt werden, benötigen aber dennoch eine kurze Metadaten-Sperre zu Beginn und am Ende. Selbst Operationen, die als „online“ oder „zero-downtime“ bezeichnet werden, müssen in einer Umgebung getestet werden, die die Produktion widerspiegelt.

Operationen, die typischerweise die meisten Probleme verursachen:

  • Erstellen von Indizes auf großen Tabellen
  • Ändern von Spaltendatentypen
  • Löschen von Spalten
  • Hinzufügen von Spalten mit Nicht-NULL-Standardwerten (in manchen Datenbanken)
  • Umbenennen von Spalten oder Tabellen
  • Ausführen von ALTER TABLE-Anweisungen, die die gesamte Tabelle neu schreiben

Operationen, die im Allgemeinen sicherer sind:

  • Hinzufügen von Spalten mit NULL-Standardwert (in PostgreSQL)
  • Hinzufügen von Indizes mit CONCURRENTLY (in PostgreSQL)
  • Erstellen neuer Tabellen
  • Hinzufügen neuer Spalten mit ONLINE DDL (in MySQL, für unterstützte Operationen)

Der Schlüssel liegt darin, zu wissen, in welche Kategorie jede Operation für Ihr spezifisches Datenbanksystem fällt, und die tatsächliche Ausführungszeit in einer Staging-Umgebung zu testen, bevor Sie sie in der Produktion ausführen.

Warum Rollbacks schwieriger sind als gedacht

Anwendungs-Rollbacks sind unkompliziert. Sie stellen die vorherige Version des Codes bereit, und die Anwendung bedient Anfragen wieder mit der alten Logik. Datenbank-Rollbacks sind nicht so.

Wenn Sie eine Spalte hinzufügen und dann ein Rollback durchführen müssen, können Sie die Spalte nicht einfach „undeployen“. Sie müssen eine weitere Migration ausführen, um sie zu entfernen. Dieser Entfernungsvorgang selbst könnte die Tabelle sperren. Wenn die Migration Datentypen geändert oder Tabellen umstrukturiert hat, könnte ein Rollback die Konvertierung der Daten zurück in das alte Format erfordern, was langsam und riskant sein kann.

Diese Asymmetrie verändert die Art und Weise, wie Sie über Risiken nachdenken. Bei Anwendungen können Sie schnell bereitstellen und bei Problemen ein Rollback durchführen. Bei Datenbanken müssen Sie Probleme von vornherein verhindern, da der Wiederherstellungspfad schmerzhaft ist.

Praktische Strategien für sicherere Datenbank-Deployments

Teams, die Datenbank-Deployments gut handhaben, verlassen sich nicht auf Glück. Sie entwickeln Prozesse, die die Wahrscheinlichkeit von Lock-bedingten Vorfällen reduzieren und die Wiederherstellung handhabbar machen, wenn etwas schiefgeht.

Planen Sie Migrationen in verkehrsarmen Zeiten. Eine Schema-Änderung um 14 Uhr an einem Dienstag durchzuführen, ist ein Rezept für Ärger. Planen Sie sie für 2 Uhr morgens am Sonntag oder in einem anderen verkehrsarmen Fenster Ihrer Anwendung. Wenn Ihre Anwendung globale Nutzer bedient, müssen Sie Migrationen möglicherweise in kleinere Schritte aufteilen, die während mehrerer verkehrsarmer Fenster ausgeführt werden können.

Teilen Sie große Änderungen in kleine Schritte auf. Führen Sie nicht eine Migration durch, die drei Spalten hinzufügt, zwei Indizes erstellt und einen Datentyp ändert, sondern teilen Sie sie in separate Migrationen auf. Jede Migration sollte klein genug sein, um schnell abgeschlossen und ohne Kaskadeneffekte zurückgesetzt werden zu können.

Messen Sie die Ausführungszeit im Staging. Bevor Sie eine Migration in der Produktion ausführen, führen Sie sie in einer Staging-Umgebung mit ähnlichem Datenvolumen und Traffic-Mustern durch. Wenn die Migration im Staging 30 Sekunden dauert, könnte sie in der Produktion mit echten Daten 30 Minuten dauern. Messen und planen Sie entsprechend.

Überwachen Sie Lock-Wartezeiten während der Migration. Richten Sie Alarme ein, die ausgelöst werden, wenn Abfragen länger als ein paar Sekunden auf Sperren warten. Wenn Sie sehen, dass die Lock-Wartezeiten steigen, benötigen Sie eine Prozedur, um die Migration abzubrechen, bevor sie einen vollständigen Ausfall verursacht.

Haben Sie eine klare Abbruchprozedur. Definieren Sie genau, was zu tun ist, wenn eine Migration zu lange dauert oder Lock-Konflikte verursacht. Dies könnte bedeuten, den Migrationsprozess abzubrechen, zur vorherigen Schema-Version zurückzukehren oder während der Fertigstellung der Migration auf ein Read-Replica umzuschalten.

Eine praktische Checkliste für Datenbank-Deployments

Bevor Sie eine Schema-Änderung in der Produktion durchführen, gehen Sie diese Checkliste durch:

  • Ist diese Operation für gleichzeitige Lese- und Schreibvorgänge in Ihrem Datenbanksystem sicher?
  • Haben Sie die Migration in einer Staging-Umgebung mit ähnlichem Datenvolumen getestet?
  • Wie ist die geschätzte Ausführungszeit basierend auf den Staging-Tests?
  • Ist die Migration für ein verkehrsarmes Fenster geplant?
  • Haben Sie einen Rollback-Plan, der keine weitere riskante Migration erfordert?
  • Sind Überwachungsalarme für Lock-Wartezeiten konfiguriert?
  • Kennt das Team die Abbruchprozedur, falls etwas schiefgeht?

Der Kernunterschied

Anwendungs-Deployment bedeutet, Code auszutauschen. Datenbank-Deployment bedeutet, Live-Daten zu transformieren, während Nutzer bedient werden. Dies sind grundlegend unterschiedliche Operationen, die unterschiedliche Strategien, unterschiedliche Risikobewertungen und unterschiedliche Rollback-Pläne erfordern.

Die Teams, die mit Datenbank-Deployments erfolgreich sind, sind diejenigen, die diesen Unterschied respektieren. Sie hängen Migrationsschritte nicht einfach an dieselbe Pipeline an, die Anwendungscode bereitstellt. Sie entwerfen separate Workflows mit angemessenen Sicherheitsvorkehrungen, Testverfahren und Überwachung.

Wenn Sie das nächste Mal eine Datenbankänderung planen, fragen Sie sich zuerst: „Was passiert mit den Nutzern, während diese Migration läuft?“ Die Antwort auf diese Frage wird Ihnen sagen, ob Sie bereit für das Deployment sind.