DBA Junior Ciberecus MX
Temas · Capítulo 3 · PF-021

Tema 21: Replicación y alta disponibilidad — los calpixqui del altépetl

#PF-021 | XP: 320 | Insignia: Tlatoani de la Replica | Tier: Senior | Tiempo: 4.5 h

Visión mexica — calpixqui llevando tributos a la provincia, altépetl espejo

Objetivo

Configurarás replicación asíncrona en MySQL y PostgreSQL, aprenderás a promover un standby a primario, configurarás failover automático, y entenderás los patrones de alta disponibilidad (replicación, cluster, load balancing).

Mapa del tema

    Conceptos: primaria, réplica, síncrona vs asíncrona Replicación en MySQL: binlog + relay log Replicación en PostgreSQL: streaming replication + WAL Failover manual y automático Patrones de alta disponibilidad LAB: configurar un primario y un standby

Visión mexica — Por Citlalli

El sistema tributario mexica tenía una pieza clave de resiliencia: el calpixqui que llevaba tributos a las provincias no viajaba solo. Iba con una escolta y llevaba un espejo del registro: una copia exacta del padron tributario que se quedaba en la provincia. Si el calpixqui moría en el camino, el espejo seguía en su destino. Si el registro del calmécac se dañaba, los espejos de las provincias permitían reconstruirlo.

La replicación de bases de datos es exactamente eso: el altépetl original (primario) mantiene espejos en otros altépetl (réplicas). Si el original cae, uno de los espejos se convierte en la nueva fuente. La diferencia con un simple respaldo: la réplica está viva, recibe cada transacción en tiempo real (o casi), y puede tomar el mando en segundos.


Bloque 1 — Conceptos: primaria, réplica, síncrona vs asíncrona

Términos clave

  • Primaria (master): la base que recibe todas las escrituras.
  • Réplica (standby/slave): copia que recibe los mismos cambios.
  • Síncrona: la primaria espera confirmación de la réplica antes de confirmar al cliente. Más seguro, más lento.
  • Asíncrona: la primaria no espera. Más rápido, pero si cae puedes perder datos recientes.
  • Semi-síncrona: la primaria espera al menos una réplica, pero no todas. Balance.

Diagrama de replicación asíncrona

Cliente → INSERT → Primaria → [binlog/WAL] → Réplica
                    ↓
                  ACK al cliente (no espera a la réplica)

Diagrama de replicación síncrona

Cliente → INSERT → Primaria → [binlog/WAL] → Réplica
                    ↓                          ↓
                  ACK ←──────── ACK réplica ────┘
                    ↓
                  ACK al cliente

Cuándo usar cada una

| Caso | Modo | Justificación | |---|---|---| | E-commerce, banca | Síncrona (o semi) | Cero pérdida tolerable | | Blog personal | Asíncrona | RPO de minutos es aceptable | | Analítica | Asíncrona con horas de retraso | No requiere datos en tiempo real | | Geo-redundancia | Asíncrona entre regiones | La latencia entre regiones haría la síncrona muy lenta |


Bloque 2 — Replicación en MySQL: binlog + relay log

Configurar el servidor primario

`my.cnf` en el primario:

[mysqld]
server_id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
binlog_row_image = FULL
gtid_mode = ON
enforce_gtid_consistency = ON

Crear usuario de replicación:

CREATE USER 'replica_user'@'%' IDENTIFIED WITH caching_sha2_password BY 'Replica_2026!';
GRANT REPLICATION SLAVE ON . TO 'replica_user'@'%';
FLUSH PRIVILEGES;

Configurar el servidor réplica

`my.cnf` en la réplica:

[mysqld]
server_id = 2
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
gtid_mode = ON
enforce_gtid_consistency = ON
relay_log = /var/log/mysql/relay-bin.log
read_only = ON  # Evita escrituras accidentales

Tomar un snapshot del primario y restaurarlo en la réplica:

# En el primario
mysqldump -u root -p --single-transaction --triggers --routines --events \
  --master-data=2 \
  tienda_tlalli | gzip > /tmp/tienda_full.sql.gz

Copiar al servidor réplica

scp /tmp/tienda_full.sql.gz replica:/tmp/

En la réplica

gunzip -c /tmp/tienda_full.sql.gz | mysql -u root -p

