Datenbank-Replikation und Failover als Diagramm: Primary, Replikas und Schutz vor Split-Brain

Ein Replikationsdiagramm zeigt, wohin Schreibzugriffe gehen, wie alt Lesezugriffe sein können, wer über ein Failover entscheidet und was zwei Primaries verhindert.

· Gründer & Entwickler

Veröffentlicht am

Datenbank-Replikation und Failover als Diagramm: Primary, Replikas und Schutz vor Split-Brain

Die meisten Replikationsdiagramme zeigen einen Primary, zwei Replikas und Pfeile dazwischen. Das Bild stimmt und ist im Incident trotzdem fast nutzlos, weil es nicht sagt, welcher Node gerade Schreibzugriffe annimmt, welche Replika veraltete Daten liefern kann und was passiert, wenn der Primary nicht mehr antwortet.

Ein nützliches Replikationsdiagramm beantwortet vier Fragen. Dieser Guide arbeitet sie mit PostgreSQL, Patroni und etcd durch und zeichnet dann das Ergebnis. Dieselben Fragen gelten für MySQL, für andere Failover-Manager und für Managed Services.

Vier Fragen, die jedes Replikationsdiagramm beantworten muss

  1. Wohin gehen Schreibzugriffe? Es muss genau einen Primary geben, und Clients brauchen einen Weg, ihn nach einem Failover zu finden.
  2. Wie veraltet können Lesezugriffe sein? Jede Replika ist synchron oder asynchron, und asynchrone hinken hinterher.
  3. Wer entscheidet über ein Failover? Irgendetwas muss den Ausfall erkennen, eine Replika auswählen und sie befördern.
  4. Was verhindert zwei Primaries? Ein abgeschnittener Primary muss aufhören, Schreibzugriffe anzunehmen, bevor ein anderer Node befördert wird.

Synchrone und asynchrone Replikation

Streaming-Replikation in PostgreSQL ist standardmäßig asynchron. Der Primary bestätigt einen Commit, bevor der Standby ihn hat; ein Absturz kann also die Transaktionen kosten, die noch nicht angekommen waren. Die PostgreSQL-Dokumentation sagt es klar: Die Menge des Datenverlusts ist proportional zur Replikationsverzögerung zum Zeitpunkt des Failovers.

Synchrone Replikation lässt Commits auf einen oder mehrere Standbys warten, die in synchronous_standby_names stehen. Wie lange sie warten, hängt von synchronous_commit ab:

synchronous_commit Ein Commit kehrt zurück, nachdem der synchrone Standby ihn
remote_apply angewendet hat, sodass Abfragen auf dem Standby ihn sehen
on (Standard) auf dauerhaften Speicher geschrieben hat
remote_write an sein Betriebssystem übergeben hat, noch ohne Flush
local gar nicht haben muss; gewartet wird nur auf den lokalen Flush
off gar nicht haben muss; nicht einmal auf die lokale Platte wird gewartet

Der Preis ist Latenz: Jeder Commit enthält einen Roundtrip zum Standby, also halte synchrone Standbys in einer nahen Zone, nicht in einer anderen Region. Und wenn der einzige synchrone Standby verschwindet, können Commits ewig warten. Nenne mehr als einen Kandidaten oder lass einen Failover-Manager die Einstellung für dich ändern.

Lese-Replikas und Lese-Routing

Eine asynchrone Replika ist ein guter Ort für Reports und leselastige Endpunkte, solange der Code weiß, dass die Daten hinterherhinken können. Auf dem Primary zeigt die View pg_stat_replication für jeden Standby write_lag, flush_lag und replay_lag; replay_lag gibt ungefähr an, wie lange es dauert, bis ein Commit auf der Replika sichtbar wird.

Entscheide pro Abfrage, welche veraltete Daten lesen dürfen. Wer gerade ein Formular gespeichert hat und die Seite neu lädt, sollte vom Primary lesen. Ein Dashboard kann von der Replika lesen.

Failover: wer entscheidet

