Einrichtung synchroner Replikation für Hochverfügbarkeit in PostgreSQL
Erfahren Sie, wie Sie PostgreSQL-Hochverfügbarkeit mit null Datenverlust (RPO=0) mithilfe synchroner Streaming-Replikation konfigurieren. Dieses Schritt-für-Schritt-Tutorial behandelt wesentliche Konfigurationen für `wal_level`, Replikationsslots, `pg_basebackup` und die korrekte Einstellung von `synchronous_commit`-Parametern auf dem primären und dem Standby-Server, um die Transaktionsdauerhaftigkeit in kritischen Umgebungen zu gewährleisten.
Einrichtung synchroner Replikation für Hochverfügbarkeit in PostgreSQL
Die Konfiguration von PostgreSQL für Hochverfügbarkeit (HA) beginnt normalerweise mit einer schwierigen Frage: Wie viele Daten können Sie sich leisten zu verlieren, wenn der primäre Server direkt nach einem Commit ausfällt? Bei normaler asynchroner Streaming-Replikation lautet die Antwort „vielleicht einige.“ Der Primäre kann der Anwendung mitteilen, dass eine Transaktion committet wurde, bevor der Standby den WAL-Eintrag empfangen oder wiedergegeben hat. Wenn der Primäre während dieses kleinen Zeitfensters ausfällt, enthält der hochgestufte Standby möglicherweise nicht die letzten wenigen committeten Transaktionen.
Synchrone Streaming-Replikation ändert diesen Kompromiss. PostgreSQL wartet auf einen oder mehrere benannte Standbys, bevor es den Erfolg des Commits meldet. Abhängig von der synchronous_commit-Stufe muss der Standby möglicherweise nur das WAL an das Betriebssystem schreiben, es auf dauerhaften Speicher leeren oder es wiedergeben, damit Abfragen auf dem Standby es sehen können. Das kann Ihnen einen RPO von null für committete Transaktionen geben, aber es bedeutet auch, dass der Schreibpfad jetzt vom Netzwerk und der Gesundheit des Standbys abhängt.
Dieser Kompromiss ist wichtig. Synchrone Replikation eignet sich gut für die kleine Menge von Daten, bei denen der Verlust selbst einer bestätigten Transaktion inakzeptabel ist: Zahlungen, Kontostände, Bestandsreservierungen, Bestellstatus, Prüfpfade. Sie ist oft schlecht geeignet für umfangreiche Ereignisprotokolle, Clickstream-Daten, Metriken oder Arbeitslasten, bei denen Verfügbarkeit und Latenz wichtiger sind als perfekte Dauerhaftigkeit über Knoten hinweg. Bevor Sie sie global aktivieren, entscheiden Sie, welcher Teil Ihrer Arbeitslast sie tatsächlich benötigt.
Voraussetzungen
Stellen Sie vor dem Start sicher, dass Sie zwei PostgreSQL-Server (Primär und Standby) eingerichtet haben, die identische Hauptversionen von PostgreSQL ausführen. Beide Server müssen Netzwerkkonnektivität haben. Für diese Anleitung nehmen wir an:
- Primärer Hostname/IP:
pg_primary - Standby-Hostname/IP:
pg_standby - Replikationsbenutzer:
repl_user - Datenbankname:
mydb
Sie benötigen außerdem ein funktionierendes Backup und ein Wartungsfenster für die anfängliche Basis-Sicherung. Die Beispiele gehen von PostgreSQL 12 oder neuer aus, wo der Standby-Modus mit standby.signal gesteuert wird und Verbindungseinstellungen normalerweise von pg_basebackup -R geschrieben werden.
Schritt 1: Konfigurieren des primären Servers
Der primäre Server erfordert spezifische Einstellungen, um Streaming-Replikation zu ermöglichen und das Write-Ahead Log (WAL) zu verwalten, das für synchrone Commits erforderlich ist.
A. Anpassen von postgresql.conf auf dem Primären
Bearbeiten Sie die postgresql.conf-Datei des primären Servers. Die folgenden Parameter sind für Streaming-Replikation obligatorisch:
# --- Erforderlich für Replikation ---
listen_addresses = '*' # Ermöglicht Verbindungen vom Standby
wal_level = replica # Muss 'replica' oder höher sein (z. B. 'logical')
max_wal_senders = 10 # Maximale gleichzeitige Verbindungen von Standbys
max_replication_slots = 10 # Slots, die für persistente Replikationsstreams benötigt werden
# --- Wesentlich für synchrones Commit ---
synchronous_standby_names = 'FIRST 1 (standby1)' # Gibt erforderliche Standbys nach application_name an
# --- Optional, aber empfohlen ---
wal_log_hints = on # Empfohlen für sicherere Replikation, erhöht jedoch das WAL-Volumen
shared_preload_libraries = 'pg_stat_statements' # Wenn Überwachung verwendet wird
Erklärung der wichtigsten Parameter:
wal_level = replica: Dies stellt sicher, dass ausreichend Informationen in das WAL geschrieben werden, um einem Standby-Server die Rekonstruktion des Datenbankzustands zu ermöglichen. Für synchrone Commits ist diese Stufe die Mindestanforderung.synchronous_standby_names: Dies ist die Kerneinstellung zur Definition, welche Standbys Schreibvorgänge bestätigen müssen. Die Namen hier sind Replikationsverbindungs-application_name-Werte, keine Replikationsslot-Namen.FIRST 1 (standby1)bedeutet, dass PostgreSQL auf den ersten verfügbaren synchronen Standby aus dieser Liste wartet.ANY 1 (standby1, standby2)bedeutet, dass jeder der aufgeführten Standbys das Commit erfüllen kann.
B. Konfigurieren der Host-basierten Authentifizierung (pg_hba.conf)
Der primäre Server muss dem Replikationsbenutzer vom/von den Standby-Server(n) erlauben, sich zu Replikationszwecken zu verbinden.
Fügen Sie einen Eintrag in pg_hba.conf auf dem Primären hinzu:
# TYPE DATABASE USER ADDRESS METHOD
host replication repl_user pg_standby/32 scram-sha-256
Ersetzen Sie pg_standby/32 durch die tatsächliche IP-Adresse oder das Subnetz Ihres Standby-Servers.
C. Erstellen des Replikationsslots und Benutzers
Stellen Sie eine Verbindung zu PostgreSQL auf dem primären Server her, um den erforderlichen Benutzer und den Replikationsslot zu erstellen.
1. Replikationsbenutzer erstellen:
CREATE ROLE repl_user WITH REPLICATION LOGIN PASSWORD 'ein_starkes_passwort';
2. Replikationsslot erstellen:
Dieser Slot stellt sicher, dass WAL-Segmente so lange aufbewahrt werden, bis der Standby den Empfang bestätigt, und verhindert, dass der Standby so weit zurückfällt, dass er eine neue Basis-Sicherung benötigt. Slots sind nützlich, können aber auch Festplatten füllen, wenn ein Standby längere Zeit ausfällt. Überwachen Sie daher das zurückgehaltene WAL.
SELECT pg_create_physical_replication_slot('standby1_slot');
Der Slot-Name muss nicht mit synchronous_standby_names übereinstimmen. In diesem Beispiel ist standby1 der application_name, der für die synchrone Standby-Auswahl verwendet wird, während standby1_slot der physische Replikationsslot ist, der für die WAL-Aufbewahrung verwendet wird.
D. Neustarten des Primären
Wenden Sie alle Konfigurationsänderungen an, indem Sie den PostgreSQL-Dienst auf dem primären Server neu starten.
sudo systemctl restart postgresql
Schritt 2: Konfigurieren des Standby-Servers
Der Standby-Server wird so konfiguriert, dass er WAL-Einträge vom Primären mithilfe einer Wiederherstellungskonfiguration streamt.
A. Basis-Sicherung
Bevor Sie mit dem Streaming beginnen, benötigt der Standby eine vollständige Kopie des Datenverzeichnisses des Primären. Stoppen Sie zuerst PostgreSQL auf dem Standby.
sudo systemctl stop postgresql
Erstellen Sie die Basis-Sicherung mit pg_basebackup. Ersetzen Sie Pfade und Verbindungsdetails nach Bedarf:
# Beispiel mit dem pg_basebackup-Dienstprogramm
pg_basebackup -h pg_primary -D /var/lib/postgresql/15/main/ -U repl_user -P -Xs -R -W
-D: Das Ziel-Datenverzeichnis auf dem Standby.-U: Der Replikationsbenutzer.-P: Fortschritt anzeigen.-Xs: Notwendige WAL-Dateien während der Basis-Sicherung einschließen.-R: Automatisch die Dateistandby.signalerstellen und die erforderlichen Verbindungseinstellungen inpostgresql.auto.conf(oder Wiederherstellungskonfiguration) generieren.
B. Konfigurieren von postgresql.conf auf dem Standby
Stellen Sie auf dem Standby sicher, dass PostgreSQL weiß, wie es sich zurück zum Primären verbindet. Das entscheidende Detail für synchrone Replikation ist application_name; es muss mit dem Namen übereinstimmen, der in synchronous_standby_names aufgeführt ist.
# --- Erforderlich auf dem Standby ---
primary_conninfo = 'host=pg_primary port=5432 user=repl_user password=ein_starkes_passwort application_name=standby1'
primary_slot_name = 'standby1_slot'
hot_standby = on # Ermöglicht Leseabfragen während des Wiederherstellungs-/Standby-Modus
C. Starten des Standbys
Starten Sie den PostgreSQL-Dienst auf dem Standby-Server.
sudo systemctl start postgresql
Schritt 3: Überprüfung und Test des synchronen Commits
Sobald beide Server laufen, überprüfen Sie die Verbindung und testen Sie dann das synchrone Verhalten.
A. Überprüfen des Replikationsstatus
Stellen Sie eine Verbindung zur primären Datenbank her und überprüfen Sie die Ansicht pg_stat_replication:
SELECT client_addr, application_name, state, sync_state FROM pg_stat_replication;
Sie sollten einen Eintrag für standby1 mit sync_state als sync sehen. Wenn potential angezeigt wird, ist der Standby verbunden, erfüllt aber derzeit nicht die synchronen Commits. Wenn async angezeigt wird, behandelt PostgreSQL ihn nicht als synchronen Standby; überprüfen Sie die Schreibweise von application_name und synchronous_standby_names.
B. Testen des synchronen Commits
Der globale Parameter, der bestimmt, wie stark PostgreSQL wartet, ist synchronous_commit. Für RPO=0 müssen Sie einen Wert verwenden, der die Synchronisierung erzwingt.
1. Globales Verhalten einstellen
Wenn Sie synchronous_standby_names auf dem Primären wie in Schritt 1 gezeigt konfiguriert haben, wartet der Standardwert synchronous_commit = on, bis der synchrone Standby den WAL-Eintrag auf dauerhaften Speicher geleert hat. remote_write wartet, bis der Standby den WAL-Eintrag an das Betriebssystem geschrieben hat, was normalerweise schneller ist, aber nicht so robust, wenn der Standby-Host vor dem Leeren abstürzt. remote_apply wartet, bis der Standby die Transaktion wiedergegeben hat, was nützlich ist, wenn Ihre Anwendung unmittelbar nach dem Schreiben auf den Primären vom Standby liest.
Für die meisten Hochverfügbarkeitseinrichtungen mit null Datenverlust ist on der praktische Ausgangspunkt. Verwenden Sie remote_apply nur, wenn das Read-After-Write-Verhalten auf dem Standby wichtig genug ist, um die zusätzliche Latenz zu rechtfertigen.
# In postgresql.conf auf dem Primären
synchronous_commit = on
Warnung: Synchrone Commits können die Schreiblatenz im Vergleich zu asynchronen Modi (
offoderlocal) deutlich erhöhen. Die zusätzliche Latenz entsteht durch Netzwerk-Roundtrips, die WAL-Schreibgeschwindigkeit des Standbys und, beiremote_apply, die Wiedergabegeschwindigkeit.
2. Testen innerhalb einer Transaktion
Um transaktional zu testen (ohne eine globale Konfigurationsänderung zu erfordern), können Sie es pro Sitzung oder Transaktion einstellen:
-- Verbindung zum Primären herstellen
BEGIN;
SET LOCAL synchronous_commit = on;
INSERT INTO sales (item, amount) VALUES ('Widget A', 100);
-- Dieses INSERT bereitet WAL vor, das vom synchronen Standby bestätigt werden muss.
COMMIT;
-- Das COMMIT ist nur erfolgreich, nachdem der Standby den WAL-Schreibvorgang bestätigt hat.
Wenn zum Zeitpunkt des Commits kein konfigurierter synchroner Standby verfügbar ist, wartet das Commit. Das ist der Sinn der Funktion, kann aber Teams während eines Ausfalls überraschen: Der Primäre läuft möglicherweise noch, aber Schreibvorgänge scheinen eingefroren zu sein, weil PostgreSQL auf eine synchrone Bestätigung wartet. Es gibt keine allgemeine PostgreSQL-Einstellung, die automatisch auf asynchrones Commit „zurückfällt“, wenn ein synchroner Standby verschwindet. Wenn Sie dieses Verhalten wünschen, muss Ihr HA-Tooling synchronous_standby_names ändern, oder Sie müssen ein Runbook haben, um es manuell zu tun, nachdem Sie entschieden haben, dass Verfügbarkeit wichtiger ist als null Datenverlust.
Ein sichereres Zwei-Standby-Muster
Ein einzelner synchroner Standby bietet starke Dauerhaftigkeit, solange alles gesund ist, schafft aber auch einen einzelnen Ausfallpunkt für die Schreibverfügbarkeit. Wenn dieser Standby ausfällt, langsam oder vom Primären isoliert ist, warten Commits. In der Produktion ist ein gängiges Muster, mindestens zwei Standbys zu betreiben und eine synchrone Bestätigung zu verlangen:
synchronous_standby_names = 'ANY 1 (standby1, standby2)'
Mit diesem Setup kann jeder der Standbys das Commit erfüllen. Wenn standby1 neu startet, kann standby2 immer noch Schreibvorgänge bestätigen. Sie müssen dennoch beide Replikate überwachen, da ein langer Ausfall eines Standbys dazu führen kann, dass sein Replikationsslot eine große Menge WAL zurückhält, aber der Primäre wird aufgrund eines einzelnen Standby-Ausfalls weniger wahrscheinlich ins Stocken geraten.
Zwei Bestätigungen zu verlangen, ist möglich:
synchronous_standby_names = 'ANY 2 (standby1, standby2, standby3)'
Das ist eine strengere Dauerhaftigkeitsentscheidung. Sie ist normalerweise Umgebungen mit sehr niedrigen Latenzverbindungen und einem klaren Grund vorbehalten, mehr als eine entfernte Kopie vor dem Commit zu verlangen. Für viele Anwendungsdatenbanken ist „irgendeiner von zwei nahen Standbys“ der bessere Kompromiss.
Was nach der Aktivierung überwacht werden sollte
Hören Sie nicht bei „der Standby verbindet sich“ auf. Synchrone Replikation kann technisch funktionieren, während die benutzerseitige Latenz schlechter wird. Beobachten Sie nach der Einführung diese Signale:
SELECT
application_name,
client_addr,
state,
sync_state,
write_lag,
flush_lag,
replay_lag
FROM pg_stat_replication;
Auf dem Primären sagt Ihnen sync_state = 'sync', welcher Standby derzeit synchron ist. write_lag, flush_lag und replay_lag helfen zu erklären, wo die Zeit bleibt. Wenn write_lag hoch ist, vermuten Sie Netzwerk- oder WAL-Schreibdruck auf dem Standby. Wenn flush_lag hoch ist, vermuten Sie Speicher. Wenn nur replay_lag hoch ist, empfängt der Standby möglicherweise WAL, wendet es aber aufgrund von E/A, CPU, Sperren oder lang laufenden Abfragen auf dem Standby langsam an.
Überwachen Sie auch die Slot-Aufbewahrung:
SELECT
slot_name,
active,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
FROM pg_replication_slots;
Ein Replikationsslot schützt einen Standby, aber er schützt nicht Ihre Festplatte. Wenn ein Slot inaktiv ist und das zurückgehaltene WAL weiter wächst, reparieren Sie entweder den Standby schnell oder löschen Sie den Slot, nachdem Sie bestätigt haben, dass Sie ihn nicht mehr benötigen.
Ein praktischer Einführungsplan
Behandeln Sie für ein vielbeschäftigtes Produktionssystem die synchrone Replikation als eine schrittweise Änderung und nicht als eine einzeilige Konfigurationsanpassung.
Erstellen Sie zuerst den Standby asynchron und lassen Sie ihn eine Weile laufen. Bestätigen Sie, dass er während Spitzenlast-Schreibphasen mithalten kann. Wenn er asynchron zurückfällt, wird er die Commit-Latenz beeinträchtigen, wenn er synchron wird.
Zweitens setzen Sie application_name und überprüfen Sie, ob der Primäre den Standby genau so sieht, wie Sie es in pg_stat_replication erwarten. Tippfehler sind hier häufig, da synchronous_standby_names mit dem Laufzeit-application_name übereinstimmt, nicht mit dem Hostnamen und nicht mit dem Slot.
Drittens aktivieren Sie die synchrone Replikation während eines verkehrsarmen Fensters und beobachten Sie die Commit-Latenz von der Anwendungsseite. PostgreSQL-Metriken können in Ordnung aussehen, während sich der Anwendungsverbindungspool staut, weil Transaktionen jetzt Verbindungen etwas länger halten.
Schreiben Sie schließlich die Ausfallentscheidung auf. Wenn der synchrone Standby weg ist und der Primäre auf Commits wartet, wer darf synchronous_standby_names lockern? Unter welchen Bedingungen? Wie werden Sie überprüfen, ob der alte Primäre oder der alte Standby die neuesten Daten enthält, bevor Sie Knoten wieder zusammenführen? Dies sind betriebliche Entscheidungen, nicht nur Datenbankeinstellungen.
Best Practices für synchrone HA
- Verwenden Sie dedizierte Standbys: Weisen Sie nur Standbys, die physisch nahe (geringe Latenz) am Primären sind, Ihrer synchronen Replikationsliste zu. Hohe Latenz wird sich direkt in der Commit-Zeit zeigen.
- Überwachen Sie die Replikationsverzögerung: Überwachen Sie auch im synchronen Modus die Standby-Verzögerung. Ein langsamer Standby, der technisch noch 'sync' ist, aber zu lange braucht, um WAL zu verarbeiten, kann dennoch die Benutzererfahrung beeinträchtigen.
- Planen Sie den Verfügbarkeitskompromiss: Entscheiden Sie im Voraus, ob ein Operator während eines Vorfalls vorübergehend einen fehlenden Standby aus
synchronous_standby_namesentfernen darf. - Verwenden Sie mehrere Standbys: Konfigurieren Sie für eine bessere Schreibverfügbarkeit
synchronous_standby_names = 'ANY 1 (standby1, standby2)', damit jeder Standby Commits bestätigen kann.