La replicación streaming de PostgreSQL proporciona un mecanismo robusto para mantener copias en tiempo real de tu base de datos en múltiples servidores. Ya sea que necesites alta disponibilidad, recuperación ante desastres o escalado de lecturas, la replicación streaming ofrece consistencia a nivel de bytes con un lag mínimo. Esta guía te lleva paso a paso a través de la configuración de una arquitectura primario-standby desde cero, la configuración del archivado WAL, el monitoreo de la salud de la replicación y la planificación de estrategias de failover.
Requisitos Previos
- Dos servidores Linux (Ubuntu 22.04+ o RHEL 9+) con conectividad de red
- PostgreSQL 16 o posterior instalado en ambos servidores
- Suficiente espacio en disco para archivos WAL y backups base
- Acceso root o sudo en ambas máquinas
- Puerto 5432 abierto entre los servidores primario y standby
- Familiaridad básica con los archivos de configuración de PostgreSQL
Entendiendo la Replicación en PostgreSQL
PostgreSQL soporta dos modos principales de replicación: replicación streaming y replicación lógica. La replicación streaming opera a nivel de bytes WAL (Write-Ahead Log), enviando un flujo continuo de registros WAL del primario a los standbys. Esto crea una copia binaria exacta del clúster completo.
El proceso de replicación sigue este flujo:
- El primario escribe los cambios en segmentos WAL
- El proceso WAL sender transmite los registros a los standbys conectados
- El WAL receiver del standby escribe los registros en el WAL local
- El proceso startup del standby reproduce los registros WAL para actualizar los archivos de datos
La replicación streaming puede operar en modo asíncrono (predeterminado) o síncrono. El modo asíncrono ofrece mejor rendimiento con lag mínimo, mientras que el síncrono garantiza cero pérdida de datos a costa de latencia en escritura.
Configuración del Servidor Primario
Edita el archivo postgresql.conf del servidor primario para habilitar la replicación:
# /etc/postgresql/16/main/postgresql.conf
listen_addresses = '*'
wal_level = replica
max_wal_senders = 5
wal_keep_size = '1GB'
hot_standby = on
El parámetro wal_level = replica asegura que el WAL contenga suficiente información para la replicación. max_wal_senders controla cuántas conexiones de streaming concurrentes se permiten. wal_keep_size previene que el primario recicle segmentos WAL antes de que el standby los haya consumido.
A continuación, configura pg_hba.conf para permitir que el standby se conecte para replicación:
# /etc/postgresql/16/main/pg_hba.conf
# TYPE DATABASE USER ADDRESS METHOD
host replication replicator 10.0.1.20/32 scram-sha-256
Reemplaza 10.0.1.20 con la dirección IP de tu servidor standby.
Crea un usuario dedicado para replicación:
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'your_secure_password';
Reinicia PostgreSQL para aplicar los cambios de configuración:
sudo systemctl restart postgresql
Configuración del Servidor Standby
En el servidor standby, detén PostgreSQL y limpia el directorio de datos existente:
sudo systemctl stop postgresql
sudo rm -rf /var/lib/postgresql/16/main/*
Ejecuta pg_basebackup para clonar los datos del primario:
sudo -u postgres pg_basebackup \
-h 10.0.1.10 \
-U replicator \
-D /var/lib/postgresql/16/main \
-Fp -Xs -P -R
Las banderas tienen propósitos específicos:
-Fp: Formato de salida plano-Xs: Transmite WAL durante el backup para evitar brechas-P: Muestra el progreso-R: Crea automáticamentestandby.signaly escribeprimary_conninfoenpostgresql.auto.conf
Si prefieres configuración manual, crea la señal de standby y los parámetros de conexión tú mismo:
touch /var/lib/postgresql/16/main/standby.signal
Agrega a postgresql.conf en el standby:
primary_conninfo = 'host=10.0.1.10 port=5432 user=replicator password=your_secure_password application_name=standby1'
hot_standby = on
Inicia el servidor standby:
sudo systemctl start postgresql
Archivado WAL
El archivado WAL proporciona una red de seguridad adicional al copiar segmentos WAL completados a una ubicación de archivo. Esto permite la recuperación a un punto en el tiempo (PITR) y protege contra escenarios donde el standby se queda muy atrás.
Configura el archivado en el primario:
# /etc/postgresql/16/main/postgresql.conf
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/wal_archive/%f'
Crea el directorio de archivo:
sudo mkdir -p /var/lib/postgresql/wal_archive
sudo chown postgres:postgres /var/lib/postgresql/wal_archive
Para entornos de producción, usa rsync o una solución de almacenamiento en la nube en lugar de cp local:
archive_command = 'rsync -a %p backup-server:/wal_archive/%f'
Configura el standby para usar WAL archivado como respaldo con restore_command:
# En el postgresql.conf del standby
restore_command = 'cp /mnt/wal_archive/%f %p'
Esto permite que el standby recupere segmentos WAL del archivo cuando el streaming se queda atrás.
Monitoreo del Lag de Replicación
Monitorear el lag de replicación es crítico para asegurar que tu standby se mantenga actualizado. En el servidor primario, consulta pg_stat_replication:
SELECT
client_addr,
application_name,
state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn,
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes,
write_lag,
flush_lag,
replay_lag
FROM pg_stat_replication;
En el servidor standby, verifica el estado del receptor y calcula el lag:
SELECT
status,
received_lsn,
latest_end_lsn,
last_msg_send_time,
last_msg_receipt_time
FROM pg_stat_wal_receiver;
-- Estimación del lag basada en tiempo
SELECT
now() - pg_last_xact_replay_timestamp() AS replication_delay;
Configura una alerta cuando el lag exceda tu umbral:
#!/bin/bash
LAG_BYTES=$(psql -h primary -U monitor -t -c \
"SELECT pg_wal_lsn_diff(sent_lsn, replay_lsn) FROM pg_stat_replication WHERE application_name='standby1';")
if [ "$LAG_BYTES" -gt 104857600 ]; then
echo "ALERTA: El lag de replicación excede 100MB ($LAG_BYTES bytes)" | mail -s "PG Replication Alert" admin@example.com
fi
Estrategias de Failover
Cuando el primario falla, necesitas un plan claro de failover. PostgreSQL proporciona varios métodos de promoción:
Promoción manual:
# Usando pg_ctl
sudo -u postgres pg_ctl promote -D /var/lib/postgresql/16/main
# Usando SQL (PostgreSQL 12+)
SELECT pg_promote(wait := true, wait_seconds := 60);
Promoción por archivo trigger:
Configura promote_trigger_file en el postgresql.conf del standby:
promote_trigger_file = '/tmp/postgresql.trigger'
Crea el archivo para activar la promoción:
touch /tmp/postgresql.trigger
Failover automatizado con Patroni:
Para entornos de producción, usa una herramienta como Patroni con etcd:
# /etc/patroni/patroni.yml (extracto)
scope: pg-cluster
namespace: /db/
name: node1
restapi:
listen: 0.0.0.0:8008
etcd:
hosts: 10.0.1.100:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
maximum_lag_on_failover: 1048576
postgresql:
listen: 0.0.0.0:5432
data_dir: /var/lib/postgresql/16/main
authentication:
replication:
username: replicator
password: your_secure_password
Después del failover, el antiguo primario debe reintroducirse como standby usando pg_rewind:
sudo -u postgres pg_rewind \
--target-pgdata=/var/lib/postgresql/16/main \
--source-server="host=10.0.1.20 port=5432 user=postgres"
Comparativa: Streaming vs Replicación Lógica
| Característica | Replicación Streaming | Replicación Lógica |
|---|---|---|
| Nivel de replicación | Nivel de bytes (WAL) | Nivel de fila (cambios decodificados) |
| Alcance del clúster | Clúster completo | Por tabla o por base de datos |
| Soporte entre versiones | Requiere misma versión mayor | Soporta versiones mayores diferentes |
| Escritura en standby | Solo lectura (hot standby) | Lectura-escritura en el suscriptor |
| Replicación de DDL | Automática | Manual (no se replica) |
| Sobrecarga de rendimiento | Muy baja | Moderada (costo de decodificación) |
| Complejidad de configuración | Simple | Moderada |
| Caso de uso | HA y recuperación ante desastres | Replicación selectiva y migraciones |
Elige replicación streaming para alta disponibilidad del clúster completo. Elige replicación lógica cuando necesites replicación selectiva de tablas, actualizaciones entre versiones o suscriptores con escritura.
Escenario del Mundo Real
Administras una base de datos de producción de comercio electrónico que maneja 5,000 transacciones por segundo. El negocio requiere menos de 30 segundos de inactividad durante cualquier fallo. Así es como diseñas la solución:
El servidor primario (10.0.1.10) maneja todas las escrituras. El standby síncrono (10.0.1.20) en el mismo centro de datos asegura cero pérdida de datos. El standby asíncrono (10.0.2.10) en un centro de datos remoto proporciona recuperación ante desastres.
Configura la replicación síncrona en el primario:
synchronous_standby_names = 'FIRST 1 (standby_dc1, standby_dc2)'
synchronous_commit = on
Dirige las consultas de lectura a los standbys usando PgBouncer o HAProxy:
# /etc/haproxy/haproxy.cfg (extracto)
listen pg-read
bind *:5433
mode tcp
balance roundrobin
option pgsql-check user haproxy
server standby1 10.0.1.20:5432 check
server standby2 10.0.2.10:5432 check
Esta arquitectura proporciona cero pérdida de datos para fallos locales y pérdida mínima para eventos a nivel de centro de datos, mientras descarga el tráfico de lectura a los standbys.
Errores Comunes y Casos Especiales
- Eliminación de segmentos WAL: Si el standby se desconecta más tiempo del que permite
wal_keep_size, no puede ponerse al día vía streaming. Siempre configura archivado WAL como red de seguridad o usa slots de replicación:SELECT pg_create_physical_replication_slot('standby1_slot'); - Slots de replicación y uso de disco: Los slots de replicación no utilizados previenen la limpieza de WAL, potencialmente llenando el disco. Monitorea
pg_replication_slotsy elimina los slots inactivos - Transacciones grandes: Una sola transacción masiva (importación masiva) genera enormes volúmenes de WAL que pueden sobrecargar el standby o la red. Divide las operaciones grandes en lotes
- Latencia de commit síncrono: Habilitar
synchronous_commit = oncon standbys síncronos agrega el tiempo de ida y vuelta de la red a cada commit. Prueba el throughput de escritura antes de habilitarlo - Divergencia de timeline: Después de promover un standby, el antiguo primario está en un timeline diferente. Debes usar
pg_rewindo reconstruirlo completamente antes de reintroducirlo como standby - Contraseña en primary_conninfo: Almacena las credenciales en
.pgpassen lugar de texto plano en los archivos de configuración
Solución de Problemas
El standby no se conecta:
# Verificar logs del standby
sudo tail -f /var/log/postgresql/postgresql-16-main.log
# Verificar conexiones de replicación en el primario
sudo -u postgres psql -c "SELECT * FROM pg_stat_replication;"
# Probar conectividad desde el standby
psql -h 10.0.1.10 -U replicator -d postgres -c "IDENTIFY_SYSTEM;"
El lag de replicación crece:
-- Verificar si el standby está reproduciendo
SELECT pg_is_in_recovery(), pg_last_wal_replay_lsn();
-- Verificar consultas de larga duración bloqueando la reproducción
SELECT pid, query, state, wait_event FROM pg_stat_activity
WHERE state != 'idle' AND backend_type = 'client backend';
El archivo WAL se llena:
# Verificar estado del archivado
sudo -u postgres psql -c "SELECT * FROM pg_stat_archiver;"
# Limpiar archivos antiguos manualmente (mantener al menos 1 día)
find /var/lib/postgresql/wal_archive -mtime +1 -delete
Resumen
- La replicación streaming de PostgreSQL crea copias binarias en tiempo real de todo tu clúster de base de datos
- Configura
wal_level = replica,max_wal_sendersy un usuario de replicación en el primario - Usa
pg_basebackupcon la bandera-Rpara inicializar standbys con configuración automática - El archivado WAL proporciona una red de seguridad para standbys que se quedan atrás del streaming
- Monitorea el lag de replicación vía
pg_stat_replicationypg_stat_wal_receiver - Usa Patroni o herramientas similares para failover automatizado en producción
- Los slots de replicación previenen la eliminación de WAL pero requieren monitoreo para evitar el agotamiento del disco
- Siempre prueba tu procedimiento de failover antes de necesitarlo en una emergencia