Configurar la réplica para conectar al primario:

-- En la réplica
CHANGE MASTER TO
  MASTER_HOST = '192.168.1.10',
  MASTER_USER = 'replica_user',
  MASTER_PASSWORD = 'Replica_2026!',
  MASTER_AUTO_POSITION = 1;  -- Usa GTID

START SLAVE;

Verificar:

SHOW SLAVE STATUS\G
-- Slave_IO_Running: Yes
-- Slave_SQL_Running: Yes
-- Seconds_Behind_Master: 0

Probar la replicación

En el primario:

USE tienda_tlalli;
INSERT INTO resenas (cliente_id, producto_id, calificacion, comentario)
VALUES (1, 5, 5, 'Excelente cacao ceremonial');

En la réplica:

SELECT  FROM resenas ORDER BY resena_id DESC LIMIT 1;
-- Debe aparecer el nuevo registro en < 1 segundo (asíncrona)

Promoción de réplica a primaria (failover manual)

Si el primario cae:

-- En la réplica
STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only = OFF;

Ahora la réplica acepta escrituras. Actualiza la configuración de la app para apuntar a la nueva IP.


Bloque 3 — Replicación en PostgreSQL: streaming replication

Configurar el servidor primario

`postgresql.conf`:

wal_level = replica
max_wal_senders = 3
wal_keep_size = '1GB'

`pg_hba.conf`:

# Permitir réplica desde la IP de la réplica
host  replication  replica_user  192.168.1.20/32  scram-sha-256
CREATE ROLE replica_user WITH LOGIN REPLICATION PASSWORD 'Replica_2026!';

Configurar el servidor réplica

Tomar un backup base con `pg_basebackup`:

pg_basebackup -U replica_user -h 192.168.1.10 \
  -D /var/lib/postgresql/16/main -Fp -Xs -P

Crear archivo `standby.signal`:

touch /var/lib/postgresql/16/main/standby.signal

`postgresql.conf` en la réplica:

primary_conninfo = 'host=192.168.1.10 port=5432 user=replica_user password=Replica_2026!'
promote_trigger_file = '/tmp/promote_to_primary'  # Failover manual

Iniciar PostgreSQL en modo standby:

sudo systemctl start postgresql

Verificar:

SELECT  FROM pg_stat_replication;
-- En el primario, debe mostrar la réplica conectada

-- En la réplica SELECT pg_is_in_recovery(); -- Esperado: t (true)

Failover manual (switchover)

# En la réplica
touch /tmp/promote_to_primary

O explícitamente:

pg_ctl promote -D /var/lib/postgresql/16/main

Verificar:

SELECT pg_is_in_recovery();
-- Esperado: f (false, ahora es primaria)

Failover automático con Patroni

Patroni es la opción más usada para HA en PostgreSQL. Combina:

  • Replicación streaming.
  • Etcd/Zookeeper para consenso de quién es el primario.
  • VIP (IP virtual) que se mueve al nuevo primario.
  • Reinicio automático del PostgreSQL si falla.
Stack típico:

