Replicación y sharding

"Una base de datos en un solo servidor es un single point of failure. Eventualmente va a fallar — la pregunta es cuánto datos vas a perder cuando eso pase."

Qué vas a aprender en este capítulo

La Esquina tiene ahora tres locales. Si el servidor de base de datos falla, todos los locales se quedan sin sistema. Además, el servidor ya no puede manejar la carga de los tres locales simultáneamente. Las soluciones: replicación (múltiples copias del servidor para alta disponibilidad) y sharding (dividir los datos entre múltiples servidores para escalar).


3.1 Replicación

💡 Intuición

La replicación es simple en concepto: tener más de una copia de la base de datos. Si una falla, la otra sigue funcionando.

El problema es mantener las copias sincronizadas. Si escribís en el servidor principal, ¿cuándo aparece ese cambio en las copias? ¿Inmediatamente? ¿Después de un segundo? ¿Puede perder cambios si el servidor principal falla antes de que las copias se actualicen?

📐 Fundamento

Topologías de replicación:

Primary-Replica (antes: Master-Slave)

Cliente ──escrituras──→ [Primary]
                           │
                    WAL stream
                           │
              ┌────────────┴────────────┐
              ↓                         ↓
          [Replica 1]              [Replica 2]
          (solo lectura)           (solo lectura)
  • El Primary acepta escrituras y lecturas.
  • Las réplicas aceptan solo lecturas (útil para distribuir la carga de queries de reporte).
  • Si el Primary falla, se promueve una réplica a Primary (failover).

Replicación síncrona vs asíncrona:

Síncrona Asíncrona
Definición El COMMIT no termina hasta que la réplica confirma El COMMIT termina en el Primary; la réplica se actualiza después
Durabilidad Ninguna escritura se pierde Se pueden perder las últimas escrituras si el Primary falla
Latencia Mayor (espera la red) Menor
Uso Datos financieros, críticos Logs, datos menos críticos

Configurar replicación síncrona en PostgreSQL:

-- En postgresql.conf del Primary:
synchronous_commit = on
synchronous_standby_names = 'replica1'

-- En recovery.conf de la Replica:
primary_conninfo = 'host=primary user=replicator'

Problema: replication lag

En replicación asíncrona, la réplica puede estar unos segundos (o más) atrás del primary. Si una aplicación escribe en el primary y luego lee de la réplica, puede no ver el dato que acaba de escribir.

# Problema de replication lag:
db_primary.execute("INSERT INTO pedidos ...")  # escribe en primary
pedido = db_replica.execute("SELECT * FROM pedidos WHERE id = ?")  
# puede devolver None si la réplica aún no tiene el dato

Solución: Leer del primary inmediatamente después de una escritura (o usar replicación síncrona para datos críticos).


3.2 El teorema CAP

💡 Intuición

En un sistema distribuido con múltiples servidores, no podés tener las tres propiedades siguientes al mismo tiempo cuando hay una falla de red. Tenés que elegir dos.

Es como elegir entre: llegar rápido, llegar con equipaje, o llegar barato. Los tres a la vez no existen.

📐 Fundamento

El teorema CAP (Eric Brewer, 2000):

Un sistema distribuido puede garantizar como máximo dos de las tres propiedades:

Propiedad Definición
Consistency (Consistencia) Todas las réplicas ven los mismos datos al mismo tiempo
Availability (Disponibilidad) El sistema siempre responde (aunque la respuesta no sea la más actual)
Partition tolerance (Tolerancia a particiones) El sistema sigue funcionando aunque haya una falla de red entre nodos

La trampa: La tolerancia a particiones no es opcional en sistemas distribuidos reales. Las redes fallan. Entonces la elección real es: CP o AP.

                    CAP
                   /   \
                  /     \
                CP       AP
               / \       / \
              /   \     /   \
           HBase  Zookeeper  Cassandra
           MongoDB(default) CouchDB

CP (Consistencia + Tolerancia a particiones):

  • Si hay una partición de red, el sistema rechaza escrituras hasta recuperar la consistencia.
  • Ej: PostgreSQL con replicación síncrona, HBase.

AP (Disponibilidad + Tolerancia a particiones):

  • Si hay una partición de red, el sistema sigue respondiendo con datos potencialmente desactualizados.
  • Ej: Cassandra, DynamoDB, CouchDB.

¿Qué elige La Esquina?

  • Para pedidos y pagos: CP — es mejor rechazar un pedido temporalmente que cobrar dos veces.
  • Para el catálogo de platillos: AP — si la réplica muestra el precio de ayer, no es el fin del mundo.

3.3 Sharding (particionamiento horizontal)

💡 Intuición

La replicación crea copias — todos los servidores tienen todos los datos. El sharding divide los datos: cada servidor tiene una parte de los datos.

