Wenn das Löschen einer Datenbankspalte die Produktion lahmlegt: Umgang mit destruktiven Schemaänderungen
Sie haben eine Datenbankmigration, die eine ungenutzte Spalte entfernt. Das SQL sieht sauber aus. Die Migration läuft fehlerfrei. Doch fünf Minuten später schlagen die Alarme an. Die Produktionsanwendung wirft Fehler, weil ein Codeabschnitt immer noch auf diese Spalte verweist. Was wie eine einfache Bereinigung aussah, hat gerade einen Ausfall verursacht.
Dieses Szenario ist häufiger, als die meisten Teams zugeben. Das Problem ist nicht die Migration selbst. Das Problem ist die Annahme, dass das Entfernen von etwas aus der Datenbank sicher ist, nur weil man denkt, dass es niemand mehr verwendet.
Was eine Änderung destruktiv macht
Datenbankänderungen fallen in zwei Kategorien. Hinzufügende Änderungen fügen etwas Neues hinzu: eine neue Spalte, eine neue Tabelle, einen neuen Index. Diese sind im Allgemeinen sicher, weil vorhandener Code einfach ignoriert, was er nicht kennt.
Destruktive Änderungen entfernen, benennen um oder verändern bestehende Strukturen. Das Löschen einer Spalte, das Umbenennen einer Tabelle, das Ändern eines Spaltentyps oder das Entfernen einer Einschränkung sind alles destruktive Änderungen. Das Risiko ist offensichtlich: Wenn eine laufende Anwendung noch von dieser Struktur abhängt, wird sie in dem Moment brechen, in dem die Änderung angewendet wird.
Die Gefahr wird durch moderne Bereitstellungsstrategien noch verstärkt. Rolling Updates und Blue-Green-Deployments bedeuten, dass alte und neue Anwendungsversionen für Minuten oder Stunden nebeneinander laufen. Eine destruktive Migration, die während des Deployments ausgeführt wird, wird sofort die alten Instanzen zerstören, die noch Traffic bedienen.
Das Mehrphasen-Migrationsmuster
Der sicherste Ansatz ist, niemals etwas in einem Schritt zu löschen. Stattdessen unterteilen Sie destruktive Änderungen in mehrere Phasen. Jede Phase muss mit der zu diesem Zeitpunkt laufenden Anwendungsversion kompatibel sein.
Das folgende Flussdiagramm veranschaulicht die drei Phasen einer sicheren Spaltenumbenennung und zeigt, welche Anwendungsversionen in jedem Schritt kompatibel sind.
Betrachten Sie die Umbenennung einer Spalte von status in status_code. Eine einzelne Migration, die die Spalte umbenennt, würde jeden Code zerstören, der immer noch status liest. Der Mehrphasen-Ansatz sieht so aus:
Phase 1: Fügen Sie die neue Spalte hinzu, ohne die alte zu entfernen. Kopieren Sie Daten von der alten in die neue Spalte. Aktualisieren Sie den Anwendungscode, um von der neuen Spalte zu lesen, während Sie weiterhin in beide schreiben. Stellen Sie diese Änderung bereit.
Phase 2: Nachdem Sie bestätigt haben, dass alle Anwendungsinstanzen die neue Spalte verwenden, stellen Sie das Schreiben in die alte Spalte ein. Aktualisieren Sie den Code, um nur noch auf status_code zu verweisen. Stellen Sie erneut bereit.
Phase 3: Sobald Sie sicher sind, dass kein laufender Code die alte Spalte berührt, löschen Sie sie in einer separaten Migration. Planen Sie dies in verkehrsarmen Stunden.
Das gleiche Muster gilt für das Entfernen von Tabellen. Erstellen Sie eine View oder eine neue Tabelle, die die alte Funktionalität ersetzt. Leiten Sie den Anwendungscode auf die neue Struktur um. Warten Sie, bis kein Code mehr auf die alte Tabelle verweist. Dann löschen Sie sie.
Soft Delete als Sicherheitsnetz
Manchmal möchten Sie Daten aus der Sicht der Anwendung entfernen, ohne sie tatsächlich aus der Datenbank zu löschen. Hier hilft Soft Delete.
Führen Sie keine DELETE-Anweisung aus, sondern fügen Sie eine Spalte wie deleted_at oder is_active hinzu. Die Anwendung filtert gelöschte Zeilen mit einer WHERE-Klausel heraus. Die Daten bleiben in der Tabelle für Audits, Wiederherstellung oder unerwartete Abhängigkeiten von anderen Funktionen.
Soft Delete ist besonders nützlich, wenn Sie nicht ganz sicher sind, ob die Daten noch benötigt werden. Es gibt Ihnen einen Sicherheitspuffer. Wenn etwas kaputt geht, können Sie die Sichtbarkeit ohne eine Datenbankwiederherstellung wiederherstellen. Der Nachteil ist, dass Ihre Tabellen größer werden und Abfragen den Filter berücksichtigen müssen. Aber für viele Teams ist dieser Kompromiss die Sicherheit wert.
Sorgfältiger Umgang mit Constraints
Das Entfernen einer Einschränkung wie eines Fremdschlüssels oder einer Unique-Constraint ist weniger riskant als das Entfernen von Daten, hat aber dennoch Konsequenzen. Constraints erzwingen die Datenintegrität. Wenn Ihr Anwendungscode darauf angewiesen ist, dass die Datenbank doppelte Einträge oder verwaiste Datensätze verhindert, kann das Entfernen der Einschränkung zu Datenkorruption führen.
Bevor Sie eine Einschränkung entfernen, prüfen Sie die Codebasis, um zu bestätigen, dass keine Logik davon abhängt. Wenn Ihre Datenbank dies unterstützt, sollten Sie die Einschränkung zunächst deaktivieren, anstatt sie zu löschen. So können Sie die Auswirkungen testen, ohne die Möglichkeit zu verlieren, sie wieder zu aktivieren.
Praktische Checkliste für destruktive Änderungen
- Stellen Sie sicher, dass kein laufender Anwendungscode auf die Struktur verweist, die Sie entfernen möchten. Überprüfen Sie sowohl die aktuelle Version als auch alle laufenden Deployments.
- Teilen Sie die Änderung in mindestens zwei Migrationen auf: eine zum Hinzufügen der neuen Struktur und Umleiten des Codes, eine weitere zum Entfernen der alten Struktur nach einer Wartezeit.
- Führen Sie destruktive Migrationen getrennt von Feature-Deployments durch. Bündeln Sie das Löschen einer Spalte nicht mit der Veröffentlichung eines neuen Endpunkts.
- Planen Sie destruktive Änderungen in verkehrsarmen Fenstern. Selbst bei mehrphasiger Planung sind unerwartete Probleme einfacher zu handhaben, wenn weniger Benutzer betroffen sind.
- Entfernen Sie nach dem Löschen alter Strukturen in einer Folgemigration Überreste wie umbenannte Spalten oder deaktivierte Constraints. Aber denken Sie daran: Auch die Bereinigung ist destruktiv, wenden Sie also denselben mehrphasigen Ansatz an.
Das Kernprinzip
Löschen Sie niemals etwas, auf das eine laufende Anwendung möglicherweise noch zugreift. Das klingt offensichtlich, aber es ist der häufigste Fehler, den Teams bei Datenbankmigrationen machen. Der Druck, das Schema sauber zu halten, die Annahme, dass "das niemand mehr verwendet", und der Wunsch, eine saubere Migration statt mehrerer kleiner auszuliefern, treiben Teams alle zu riskanten einstufigen Löschungen.
Mehrphasen-Migrationen brauchen mehr Zeit und mehr Deployments. Aber sie verhindern die Art von Produktionsausfall, der eine einfache Schema-Bereinigung in einen Notfall-Rollback verwandelt. Ein sauberes Schema ist eine kaputte Anwendung nicht wert.