PostgreSQL erkennt einen ausgefallenen Primary nicht selbst und befördert auch keinen Standby; die Dokumentation überlässt das externen Tools. Patroni ist eine verbreitete Wahl. Auf jedem Node läuft ein Patroni-Agent, und die Agents teilen ihren Zustand über einen verteilten Konfigurationsspeicher wie etcd:

  • Der Primary hält einen Leader-Key mit TTL und erneuert ihn in jeder Schleife (loop_wait, standardmäßig 10 Sekunden).
  • Kann er den Key ttl Sekunden lang nicht erneuern (standardmäßig 30), läuft der Key ab, und die berechtigten Replikas starten ein Leader-Rennen.
  • Der beste Kandidat übernimmt den Key und befördert sich selbst; die anderen folgen ihm.

Clients finden den neuen Primary über einen Proxy. Patronis REST-API beantwortet GET /primary nur auf dem Node mit dem Leader-Key mit HTTP 200; HAProxy kann Schreibzugriffe also an den Node schicken, der Ja sagt:

listen primary
    bind *:5000
    option httpchk OPTIONS /primary
    http-check expect status 200
    default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
    server pg-a 10.0.1.10:5432 check port 8008
    server pg-b 10.0.2.10:5432 check port 8008
    server pg-c 10.0.3.10:5432 check port 8008

Ein zweiter Listener auf Port 5001 prüft /replica?lag=16MB und nimmt eine Replika heraus, die mehr als 16 MB WAL hinterherhinkt. Betreib HAProxy auf mehr als einer Maschine, sonst wird er zum Single Point of Failure, den du gerade beseitigen wolltest.

Split-Brain: was zwei Primaries verhindert

Zwei Primaries, die Schreibzugriffe annehmen, bedeuten zwei auseinanderlaufende Historien, und eine davon wird verworfen. Die PostgreSQL-Dokumentation nennt das Gegenmittel STONITH: Dem alten Primary muss gesagt werden, dass er nicht mehr Primary ist. Patroni schichtet mehrere Schutzmechanismen übereinander:

  • Die Lease. Ein Primary, der den Leader-Key nicht aktualisieren kann, nimmt keine Schreibzugriffe mehr an: Patroni stoppt PostgreSQL oder stuft es herab. Ist der Speicher selbst länger als retry_timeout nicht erreichbar, stuft Patroni den Primary herab, sofern der DCS-Failsafe-Modus nicht aktiv ist.
  • Ein Watchdog. Hängt der Patroni-Prozess, setzt ein Linux-Watchdog die Maschine zurück. Patroni stellt ihn so ein, dass er 5 Sekunden vor Ablauf der TTL auslöst.
  • Ein Quorum. etcd braucht die Mehrheit seiner Mitglieder, zwei von drei, um einen Schreibzugriff anzunehmen. Drei Mitglieder in drei Zonen überstehen den Verlust einer Zone, und eine abgetrennte Minderheit kann niemanden wählen.
  • Ein einziger Prozess hat das Sagen. Nur Patroni darf PostgreSQL starten. Ein Systemdienst, der Postgres nach einem Absturz selbst neu startet, kann einen zweiten Primary zurückbringen.

Kommt der alte Primary zurück, muss er als Replika wieder beitreten. pg_rewind synchronisiert sein Datenverzeichnis mit dem neuen Primary, ohne unveränderte Datenblöcke zu kopieren; Patroni führt es aus, wenn use_pg_rewind aktiv ist.

# Patroni dynamic configuration (patronictl edit-config)
ttl: 30
loop_wait: 10
retry_timeout: 10
synchronous_mode: on        # promote only a node that has every acknowledged commit
postgresql:
  use_pg_rewind: true
  parameters:
    wal_log_hints: "on"

# Local patroni.yml tags
# pg-a: noloadbalance: true            (failover target, no reads)
# pg-c: nosync: true, nofailover: true (reads only)