Pensalo como una librería: en lugar de tener 10 copias del mismo catálogo completo, tener 10 estantes donde cada uno tiene libros de un rango del alfabeto. Buscás "García Márquez" — vas directo al estante G-M.

📐 Fundamento

Estrategias de sharding:

1. Range sharding (por rango):

Shard 1: pedidos donde id BETWEEN 1 AND 1,000,000
Shard 2: pedidos donde id BETWEEN 1,000,001 AND 2,000,000
Shard 3: pedidos donde id > 2,000,001
  • Ventaja: simple, consultas por rango eficientes.
  • Desventaja: hot spots — si todos los nuevos pedidos van al shard 3, ese shard recibe toda la carga.

2. Hash sharding:

shard_id = hash(pedido_id) % num_shards
# pedido_id = 12345 → hash = 9876543 → 9876543 % 3 = 0 → Shard 0
  • Ventaja: distribución uniforme de carga.
  • Desventaja: consultas por rango requieren consultar todos los shards.

3. Directory-based sharding:

Un servidor de metadatos (lookup service) mantiene un mapa de "qué shard tiene qué datos".

Lookup: pedido_12345 → Shard 2
Lookup: pedido_99999 → Shard 0
  • Ventaja: más flexible, permite mover shards.
  • Desventaja: el lookup service es un single point of failure.

Problemas del sharding:

Problema Descripción Mitigación
Cross-shard queries JOIN entre datos en diferentes shards es muy costoso Desnormalizar, mantener datos relacionados juntos
Cross-shard transactions ACID entre shards requiere protocolo 2PC (Two-Phase Commit) Evitar transacciones cross-shard
Resharding Agregar un nuevo shard requiere redistribuir datos Consistent hashing para minimizar movimiento
Hotspots Algunos shards reciben más carga Hash sharding o sharding por tiempo

Consistent hashing:

En lugar de hash(key) % N (que requiere redistribuir todos los datos si N cambia), consistent hashing organiza los shards en un "anillo". Al agregar o quitar un shard, solo se mueven los datos del shard adyacente — no todos.

Anillo de hash (valores 0-360):
Shard A: 0-120
Shard B: 120-240
Shard C: 240-360

Al agregar Shard D en posición 180:
Shard A: 0-120    (sin cambio)
Shard B: 120-180  (reducido)
Shard D: 180-240  (recibe datos de B)
Shard C: 240-360  (sin cambio)

Solo se mueven los datos entre 180 y 240.

🛠️ En la práctica

La Esquina: estrategia de replicación para tres locales

La Esquina tiene tres locales: San Miguel Centro, San Miguel Norte, y Usulután. La arquitectura recomendada:

                    [Primary — San Miguel]
                         │
              ┌──────────┴──────────┐
              ↓                     ↓
    [Replica — SMN]         [Replica — Usulután]
    (sync para pedidos)     (async para reportes)

Reglas de la aplicación:

  1. Escrituras (nuevos pedidos, pagos): siempre al Primary.
  2. Lecturas de reportes y estadísticas: pueden ir a las réplicas.
  3. Lecturas inmediatamente después de una escritura (ej: confirmar que el pedido se registró): ir al Primary.

Para escalar lecturas: Agregar más réplicas. La réplica de Usulután puede servir reportes de fin del día sin impactar el Primary que está recibiendo pedidos en tiempo real.

Para escalar escrituras: Si los 3 locales generan demasiados pedidos para un solo Primary, se puede hacer sharding por local_id:

  • Shard 0: local San Miguel Centro
  • Shard 1: local San Miguel Norte
  • Shard 2: local Usulután

Cada local escribe a su shard y lee del Primary global para datos compartidos (menú, inventario central).


3.4 Ejercicios

✏️ Ejercicio 3.1 — CAP en contexto

Para cada sistema, indicá si elegirías CP o AP y justificá:

a. Sistema de registro de votos en una elección. b. Red social: timeline de publicaciones. c. Sistema de reservas de asientos en un avión. d. Catálogo de productos de un e-commerce.

✏️ Ejercicio 3.2 — Diseñar sharding

La tabla pedidos de La Esquina tiene 50 millones de filas y crece a 500,000 por mes. Diseñá una estrategia de sharding respondiendo:

a. ¿Qué columna usarías como shard key? Justificá. b. ¿Hash sharding o range sharding? ¿Por qué? c. ¿Qué consultas se verían perjudicadas por tu diseño? d. ¿Cómo reorganizarías los datos para minimizar cross-shard queries?


3.5 Para profundizar


Definiciones nuevas: replicación, primary-replica, failover, replication lag, WAL streaming, teorema CAP, partición de red, sharding, shard key, range sharding, hash sharding, consistent hashing, hot spot, cross-shard query, Two-Phase Commit.