StackPractices
intermediate Por Mathias Paulenko

Replicación de bases de datos — primaria y multi-primaria

Guía práctica de estrategias de replicación de bases de datos: primaria-réplica, multi-primaria, síncrona vs asíncrona, y cómo manejar failover y resolución de conflictos.

Visión General

La replicación mantiene copias de tus datos en varios nodos: una primaria acepta escrituras, las réplicas aplican el stream y sirven lecturas. Es como sobrevives a una primaria muerta a las 3 de la mañana y cómo escalas lecturas más allá de lo que una sola máquina aguanta. El coste es complejidad — lag, conflictos, split-brain y trade-offs de consistencia que tendrás que diseñar.

Esta guía cubre las estrategias que corren en producción: topologías primaria-réplica (aún llamadas master-slave), configuraciones multi-primaria, modos síncronos vs asíncronos, mecánica de failover y resolución de conflictos. Para los snippets operativos, consulta la receta de replicación de bases de datos. Y si buscas el detalle específico de PostgreSQL, existe la guía completa de replicación en PostgreSQL.

Cuándo Usar

  • El tráfico de lectura supera un solo nodo y escalar verticalmente dejó de ser rentable
  • Necesitas failover automático, no una restauración de backups de 2 horas
  • Copias fuera del sitio para recuperación ante desastres
  • Analítica pesada que no debería competir con las queries transaccionales

Replicación Primaria-Réplica

Una primaria gestiona escrituras; las réplicas gestionan lecturas. Sigue siendo la topología más común — los términos “master-slave” y “primaria-réplica” se refieren a lo mismo, y verás ambos en documentación y herramientas antiguas.