Mit aktivem synchronous_mode befördert Patroni nur einen Standby, von dem es weiß, dass er jeden als erfolgreich gemeldeten Commit hat. Der Preis ist Verfügbarkeit: Fällt der Primary aus, während kein synchroner Standby verbunden ist, befördert Patroni niemanden, und Schreibzugriffe bleiben aus, bis jemand eingreift.

Replikation ist kein Backup

Ein DROP TABLE erreicht jede Replika in Millisekunden. Betreib kontinuierliche WAL-Archivierung und regelmäßige Basis-Backups; damit kannst du per Point-in-Time-Recovery jeden Zeitpunkt nach dem Basis-Backup wiederherstellen. Teste Wiederherstellungen nach Plan.

Das Diagramm

Drei Availability Zones mit PostgreSQL-Nodes. HAProxy schickt Schreibzugriffe des Bestellservice an pg-b, den Primary, und Lesezugriffe an pg-c, eine asynchrone Replika. pg-b streamt WAL synchron an pg-a und asynchron an pg-c, hält den Leader-Key in etcd und archiviert WAL.

11 Komponenten · 8 Verbindungen

In Archboard öffnen
Diagramminhalt
  • Order service
  • HAProxy
  • AZ a
  • AZ b
  • AZ c
  • pg-a · sync standby
  • pg-b · primary
  • pg-c · async replica
  • etcd
  • WAL archive + base backups
  • SQL
  • writes :5000
  • reads :5001
  • WAL (sync)
  • WAL (async)
  • leader key, TTL 30 s
  • races if key expires
  • archived WAL
Ein PostgreSQL-Cluster über drei Zonen, verwaltet von Patroni: Schreibzugriffe über HAProxy zum Primary, ein synchroner Standby für das Failover, eine asynchrone Replika für Lesezugriffe, ein Leader-Key in etcd und ein WAL-Archiv für Point-in-Time-Recovery.

So liest du es:

  • Schreibzugriffe. Der Bestellservice verbindet sich mit HAProxy. Port 5000 geht an pg-b, den einzigen Node, der /primary mit 200 beantwortet.
  • Lesezugriffe. Port 5001 geht an pg-c, eine asynchrone Replika in einer anderen Zone. pg-a nimmt keine Lesezugriffe an.
  • Replikation. pg-b streamt WAL synchron an pg-a (durchgezogen) und asynchron an pg-c (gestrichelt).
  • Failover. pg-b erneuert den Leader-Key in etcd. Läuft der Key ab, bewirbt sich pg-a, der synchrone Standby, darum. pg-c ist mit nofailover markiert.
  • Wiederherstellung. pg-b archiviert WAL und Basis-Backups im Objektspeicher.
  • Ziele. Die Notiz oben links enthält RPO, RTO, das Lag-Budget und die Backup-Aufbewahrung.

Was du in einem Replikationsdiagramm beschriften solltest

  • Den Modus an jedem Replikationspfeil, synchron oder asynchron, und das Lag-Budget für asynchrone Replikas.
  • Die Rolle jeder Replika: Failover-Kandidat, Lese-Replika oder beides.
  • Wo der Leader-Lock liegt und seine TTL, oder welcher Managed Service diese Rolle übernimmt.
  • Wie Clients den Primary finden: ein Proxy, ein DNS-Name oder eine virtuelle IP.
  • Fehlerdomänen: Availability Zones als Rahmen, einschließlich der Orte, an denen die Konsens-Mitglieder laufen.
  • RPO- und RTO-Ziele, damit Reviewer das Design daran messen können.

Was Managed Services für dich übernehmen

Managed Datenbanken beantworten dieselben Fragen, aber manche Antworten sind festgelegt. Bei einer Amazon RDS Multi-AZ DB Instance ist der Standby synchron, liegt in einer anderen Zone und kann keine Lesezugriffe bedienen. Ein Failover stellt den DNS-Eintrag auf den Standby um und dauert typischerweise 60 bis 120 Sekunden. Ein Multi-AZ DB Cluster hat zwei lesbare Standbys, nutzt semisynchrone Replikation und schließt ein Failover typischerweise in unter 35 Sekunden ab. Im Diagramm ersetzt der Endpoint HAProxy, und AWS ersetzt Patroni und etcd. Lag, Lese-Routing und Ziele beschriftest du trotzdem.

