🌐 Detecting your location…

So migrieren Sie eine Produktionsdatenbank ohne Ausfallzeiten im Jahr 2026: Vollständiger Leitfaden

⏱️7 min read  ·  1,479 words

Die Migration, die zum Produktionsausfall führt, ist selten komplex. Normalerweise ist es ein einzelnesALTER TABLE Dadurch wurde eine exklusive Sperre für eine große Tabelle vorgenommen, während die Anwendung wartete. Die Migration ohne Ausfallzeiten ist eher eine Disziplin als ein Werkzeug: Teilen Sie jede Änderung in Schritte auf, die einzeln sicher sind, wobei alter und neuer Code gleichzeitig ausgeführt werden.

Das Grundprinzip: Erweitern und verkleinern

Sie können eine Schemaänderung und den Code, der sie benötigt, nicht gleichzeitig bereitstellen. Bei jeder fortlaufenden Bereitstellung werden alte und neue Anwendungsversionen gleichzeitig ausgeführt. Jede Migration muss daher mit beidem kompatibel sein.

Das Expand-Contract-Muster teilt jede Änderung in drei Bereitstellungen auf:

  1. Erweitern Sie – Fügen Sie die neue Struktur hinzu. Alter Code ignoriert es; neuer Code kann es verwenden.
  2. Migrieren — Daten auffüllen und die Anwendung auf die neue Struktur umstellen.
  3. Vertrag – Entfernen Sie die alte Struktur, sobald nichts mehr darauf verweist.

Jeder Schritt wird separat eingesetzt und ist einzeln reversibel. Das macht die gesamte Sequenz sicher.

Beispiel: Umbenennen einer Spalte

Eine Umbenennung sieht trivial aus und ist eine der gefährlichsten Operationen, da sie alten Code sofort zerstört, wenn er ausgeführt wird.

-- ❌ Never do this on a live system.
ALTER TABLE users RENAME COLUMN email TO email_address;
-- Every running instance still querying "email" fails immediately.

Die sichere Reihenfolge:

-- Step 1 (expand): add the new column. Nullable, no default — instant.
ALTER TABLE users ADD COLUMN email_address TEXT;

-- Step 2: deploy code that WRITES both columns and READS the old one.
--   UPDATE users SET email = $1, email_address = $1 WHERE id = $2

-- Step 3: backfill existing rows in batches (see below).

-- Step 4: deploy code that READS the new column and still writes both.

-- Step 5: deploy code that only uses the new column.

-- Step 6 (contract): drop the old column, once nothing references it.
ALTER TABLE users DROP COLUMN email;

Sechs Bereitstellungen für eine Umbenennung fühlen sich übertrieben an, bis das erste Mal, dass eine einstufige Umbenennung zu einem Ausfall führt. Die meisten Teams kommen schließlich zu dem Schluss, dass sich Umbenennungen nicht lohnen, und behalten einfach den ursprünglichen Namen bei.

Welche Vorgänge sperren

Zu wissen, welche Anweisungen eine Blockierungssperre erfordern, ist der größte Teil der Fertigkeit. Auf modernem PostgreSQL:

Betrieb Sicher? Notizen
Nullable-Spalte hinzufügen Sicher Nur Metadaten, sofort
Spalte mit konstantem Standardwert hinzufügen Sicher Kein Umschreiben der Tabelle in modernen Versionen
Spalte mit flüchtigem Standardwert hinzufügen Gefährlich Schreibt die gesamte Tabelle
neu Index hinzufügen Gefährlich Blockiert Schreibvorgänge – GLEICHZEITIG verwenden
NOT NULL zur vorhandenen Spalte hinzufügen Gefährlich Vollständiger Scan unter Sperre – verwenden Sie zuerst eine CHECK-Einschränkung
Spaltentyp ändern Gefährlich Normalerweise eine vollständige Neufassung
Fremdschlüssel hinzufügen Gefährlich Validiert alle Zeilen – fügen Sie zuerst NOT VALID hinzu
Spalte löschen Sicher Nur Metadaten, bricht aber alten Code

Überprüfen Sie immer anhand Ihrer spezifischen Datenbankversion. Das Verhalten dieser Vorgänge hat sich im Vergleich zu den letzten Versionen erheblich verbessert, und für ältere Versionen geschriebene Ratschläge sind oft unnötig vorsichtig – oder umgekehrt gefährlich veraltet.