flowchart diagram: App -->|escrituras| P[Primaria R+W

Replicación Asíncrona

La primaria confirma localmente y luego envía los cambios a las réplicas. Latencia mínima, pero puedes perder los últimos segundos de escrituras si la primaria muere antes de que las réplicas se pongan al día.

-- MySQL 8.0.22+ (versiones antiguas: CHANGE MASTER TO / START SLAVE)
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='primary_host',
  SOURCE_USER='replica',
  SOURCE_PASSWORD='password';
START REPLICA;

-- PostgreSQL (streaming replication)
-- Primaria: wal_level = replica, max_wal_senders = 3
-- Standby: primary_conninfo = 'host=primary_host port=5432'

Replicación Semi-Síncrona

La primaria espera a que al menos una réplica confirme la recepción antes de hacer commit. Punto medio entre seguridad y velocidad.

-- MySQL
SET GLOBAL rpl_semi_sync_source_enabled = 1;
SET GLOBAL rpl_semi_sync_source_timeout = 10000;  -- 10 segundos

Replicación Síncrona

La primaria espera a que todas las réplicas (o un quórum configurado) confirmen. Cero pérdida de datos, a costa de latencia de escritura.

-- PostgreSQL synchronous_commit
SET synchronous_commit = 'remote_apply';
SET synchronous_standby_names = 'replica1, replica2';

Lag de Replicación

El lag es el retraso entre una escritura en la primaria y su aparición en las réplicas. Qué lo causa y qué hacer:

CausaMitigación
Latencia de redRegiones cercanas, wal_compression
Alto volumen de escriturasSharding de escrituras, más réplicas
Transacciones grandesDividir en lotes más pequeños
Hardware lento en réplicaIgualar specs de la réplica a la primaria
Lecturas compitiendo con applyRéplicas de lectura dedicadas

Detectar el Lag

-- PostgreSQL (ejecutar en la réplica)
SELECT
  now() - pg_last_xact_replay_timestamp() AS lag;

-- MySQL 8.0.22+
SHOW REPLICA STATUS\G
-- observa Seconds_Behind_Source (Seconds_Behind_Master en versiones antiguas)

Replicación Multi-Primaria

Varios nodos aceptan escrituras — tradicionalmente llamado multi-master. Desbloquea escalado de escrituras y distribución geográfica, y es donde la replicación se complica de verdad.

flowchart diagram: Primaria A

Escenarios de Conflicto

EscenarioConflicto
Misma clave insertadaViolación de clave primaria
Misma fila actualizadaLast-write-wins o merge
Fila borrada en A, actualizada en BGana update o flag de conflicto
IDs auto-incrementalesIDs duplicados entre primarias

Estrategias de Resolución de Conflictos

  1. Last-write-wins — decide el timestamp o vector clock; simple, puede perder escrituras silenciosamente
  2. Merge — la lógica de la aplicación combina ambos cambios
  3. Resolución manual — marca los conflictos para revisión humana
  4. Evitación — particiona los datos para que cada fila tenga un solo escritor; lo más simple y usualmente la respuesta correcta

Failover

Cambiar a una réplica cuando la primaria falla. Las dos variantes:

Failover Manual

# PostgreSQL: promover standby
pg_ctl promote -D /var/lib/postgresql/data

# MySQL 8.0.22+: parar réplica, resetear, promover
STOP REPLICA;
RESET REPLICA ALL;

El failover manual es simple hasta que son las 3 de la mañana y no hay nadie — ahí “lo promovemos a mano” se convierte en una hora de caída.

Failover Automático (Patroni)

# patroni.yml
scope: mycluster
restapi:
  listen: 0.0.0.0:8008
  connect_address: 10.0.0.1:8008
etcd:
  hosts: 10.0.0.10:2379,10.0.0.11:2379,10.0.0.12:2379
postgresql:
  data_dir: /var/lib/postgresql/data
  pg_hba:
    - host replication replicator 10.0.0.0/24 md5

Réplicas de Lectura para Escalado

Enruta lecturas a réplicas, escrituras a la primaria. La aplicación es dueña del enrutado — y de lidiar con el lag.

class DatabaseRouter {
  private primary: Pool;   // pg.Pool para escrituras
  private replicas: Pool[];
  private currentReplica = 0;

  getWritePool(): Pool {
    return this.primary;
  }

  getReadPool(): Pool {
    // Round-robin entre réplicas
    const pool = this.replicas[this.currentReplica];
    this.currentReplica = (this.currentReplica + 1) % this.replicas.length;
    return pool;
  }
}

// Uso
const db = new DatabaseRouter();
await db.getWritePool().query('INSERT INTO ...');
const result = await db.getReadPool().query('SELECT ...');

Temas Avanzados

Escenario: Alta Disponibilidad con Patroni + HAProxy

Sistema: cluster PostgreSQL de 3 nodos
  - 1 primaria (us-east-1a)
  - 2 réplicas (us-east-1b, us-east-1c)
  - cluster etcd (3 nodos) para consenso
  - HAProxy como balanceador

Topología:
  Clientes -> HAProxy -> Primaria (escrituras)
                       -> Réplica1 (lecturas)
                       -> Réplica2 (lecturas)

  etcd <- Patroni (health checks, elección de líder)
  Patroni gestiona: failover automático, reinicio, reconfiguración

Config Patroni:
  scope: production
  restapi:
    listen: 0.0.0.0:8008
  etcd:
    hosts: 10.0.0.10:2379,10.0.0.11:2379,10.0.0.12:2379
  postgresql:
    data_dir: /var/lib/postgresql/data
    parameters:
      wal_level: replica
      max_wal_senders: 10
      hot_standby: on
      synchronous_commit: on
      synchronous_standby_names: "FIRST 1 (replica1, replica2)"
    recovery_conf:
      primary_conninfo: "host=10.0.0.1 port=5432 user=replicator"

Config HAProxy:
  listen pg_write
    bind *:5432
    mode tcp
    option tcp-check
    tcp-check send GET /master HTTP/1.0\r\n\r\n
    tcp-check expect status 200
    server pg1 10.0.0.1:5432 check port 8008
    server pg2 10.0.0.2:5432 check port 8008 backup
    server pg3 10.0.0.3:5432 check port 8008 backup

  listen pg_read
    bind *:5433
    mode tcp
    balance roundrobin
    option tcp-check
    tcp-check send GET /replica HTTP/1.0\r\n\r\n
    tcp-check expect status 200
    server pg2 10.0.0.2:5432 check port 8008
    server pg3 10.0.0.3:5432 check port 8008

Failover automático:
  1. Patroni detecta primaria caída (health check falla)
  2. etcd ejecuta elección de líder entre réplicas
  3. La réplica ganadora se promueve a primaria
  4. HAProxy redirige el tráfico automáticamente
  5. Las demás réplicas se reconectan a la nueva primaria
  Tiempo total: 10-30 segundos

Monitoreo:
  | Métrica | Alerta |
  |---------|--------|
  | Lag de replicación | > 5s |
  | Salud Patroni | non-200 |
  | Cambios de líder etcd | > 1/h |
  | Conexiones activas | > 80% del máx |
  | Backlog de WAL | > 1GB |

Lecciones:
  - Patroni + etcd da failover automático sólido
  - HAProxy distingue primaria/réplica vía API REST de Patroni
  - synchronous_commit garantiza cero pérdida de datos
  - 3 nodos es el mínimo para quórum (nunca uses 2)
  - Prueba el failover regularmente en staging

Errores Comunes

  • Ignorar el lag de replicación — una lectura justo después de escribir puede caer en una réplica que aún no la aplicó
  • Escribir en réplicas — crea split-brain y datos divergentes
  • Sin automatización de failover — minutos de trabajo manual se convierten en una hora de caída
  • Vigilar solo Seconds_Behind_Source — marca cero mientras la réplica aún está aplicando una transacción grande
  • Réplicas subdimensionadas — si no aguantan el throughput de escritura de la primaria, el lag crece para siempre

Solución de Problemas

  • El lag no deja de crecer: localiza primero el cuello de botella — red, I/O de disco en la réplica, o una transacción larga. Divide escrituras masivas en lotes y revisa pg_stat_replication para ver dónde se atasca el stream.
  • La réplica no logra ponerse al día: si se genera WAL más rápido de lo que la réplica lo aplica, el límite es el apply single-threaded. Activa apply paralelo donde se soporte, o reduce los picos de escritura.
  • Replication slot llenando el disco: una réplica muerta mantiene su slot reteniendo WAL en la primaria. Elimínalo con pg_drop_replication_slot o llenará el volumen y tumbará la primaria.
  • Split-brain tras un failover: dos nodos creen ser primaria. Para uno inmediatamente, resuelve la divergencia con pg_rewind y reconéctalo como réplica. Nunca corras dos primarias escribibles fuera de un setup multi-primaria real.
  • La réplica no arranca tras la promoción: revisa primary_conninfo y las credenciales de replicación — una réplica mal repuntada falla silenciosamente en los logs.

Ver También

Preguntas frecuentes

¿La replicación reemplaza los backups?

No. La replicación protege contra fallos de nodo, no contra corrupción, ransomware o un DROP TABLE accidental — ese tipo de daño se replica a todos los nodos en segundos. Mantén backups separados y probados.

¿Debería usar replicación síncrona o asíncrona?

Asíncrona para la mayoría de cargas — no frena las escrituras. Usa síncrona (o synchronous_commit = remote_apply) para datos donde perder los últimos segundos de transacciones es inaceptable, aceptando el coste de latencia.

¿Cómo manejo cambios de esquema en sistemas replicados?

Usa herramientas de cambio de esquema online (pt-online-schema-change, gh-ost, o DDL online nativo) para que el DDL no bloquee las réplicas. Aplica en la primaria y deja que replique — aplicar cambios de esquema directo en réplicas rompe el stream.

¿Cómo manejo el split-brain?

Prevenir gana a resolver: Patroni usa quórum de etcd para que solo un nodo tenga el lock de líder, y degrada a solo-lectura si etcd no responde. Si ocurre igual, para un nodo inmediatamente, rebobina la divergencia con pg_rewind y reconéctalo como réplica.

¿Puedo replicar entre proveedores cloud?

Sí, pero espera más latencia y costes de egress. La replicación lógica es la opción práctica aquí — puedes sincronizar solo las tablas que necesitas en vez de enviar todo el WAL.

¿Cuánto lag de replicación es aceptable?

La analítica tolera minutos. Las lecturas de cara al usuario necesitan menos de un segundo o una ruta de vuelta a la primaria. Las escrituras financieras necesitan replicación síncrona — tolerancia cero.

¿Cuál es el tamaño mínimo de cluster para failover automático?

Tres nodos, siempre. Los sistemas por quórum (Patroni+etcd) no pueden elegir líder con dos — un nodo caído no deja mayoría. Los setups "HA" de dos nodos son la clásica falsa economía.