[App] → [HAProxy] → [Patroni + PostgreSQL #1] ⇄ [Patroni + PostgreSQL #2]
                        ↑                              ↑
                        └── [Etcd cluster] ─────────────┘

Patroni se encarga de:

  • Detectar falla del primario.
  • Promover la réplica con menos lag.
  • Mover la VIP.
  • Actualizar HAProxy para enviar tráfico al nuevo primario.
Tiempo de failover: 10-30 segundos.


Bloque 4 — Failover manual y automático

Switchover planeado (ej. mantenimiento del primario)

    Avisar a las apps que habrá un corte breve. En el primario: `SET GLOBAL read_only = ON;` (rechaza nuevas escrituras). Esperar a que la réplica se ponga al día (lag = 0). Promover la réplica. Apuntar la app a la nueva IP. (Opcional) Convertir el viejo primario en réplica.

Failover de emergencia (el primario se cayó)

    Confirmar que el primario está caído (no es solo lag de red). Si usas Patroni: automático, esperar 30 segundos. Si no: promover manualmente la réplica. Actualizar DNS o la config de la app. Investigar la causa: ¿hardware? ¿red? ¿bug?

Pre-flight checks antes de promover

  • Lag de la réplica: ¿está al día? Si hay lag de horas, perderás esas horas de datos.
  • Conexiones activas: ¿hay queries en curso en el primario caído?
  • WALs pendientes: ¿hay WALs en el primario que no se enviaron a la réplica?
-- En la réplica, ver el último WAL recibido
SELECT pg_last_wal_receive_lsn();
SELECT pg_last_wal_replay_lsn();
-- Si difieren, hay WALs pendientes


Bloque 5 — Patrones de alta disponibilidad

Patrón 1: Réplica de lectura (read replica)

[App writes] → [Primaria]
[App reads]  → [Réplica 1, Réplica 2, Réplica 3]

  • La primaria maneja todas las escrituras.
  • Las réplicas manejan lecturas (reportes, dashboards, búsquedas).
  • Reduce carga en la primaria.

Patrón 2: Hot standby + failover automático

[App] → [HAProxy] → [Primaria] ⇄ [Standby]
                          ↑
                       [Patroni + etcd]

  • Una réplica que puede tomar el mando en 30 segundos.
  • Útil para e-commerce, banca.

Patrón 3: Multi-región

[App región A] → [Primaria A] ⇄ [Réplica B] (en otra región)
                                          ↓
                              [Réplica A'] (lectura local en A)

  • Protección contra caída de toda una región (terremoto, corte de energía masivo).
  • Latencia entre regiones hace que la replicación sea asíncrona.
  • RPO puede ser de minutos.

Patrón 4: Cluster (Galera Cluster para MySQL)

[App] → [Nodo 1] ⇄ [Nodo 2] ⇄ [Nodo 3]

  • Todos los nodos aceptan lecturas y escrituras.
  • Consenso síncrono (certification-based).
  • Latencia de commit mayor que replicación asíncrona.
  • Útil cuando necesitas geolocalización de escrituras.

Patrón 5: Cloud-managed (AWS RDS Multi-AZ, Aurora, etc.)

  • El proveedor maneja replicación, failover, backups.
  • Failover de RDS Multi-AZ: 60-120 segundos.
  • Costo mayor pero menos operación.

AI Mission

Pídele a tu IA que diseñe la arquitectura HA para tu caso. Prompt sugerido:

Tengo una base de datos MySQL 8 con 50 GB de datos, 500 escrituras/segundo
pico, 5000 lecturas/segundo, RPO objetivo de 5 minutos, RTO de 1 minuto,
presupuesto de $200 USD/mes en infraestructura.

Dame:

    Diagrama de la arquitectura HA que cumpla RPO y RTO. Proveedor cloud recomendado (AWS, GCP, Azure, DigitalOcean). Estimación de costo mensual. Procedimiento de failover documentado en 5 pasos. Riesgos que no se cubren con esta arquitectura.
Aplica la recomendación, pero verifica los costos en la página del proveedor antes de decidir. La IA puede subestimar o sobreestimar según su entrenamiento.


Errores típicos

    Réplica con `read_only = OFF` accidentalmente → escrituras divergentes, replicación rota. No monitorear el lag de la réplica → failover con horas de datos perdidos. Failover sin probar → el día del incidente, los comandos fallan o la red no coopera. DNS con TTL alto (24h) para el primario → el failover tarda horas en propagarse. Usar TTL bajo (60s) o VIP flotante. Réplica en la misma máquina que el primario → si la máquina cae, se cae todo. Replicación cruzada circular (A → B → A) → caos garantizado.

LAB práctico — Configurar primario y standby

Objetivo: levantar un primario y una réplica asíncrona en MySQL o PostgreSQL, verificar que la replicación funciona, y practicar el failover manual.

Opción A: MySQL con dos instancias en la misma máquina

# Crear segundo datadir
sudo mysql_install_db --datadir=/var/lib/mysql-replica --user=mysql

Inicializar con configuración distinta

sudo nano /etc/mysql/replica.cnf
[mysqld]
port = 3307
datadir = /var/lib/mysql-replica
server_id = 2
log_bin = /var/log/mysql-replica/mysql-bin.log
read_only = ON
sudo chown -R mysql:mysql /var/lib/mysql-replica /var/log/mysql-replica
sudo mysqld --defaults-file=/etc/mysql/replica.cnf &

Opción B: PostgreSQL con `pg_basebackup`

(Ver Bloque 3.)

Paso 2 — Configurar la replicación

MySQL:

-- En el primario
CREATE USER 'replica_user'@'%' IDENTIFIED WITH caching_sha2_password BY 'Replica_2026!';
GRANT REPLICATION SLAVE ON . TO 'replica_user'@'%';
FLUSH PRIVILEGES;

-- Snapshot mysqldump -u root -p --single-transaction --master-data=2 tienda_tlalli > /tmp/tienda.sql

-- En la réplica mysql -u root -p -P 3307 -e "CREATE DATABASE tienda_tlalli;" mysql -u root -p -P 3307 tienda_tlalli < /tmp/tienda.sql

CHANGE MASTER TO MASTER_HOST = '127.0.0.1', MASTER_PORT = 3306, MASTER_USER = 'replica_user', MASTER_PASSWORD = 'Replica_2026!', MASTER_AUTO_POSITION = 1;

START SLAVE; SHOW SLAVE STATUS\G

PostgreSQL:

pg_basebackup -U replica_user -h 127.0.0.1 \
  -D /var/lib/postgresql/16-replica -Fp -Xs -P

touch /var/lib/postgresql/16-replica/standby.signal echo "primary_conninfo = 'host=127.0.0.1 port=5432 user=replica_user password=Replica_2026!'" \ >> /var/lib/postgresql/16-replica/postgresql.conf

Paso 3 — Probar

-- En el primario: insertar
INSERT INTO resenas (cliente_id, producto_id, calificacion, comentario)
VALUES (1, 1, 5, 'Replica funciona');
-- En la réplica: verificar
SELECT * FROM resenas ORDER BY resena_id DESC LIMIT 1;
-- Debe aparecer

Paso 4 — Failover manual

MySQL:

-- En la réplica
STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only = OFF;

PostgreSQL:

pg_ctl promote -D /var/lib/postgresql/16-replica

Paso 5 — Verificar que la nueva primaria acepta escrituras

INSERT INTO resenas (...) VALUES (...);
-- Debe funcionar

Entregables del LAB

  • [ ] Réplica conectada y replicando (`Seconds_Behind_Master = 0`).
  • [ ] Inserción en primario visible en réplica en < 5 segundos.
  • [ ] Failover manual ejecutado exitosamente.
  • [ ] Antigua primaria reconectada como nueva réplica (opcional).
  • [ ] Documento `REPLICACION.md` con la arquitectura, comandos, y procedimiento de failover.

Quest

Preguntas de opción múltiple (10)

1. ¿Qué es una réplica asíncrona?

A. La primaria espera confirmación de la réplica antes de hacer commit. B. La primaria no espera a la réplica; puede haber pequeña pérdida de datos en failover. C. Las réplicas escriben a la primaria. D. No existe ese concepto.

2. ¿Qué archivo se usa para activar el modo standby en PostgreSQL?

A. `postgresql.conf` B. `recovery.conf` C. `standby.signal` D. `pg_hba.conf`

3. ¿Qué hace `read_only = ON` en MySQL?

A. Impide lecturas. B. Impide escrituras (excepto las de usuarios con SUPER). C. Activa la réplica. D. Cambia el charset.

4. ¿Qué herramienta automatiza el failover en PostgreSQL?

A. pg_dump. B. Patroni. C. psql. D. pg_basebackup.

5. ¿Qué variable MySQL muestra el atraso de la réplica?

A. `Slave_Lag` B. `Seconds_Behind_Master` C. `Replica_Delay` D. `Read_Lag`

6. ¿Qué es RPO?

A. Recovery Point Objective: pérdida de datos tolerable. B. Recovery Process Order. C. Read Parallel Operations. D. Relational Performance Optimizer.

7. ¿Qué es RTO?

A. Recovery Time Objective: tiempo de inactividad tolerable. B. Real-Time Operations. C. Read Through Output. D. Relational Table Optimization.

8. ¿Qué hace `pg_basebackup`?

A. Respaldo lógico. B. Respaldo físico base mientras la base sigue corriendo. C. Borra la base. D. Activa la réplica.

9. ¿Qué modo de replicación tiene mayor latencia de commit?

A. Asíncrona. B. Síncrona. C. Semi-síncrona. D. Ninguna.

10. ¿Por qué es importante monitorear el lag de la réplica?

A. Para hacer la base más rápida. B. Para saber cuántos datos se perderían en un failover. C. Para ahorrar CPU. D. No es importante.

Preguntas abiertas (3)

A. Explica la diferencia entre replicación síncrona y asíncrona. ¿Cuándo usarías cada una en un sistema de e-commerce?

B. Tu primaria se cayó y necesitas hacer failover manual. Lista los 5 pasos en orden y los riesgos de cada uno.

C. Compara Galera Cluster con replicación asíncrona de MySQL. Para una startup con 10K usuarios concurrentes, ¿cuál elegirías y por qué?


Respuestas modelo

1. B — Asíncrona = la primaria no espera. La A es síncrona. La C es absurdo. La D es falsa.

2. C — `standby.signal`. La A es config. La B ya no se usa. La D es autenticación.

3. B — `read_only = ON` impide escrituras a usuarios normales. La A es lo contrario. La C no es lo que hace. La D es charset.

4. B — Patroni. La A es respaldo. La C es cliente. La D es backup.

5. B — `Seconds_Behind_Master`. La A, C, D no existen.

6. A — RPO = Recovery Point Objective. La B, C, D son ficticias.

7. A — RTO = Recovery Time Objective. La B, C, D son ficticias.

8. B — `pg_basebackup` = respaldo físico base en línea. La A es `pg_dump`. La C es `DROP`. La D es configuración manual.

9. B — Síncrona espera ACK de la réplica, mayor latencia. La A es la más rápida. La C es balance. La D no aplica.

10. B — Para saber la pérdida de datos en failover. La A no es su propósito. La C no es cierto. La D es falsa.


Respuestas a las preguntas abiertas

A. Síncrona: la primaria espera confirmación de la réplica antes de retornar al cliente. Cero pérdida de datos, pero mayor latencia de commit. Asíncrona: la primaria no espera, puede haber pérdida si la primaria cae antes de enviar los cambios. Para e-commerce, síncrona o semi-síncrona es preferible para datos críticos (pedidos, pagos); asíncrona para datos secundarios (logs, métricas).

B. Cinco pasos:

    Confirmar caída del primario (no es lag de red). Verificar lag de la réplica candidata (si hay horas de lag, esperar o aceptar pérdida). Promover la réplica (`STOP SLAVE; RESET SLAVE ALL; SET GLOBAL read_only = OFF` en MySQL; `pg_ctl promote` en PostgreSQL). Apuntar la app a la nueva IP (cambiar DNS, VIP o config). Investigar la causa y restaurar la antigua primaria como nueva réplica.
Riesgos: pérdida de datos si hay lag, nueva primaria con bugs, tiempo de reconfiguración de la app, IP o DNS no propagado.

C. Para una startup con 10K usuarios concurrentes: Galera Cluster si necesitas geolocalización de escrituras o alta disponibilidad inmediata; replicación asíncrona si la mayoría son lecturas y un failover de 30 segundos es aceptable. Galera tiene mayor latencia de commit y requiere al menos 3 nodos (quorum). Para una startup, RDS Multi-AZ (cloud managed) suele ser la opción más pragmática: el proveedor maneja la replicación y failover.


Tras las huellas — Por Citlalli

El sistema de calpixqui que llevaban tributos a las provincias tenía un detalle brillante: si un calpixqui no llegaba a su destino en 20 días, otro partía con una copia del registro. No esperaban a confirmar que el primero había llegado. Esa es la esencia de la replicación asíncrona con múltiples destinos: el sistema sigue funcionando aunque una pieza falle.

La diferencia con los mexicas es que hoy no esperamos 20 días. Con la replicación moderna, los espejos se actualizan en segundos, y la pérdida tolerable de una réplica se mide en segundos también. Pero el principio es el mismo: múltiples copias, destinos distribuidos, failover automático cuando un camino falla.

Sabiduría mexica: no confíes en un solo camino. Siempre ten un espejo, y otro, y otro.

Cierre del cuaderno

    Diferencia entre replicación síncrona, asíncrona y semi-síncrona. Comandos para configurar una réplica en MySQL. Archivo que activa el modo standby en PostgreSQL. Mi RPO y RTO actuales vs objetivos del negocio: ¿estoy dentro? ¿He probado el failover en los últimos 6 meses? ____________.