Indizes sicher hinzufügen

-- ❌ Blocks all writes to the table for the duration.
CREATE INDEX idx_users_email ON users(email);

-- ✅ Builds without blocking writes.
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);

Zwei Dinge, die Sie überCONCURRENTLYwissen sollten . Es kann nicht innerhalb eines Transaktionsblocks ausgeführt werden, was bedeutet, dass die meisten Migrationsframeworks eine explizite Konfiguration benötigen, um dies zu ermöglichen. Und es kann teilweise fehlschlagen, sodass ein ungültiger Index zurückbleibt, der bereinigt werden muss.

-- Find invalid indexes left behind by a failed concurrent build
SELECT indexrelid::regclass AS index_name
FROM pg_index
WHERE NOT indisvalid;

-- Drop and retry
DROP INDEX CONCURRENTLY idx_users_email;

Hinzufügen von NOT NULL ohne lange Sperre

Hinzufügen vonNOT NULL Scannt direkt die gesamte Tabelle, während eine Sperre gehalten wird. Eine validierte CHECK-Einschränkung erreicht die gleiche Garantie in zwei nicht blockierenden Schritten.

-- 1. Add the constraint without validating existing rows — instant.
ALTER TABLE users
  ADD CONSTRAINT users_email_not_null
  CHECK (email IS NOT NULL) NOT VALID;

-- 2. Validate separately. Takes a weaker lock that allows reads and writes.
ALTER TABLE users VALIDATE CONSTRAINT users_email_not_null;

Für Fremdschlüssel gilt aus demselben Grund das gleiche NOT VALID/VALIDATE-Muster.

Hinterfüllung großer Tische

Ein einzelnesUPDATE über Millionen von Zeilen hält Sperren, erzeugt ein enormes Protokollvolumen für das Vorausschreiben und kann die Replikation blockieren. Stapeln Sie es.

-- ❌ One statement, one very long transaction, many locked rows.
UPDATE users SET email_address = email;
-- ✅ Batched, resumable, and gentle on replication.
DO $$
DECLARE
  batch_size INT := 5000;
  affected   INT;
BEGIN
  LOOP
    UPDATE users
    SET email_address = email
    WHERE id IN (
      SELECT id FROM users
      WHERE email_address IS NULL AND email IS NOT NULL
      ORDER BY id
      LIMIT batch_size
      FOR UPDATE SKIP LOCKED
    );

    GET DIAGNOSTICS affected = ROW_COUNT;
    EXIT WHEN affected = 0;

    COMMIT;
    PERFORM pg_sleep(0.1);   -- let replicas catch up
  END LOOP;
END $$;

FOR UPDATE SKIP LOCKED verhindert, dass der Backfill Zeilen blockiert, die Ihre Anwendung gerade aktualisiert. Der kurze Ruhezustand zwischen den Stapeln verhindert, dass die Replikationsverzögerung zunimmt, und es ist der Schritt, den die Leute unterlassen und bereuen.

Führen Sie bei sehr großen Tabellen den Backfill als separaten Auftrag und nicht innerhalb einer Migration aus, damit eine Bereitstellung nie darauf wartet.

Legen Sie ein Sperrzeitlimit fest

Diese einzelne Einstellung verhindert die meisten Migrationsausfälle. Ohne sie wartet eine Migration, die keine Sperre erhalten kann, auf unbestimmte Zeit – und jede dahinter in der Warteschlange stehende Abfrage wartet ebenfalls, und das ist, wie manALTER TABLE blockiert eine gesamte Anwendung.

-- Fail fast instead of queueing behind a long-running query.
SET lock_timeout = '3s';
SET statement_timeout = '30s';

ALTER TABLE users ADD COLUMN email_address TEXT;

Wenn das Zeitlimit überschritten wird, versuchen Sie es später noch einmal. Eine fehlgeschlagene Migration, die Sie erneut ausführen können, ist wesentlich besser als eine gesperrte Tabelle während des Spitzenverkehrs.

Migration zwischen Datenbanken

