PostgreSQL Streaming Replication ermöglicht Echtzeit-Replikation von Daten auf Standby-Server. Dies erhöht Verfügbarkeit und ermöglicht Read-Scaling.
Architektur
Master-Standby Setup
Schreibzugriffe → Primary Server (Master)
│
│ WAL Streaming
▼
Standby Server (Replica)
│
▼
Lesezugriffe (Hot Standby)
Vorteile
| Feature | Beschreibung |
|---|
| Hochverfügbarkeit | Failover bei Master-Ausfall |
| Read Scaling | Lesezugriffe auf Replicas |
| Backup | Point-in-Time Recovery |
| Disaster Recovery | Geografische Verteilung |
Voraussetzungen
Server-Setup
Primary: 192.168.1.10 (master)
Standby: 192.168.1.11 (replica)
PostgreSQL Version: 16
PostgreSQL installieren
# Auf beiden Servern
apt install postgresql-16
Primary Server konfigurieren
postgresql.conf
# /etc/postgresql/16/main/postgresql.conf
# Netzwerk
listen_addresses = '*'
port = 5432
# WAL-Einstellungen
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1GB
# Archivierung (optional für PITR)
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/archive/%f'
# Synchrone Replikation (optional)
# synchronous_commit = on
# synchronous_standby_names = 'standby1'
pg_hba.conf
# /etc/postgresql/16/main/pg_hba.conf
# Replikations-Verbindungen erlauben
host replication replicator 192.168.1.11/32 scram-sha-256
host replication replicator 192.168.1.0/24 scram-sha-256
Replikations-Benutzer erstellen
-- Als postgres-User
sudo -u postgres psql
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'sicheres_passwort';
-- Prüfen
\du replicator
Primary neustarten
systemctl restart postgresql
Standby Server einrichten
PostgreSQL stoppen
systemctl stop postgresql
Daten-Verzeichnis leeren
rm -rf /var/lib/postgresql/16/main/*
Basis-Backup vom Primary
# Als postgres-User
sudo -u postgres pg_basebackup \
-h 192.168.1.10 \
-U replicator \
-D /var/lib/postgresql/16/main \
-Fp -Xs -P -R
# Optionen:
# -Fp: Plain format
# -Xs: WAL während Backup streamen
# -P: Progress anzeigen
# -R: Standby-Konfiguration erstellen
Standby-Signal prüfen
# pg_basebackup mit -R erstellt diese Datei
ls -la /var/lib/postgresql/16/main/standby.signal
# Und postgresql.auto.conf mit Verbindungsinfo
cat /var/lib/postgresql/16/main/postgresql.auto.conf
# primary_conninfo = 'host=192.168.1.10 user=replicator ...'
postgresql.conf (Standby)
# /etc/postgresql/16/main/postgresql.conf
# Hot Standby aktivieren
hot_standby = on
# Feedback an Primary
hot_standby_feedback = on
Standby starten
systemctl start postgresql
Replikation prüfen
Auf Primary
-- Replikations-Status
SELECT * FROM pg_stat_replication;
-- Detailliert
SELECT
client_addr,
state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn,
sync_state
FROM pg_stat_replication;
Auf Standby
-- Replikations-Status
SELECT * FROM pg_stat_wal_receiver;
-- Ist Replica?
SELECT pg_is_in_recovery();
-- Sollte 't' (true) zurückgeben
-- Lag prüfen
SELECT
now() - pg_last_xact_replay_timestamp() AS replication_lag;
Test
-- Auf Primary: Daten einfügen
CREATE TABLE test_replication (id SERIAL, data TEXT);
INSERT INTO test_replication (data) VALUES ('test data');
-- Auf Standby: Daten sollten erscheinen
SELECT * FROM test_replication;
Synchrone Replikation
Primary konfigurieren
# /etc/postgresql/16/main/postgresql.conf
synchronous_commit = on
synchronous_standby_names = 'standby1' # Name des Standbys
Standby konfigurieren
# /etc/postgresql/16/main/postgresql.auto.conf
primary_conninfo = 'host=192.168.1.10 user=replicator password=xxx application_name=standby1'
Prüfen
-- Auf Primary
SELECT application_name, sync_state FROM pg_stat_replication;
-- sync_state sollte 'sync' sein
Manueller Failover
# Auf Standby
sudo -u postgres pg_ctl promote -D /var/lib/postgresql/16/main
# Oder
sudo -u postgres psql -c "SELECT pg_promote();"
Status prüfen
-- Sollte 'f' (false) sein nach Promotion
SELECT pg_is_in_recovery();
Alten Primary als Standby einrichten
# 1. PostgreSQL stoppen
systemctl stop postgresql
# 2. Daten-Verzeichnis löschen
rm -rf /var/lib/postgresql/16/main/*
# 3. Basis-Backup vom neuen Primary
sudo -u postgres pg_basebackup \
-h 192.168.1.11 \
-U replicator \
-D /var/lib/postgresql/16/main \
-Fp -Xs -P -R
# 4. Starten
systemctl start postgresql
Automatischer Failover mit Patroni
Installation
apt install patroni python3-etcd
Konfiguration
# /etc/patroni/patroni.yml
scope: postgres-cluster
name: node1
restapi:
listen: 0.0.0.0:8008
connect_address: 192.168.1.10:8008
etcd:
hosts: 192.168.1.20:2379,192.168.1.21:2379,192.168.1.22:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
parameters:
max_connections: 100
shared_buffers: 256MB
initdb:
- encoding: UTF8
- data-checksums
postgresql:
listen: 0.0.0.0:5432
connect_address: 192.168.1.10:5432
data_dir: /var/lib/postgresql/16/main
authentication:
replication:
username: replicator
password: sicheres_passwort
superuser:
username: postgres
password: postgres_passwort
Service
systemctl enable patroni
systemctl start patroni
# Status
patronictl -c /etc/patroni/patroni.yml list
pg_rewind für schnellen Resync
Nach Failover
# Alter Primary als Standby
sudo -u postgres pg_rewind \
--target-pgdata=/var/lib/postgresql/16/main \
--source-server='host=192.168.1.11 user=postgres dbname=postgres'
Voraussetzungen
# Auf allen Servern
wal_log_hints = on
# Oder
# initdb mit --data-checksums
Monitoring
Replikations-Lag
-- Auf Primary
SELECT
application_name,
client_addr,
state,
pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn) AS sent_lag,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_lag
FROM pg_stat_replication;
Monitoring-Skript
#!/bin/bash
# check_replication.sh
LAG=$(sudo -u postgres psql -t -c "
SELECT COALESCE(
EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp())),
0
)::INT
")
if [ "$LAG" -gt 300 ]; then
echo "CRITICAL: Replication lag is ${LAG} seconds"
exit 2
elif [ "$LAG" -gt 60 ]; then
echo "WARNING: Replication lag is ${LAG} seconds"
exit 1
else
echo "OK: Replication lag is ${LAG} seconds"
exit 0
fi
Prometheus
-- pg_stat_replication für Prometheus exportieren
SELECT
'pg_replication_lag_seconds{application_name="' || application_name || '"} ' ||
COALESCE(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn), 0)
FROM pg_stat_replication;
Read Replicas nutzen
Connection Pooling mit PgBouncer
# /etc/pgbouncer/pgbouncer.ini
[databases]
myapp_primary = host=192.168.1.10 dbname=myapp
myapp_replica = host=192.168.1.11 dbname=myapp
[pgbouncer]
listen_addr = *
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20
Anwendungs-Konfiguration
# Django settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'HOST': '192.168.1.10', # Primary für Schreiben
'NAME': 'myapp',
},
'replica': {
'ENGINE': 'django.db.backends.postgresql',
'HOST': '192.168.1.11', # Replica für Lesen
'NAME': 'myapp',
}
}
Zusammenfassung
| Befehl | Funktion |
|---|
| pg_basebackup | Basis-Backup erstellen |
| pg_ctl promote | Standby zum Primary |
| pg_rewind | Schneller Resync |
| Parameter | Beschreibung |
|---|
| wal_level | replica für Streaming |
| max_wal_senders | Max. Replikations-Verbindungen |
| hot_standby | Lesezugriff auf Standby |
| synchronous_commit | Synchrone Replikation |
| View | Information |
|---|
| pg_stat_replication | Status auf Primary |
| pg_stat_wal_receiver | Status auf Standby |
| pg_is_in_recovery() | Replica-Check |
Fazit
PostgreSQL Streaming Replication ist robust und performant. Hot Standby ermöglicht Lesezugriffe auf Replicas. Für automatischen Failover empfiehlt sich Patroni mit etcd. Synchrone Replikation garantiert Datenkonsistenz bei Failover. Read Replicas mit PgBouncer verbessern die Skalierbarkeit. Die native Replikation ist für die meisten Hochverfügbarkeits-Anforderungen ausreichend.