Teste das Failover

Wechsle den Primary absichtlich, zum Beispiel in jedem Wartungsfenster mit patronictl switchover. Die PostgreSQL-Dokumentation empfiehlt regelmäßiges Umschalten, weil es zugleich den Failover-Mechanismus testet. Miss, wie lange Schreibzugriffe scheitern, prüf, ob HAProxy folgt, und stell sicher, dass kein bestätigter Commit fehlt. Für ein Failover über Regionen hinweg siehe das AWS-Multi-Region-Architekturdiagramm.

In Archboard nachbauen

  1. Melde dich mit Google oder GitHub an und leg ein Diagramm an.
  2. Füge drei Rahmen aus dem Basis-Pack für die Availability Zones hinzu und in jeden einen Postgres-Node aus dem Daten-Pack: pg-a (synchroner Standby), pg-b (Primary) und pg-c (asynchrone Replika).
  3. Füge über den Zonen einen Container aus dem C4-Modell-Pack für den Bestellservice und einen Lastverteiler aus dem Netzwerk-Pack für HAProxy hinzu.
  4. Füge darunter einen Container aus dem C4-Modell-Pack für etcd und eine Objektablage aus dem Daten-Pack für das WAL-Archiv hinzu.
  5. Beschrifte die Kanten: „writes :5000“, „reads :5001“, „WAL (sync)“ durchgezogen, „WAL (async)“ gestrichelt, „leader key, TTL 30 s“ von pg-b zu etcd und eine gestrichelte Kante von pg-a zu etcd für das Leader-Rennen.
  6. Füge einen Markdown-Block aus dem Basis-Pack mit RPO, RTO, Lag-Budget und Backup-Aufbewahrung hinzu.

Das Beispiel nutzt 11 Diagramm-Nodes und bleibt damit im Free-Plan. Wie du das Diagramm aktuell hältst, während sich der Cluster ändert, zeigt Architekturdiagramme, die nicht veralten; die allgemeine Methode findest du im Beispiel für ein System-Design-Diagramm.

FAQ

Ist eine Lese-Replika ein Backup? Nein. Löschungen und fehlerhafte Migrationen werden ebenfalls repliziert. Backups mit WAL-Archivierung erlauben die Wiederherstellung auf einen Zeitpunkt vor dem Fehler.

Bedeutet synchrone Replikation null Datenverlust? Für Commits, die ein synchroner Standby bestätigt hat, ja, solange das Failover nur diesen Standby befördert. Commits, die angenommen wurden, während kein synchroner Standby verbunden war, existieren nur auf dem Primary.

Warum drei etcd-Mitglieder und nicht zwei? etcd braucht eine Mehrheit, um Schreibzugriffe anzunehmen. Zwei Mitglieder überstehen den Verlust keines einzigen, drei überstehen den Verlust eines Mitglieds und behalten bei einer Netzwerktrennung auf einer Seite die Mehrheit.

Kann ich statt eines Proxys DNS nutzen, um den Primary zu finden? Ja, Managed Services machen das so. Halte die DNS-TTLs kurz und sorg dafür, dass Clients Auflösungen nicht lange cachen; AWS empfiehlt eine JVM-DNS-Cache-TTL von höchstens 60 Sekunden.

Wie oft sollten wir das Failover testen? So regelmäßig, dass das Team ihm vertraut, zum Beispiel ein geplanter Switchover in jedem Wartungsfenster plus ein ungeplanter Ausfalltest in Staging.

Kostenlos starten — ein Seat, keine Kreditkarte.

Ähnliche Beiträge

← Zurück zum Blog

In einer Minute ein Canvas öffnen

Melden Sie sich mit Google oder GitHub an. Kostenlos für einen Sitzplatz. Upgraden Sie, wenn das Team dazukommt.