Bei der Umstellung auf eine neue Datenbank-Engine oder -Instanz wird dasselbe Prinzip in größerem Maßstab angewendet.

  1. Replizieren — Richten Sie eine logische Replikation ein oder ändern Sie die Datenerfassung von alt auf neu. Lassen Sie es vollständig aufholen.
  2. Duales Lesen – Lesen aus der alten Datenbank und Lesen aus der neuen im Hintergrund, um die Ergebnisse zu vergleichen. Protokollieren Sie jede Nichtübereinstimmung und beheben Sie die Ursachen, bevor Sie fortfahren.
  3. Duales Schreiben — beiden schreiben. Das Alte bleibt maßgebend.
  4. Überschneiden – Switch liest in die neue Datenbank. Schreiben Sie beiden weiter.
  5. Stilllegung– Hören Sie auf, in die alte Datenbank zu schreiben, sobald Sie sicher sind, dass Sie sie über einen sinnvollen Zeitraum als Rollback-Pfad beibehalten haben.

Die Vergleichsphase in Schritt 2 macht dies sicher. Wenn Sie es überspringen, müssen Sie Datenunterschiede nach der Umstellung entdecken, wenn ein Rollback teuer ist.

Rollback-Planung

Jede Migration braucht eine Antwort auf die Frage „Was ist, wenn das falsch ist?“ bevor es läuft.

Additive Änderungen werden trivial zurückgesetzt – Das Löschen einer Spalte, die Sie gerade hinzugefügt haben, ist sicher, da nichts davon abhängt.

Destruktive Änderungen werden nicht rückgängig gemacht. Sobald eine Spalte gelöscht wird, sind die Daten verschwunden. Genau aus diesem Grund kommen Vertragsschritte zuletzt und erst nach einer Zeit des Vertrauens.

Auch Hinterfüllungen benötigen Reversibilität. Wenn Sie eine Spalte überschreiben, bewahren Sie die ursprünglichen Werte irgendwo auf, bis Sie sicher sind.

Schreiben Sie niemals eine Migration, deren Abwärtsschritt Daten löscht. Wenn bei einem Rollback Informationen verloren gehen würden, sollte die Migration nicht automatisch rückgängig gemacht werden – machen Sie den Wiederherstellungspfad zu einer bewussten, manuellen Entscheidung.

Checkliste vor dem Flug

  • Testen Sie eine wiederhergestellte Kopie der Produktionsdaten im Produktionsmaßstab
  • Wissen Sie, ob jede Anweisung eine blockierende Sperre
  • benötigt Setzelock_timeout undstatement_timeout
  • Stapeln Sie jeden Backfill und führen Sie ihn außerhalb der Bereitstellung aus
  • Bestätigen Sie, dass sowohl der alte als auch der neue Anwendungscode mit dem Zwischenschema
  • funktionieren Beobachten Sie die Replikationsverzögerung während und nach
  • Verfügen Sie über ein verifiziertes Backup und wissen Sie, wie lange eine Wiederherstellung dauert
  • Bereitstellung bei geringem Datenverkehr, auch wenn keine Sperrung zu erwarten ist

Fazit

Die Migration ohne Ausfallzeiten hängt von einigen Disziplinen ab:Teilen Sie jede Änderung in Erweiterungs-, Migrations- und Vertragsphasen auf, die separat bereitgestellt werden. wissen, welche Anweisungen blockierende Sperren erfordern und CONCURRENTLY und NOT VALID verwenden, um sie zu vermeiden; Batch-Backfills mit Pausen, damit die Replikation mithalten kann; und legen Sie immer ein Sperrzeitlimit fest, damit eine Migration schnell fehlschlägt, anstatt Ihre gesamte Anwendung dahinter in die Warteschlange zu stellen. Die zusätzlichen Bereitstellungen fühlen sich bis zur ersten Migration wie ein Mehraufwand an, der andernfalls zu einem Ausfall geführt hätte.

MD Rafikul Islam

Written by

MD Rafikul Islam is a software developer and the editor of TechPulse. He writes about developer tooling, hardware, and the practical decisions that come up in day-to-day engineering work — which laptop to buy, which framework to commit to, why a build broke at 2am. He tests the tools he writes about and says plainly when something is not worth the money. Corrections and corrections requests are welcome at rony.yf25@gmail.com.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা