TechLead
Leccion 12 de 30
7 min de lectura
Diseño de Sistemas

Estrategias de Replicación de Base de Datos

Domina la replicación de base de datos incluyendo maestro-esclavo, multi-maestro, replicación síncrona vs asíncrona, modelos de consistencia y resolución de conflictos

¿Qué Es la Replicación de Base de Datos?

La replicación de base de datos es el proceso de copiar y mantener objetos de base de datos a través de múltiples servidores. La replicación proporciona redundancia, mejora el rendimiento de lectura y asegura alta disponibilidad.

¿Por Qué Replicar?

  • Alta Disponibilidad: Si un nodo falla, otros pueden continuar sirviendo solicitudes.
  • Escalabilidad de Lectura: Distribuir consultas de lectura entre múltiples réplicas.
  • Distribución Geográfica: Colocar datos más cerca de los usuarios en diferentes regiones.
  • Recuperación ante Desastres: Mantener copias en diferentes centros de datos.
  • Aislamiento de Analíticas: Ejecutar consultas analíticas pesadas en réplicas sin impactar producción.

Replicación Maestro-Esclavo (Primary-Replica)

La topología de replicación más común. Un nodo se designa como maestro (primario) y maneja todas las operaciones de escritura. Uno o más nodos esclavos (réplicas) reciben copias de los datos y sirven consultas de lectura.

Cómo Funciona

  • Todas las escrituras van al nodo maestro
  • El maestro escribe cambios en un log de replicación (binlog en MySQL, WAL en PostgreSQL)
  • Las réplicas consumen el log y aplican cambios a sus copias locales
  • Las consultas de lectura pueden ser servidas por cualquier réplica o el maestro
// Master-Slave routing logic
interface DatabasePool {
  master: DatabaseConnection;
  replicas: DatabaseConnection[];
}

class ReplicaRouter {
  private pool: DatabasePool;
  private currentReplica = 0;

  constructor(pool: DatabasePool) {
    this.pool = pool;
  }

  // All writes go to master
  async write(query: string, params: any[]): Promise<any> {
    return this.pool.master.execute(query, params);
  }

  // Reads are distributed across replicas (round-robin)
  async read(query: string, params: any[]): Promise<any> {
    const replica = this.pool.replicas[this.currentReplica];
    this.currentReplica = (this.currentReplica + 1) % this.pool.replicas.length;
    return replica.execute(query, params);
  }

  // For reads that must see latest writes (read-after-write consistency)
  async readFromMaster(query: string, params: any[]): Promise<any> {
    return this.pool.master.execute(query, params);
  }
}

Limitaciones de Maestro-Esclavo

  • Cuello de Botella de Escritura Única: Todas las escrituras deben pasar por un nodo, limitando el rendimiento de escritura
  • Retraso de Replicación: Las réplicas pueden servir datos obsoletos durante cargas de escritura altas
  • Complejidad de Failover: Promover una réplica a maestro requiere coordinación y puede causar breve tiempo de inactividad

Replicación Multi-Maestro

En la replicación multi-maestro, dos o más nodos aceptan operaciones de escritura. Cada maestro replica sus cambios a los otros maestros. Esto elimina el cuello de botella de escritura única pero introduce el desafío de conflictos de escritura.

Cuándo Usar Multi-Maestro

  • Despliegues multi-región: Cada región tiene un maestro local para minimizar la latencia de escritura
  • Alto rendimiento de escritura: Cuando un solo maestro no puede manejar el volumen de escritura
  • Configuraciones activo-activo: Ambos centros de datos sirven tráfico activamente de forma simultánea

Comparación de Topologías de Replicación

Aspecto Maestro-Esclavo Multi-Maestro
Nodos de EscrituraÚnicoMúltiples
Escalabilidad de EscrituraLimitada por un nodoEscala horizontalmente
Manejo de ConflictosSin conflictos (escritor único)Debe manejar conflictos
ConsistenciaMás fácil de mantenerEventualmente consistente por defecto
FailoverRequiere promociónAutomático (otros maestros disponibles)
ComplejidadMás simpleSignificativamente más compleja

Replicación Síncrona vs Asíncrona

El momento de cuándo se replican los datos a nodos secundarios es una decisión de diseño crítica que afecta tanto la consistencia como el rendimiento.

Replicación Síncrona

El maestro espera a que al menos una réplica confirme que ha escrito los datos antes de confirmar la escritura al cliente. Esto garantiza que los datos existen en múltiples nodos pero agrega latencia a cada operación de escritura.

Replicación Asíncrona

El maestro confirma la escritura inmediatamente después de escribir localmente y replica a los seguidores en segundo plano. Esto proporciona menor latencia de escritura pero arriesga pérdida de datos si el maestro falla antes de que se complete la replicación.

Replicación Semi-Síncrona

Un punto medio práctico: el maestro espera a que al menos una réplica confirme, pero no todas las réplicas. MySQL soporta esto nativamente con rpl_semi_sync_master_wait_for_slave_count.

// Illustrating sync vs async replication behavior

// Synchronous: write is only confirmed after replica acknowledges
async function syncWrite(master: DB, replicas: DB[], data: Record<string, any>): Promise<void> {
  // Write to master
  await master.insert(data);

  // Wait for at least one replica to confirm
  await Promise.any(
    replicas.map((replica) => replica.applyReplicationEvent(data))
  );

  // Only now confirm to the client
  return; // Write confirmed
}

// Asynchronous: write is confirmed immediately, replication is background
async function asyncWrite(master: DB, replicas: DB[], data: Record<string, any>): Promise<void> {
  // Write to master
  await master.insert(data);

  // Confirm to client immediately
  // Replication happens in background
  Promise.allSettled(
    replicas.map((replica) => replica.applyReplicationEvent(data))
  ).catch((err) => console.error("Replication failed:", err));

  return; // Write confirmed before replication
}

Retraso de Replicación y Consistencia Eventual

El retraso de replicación es el tiempo entre una escritura en el maestro y cuándo esa escritura se vuelve visible en las réplicas. En replicación asíncrona, el retraso puede variar de milisegundos a segundos (o incluso minutos bajo carga pesada). Esto significa que las réplicas sirven datos eventualmente consistentes.

Problemas Comunes Causados por el Retraso de Replicación

  • Inconsistencia de lectura después de escritura: Un usuario escribe datos e inmediatamente los lee de una réplica que aún no ha recibido la actualización
  • Violaciones de lectura monótona: Un usuario ve un valor más nuevo, luego en la siguiente solicitud llega a una réplica diferente más retrasada y ve un valor más antiguo
  • Violaciones de causalidad: Una respuesta a un comentario aparece antes del comentario original en una réplica retrasada

Mitigando el Retraso de Replicación

class ConsistentReader {
  private pool: DatabasePool;

  // Strategy 1: Read-after-write consistency
  // After a write, read from master for a short window
  async readAfterWrite(
    userId: string,
    query: string,
    params: any[],
    lastWriteTimestamp: number
  ): Promise<any> {
    const lagWindow = 5000; // 5 seconds
    const timeSinceWrite = Date.now() - lastWriteTimestamp;

    if (timeSinceWrite < lagWindow) {
      // Recent write, read from master
      return this.pool.master.execute(query, params);
    }

    // Safe to read from replica
    return this.readFromReplica(query, params);
  }

  // Strategy 2: Monotonic reads
  // Always route a user to the same replica within a session
  async monotonicRead(
    sessionId: string,
    query: string,
    params: any[]
  ): Promise<any> {
    const replicaIndex = this.hashToReplica(sessionId);
    return this.pool.replicas[replicaIndex].execute(query, params);
  }

  private hashToReplica(key: string): number {
    let hash = 0;
    for (let i = 0; i < key.length; i++) {
      hash = (hash * 31 + key.charCodeAt(i)) % this.pool.replicas.length;
    }
    return hash;
  }
}

Estrategias de Resolución de Conflictos

En la replicación multi-maestro, dos nodos pueden modificar la misma fila simultáneamente, creando un conflicto de escritura. Hay varias estrategias para resolver conflictos.

  • Último en Escribir Gana (LWW): La escritura con el timestamp más reciente gana. Simple pero puede perder datos. Usado por Cassandra.
  • Resolución a Nivel de Aplicación: La aplicación define lógica de fusión personalizada. Por ejemplo, un carrito de compras podría tomar la unión de elementos de ambas versiones.
  • CRDTs (Tipos de Datos Replicados Libres de Conflictos): Estructuras de datos diseñadas para fusionarse automáticamente sin conflictos. Usados en herramientas de edición colaborativa.
  • Vectores de Versión: Rastrean el historial causal de cada actualización para detectar y resolver modificaciones concurrentes. Usado por DynamoDB y Riak.
// Last Write Wins conflict resolution
interface VersionedRecord {
  data: Record<string, any>;
  timestamp: number;
  nodeId: string;
}

function resolveConflictLWW(a: VersionedRecord, b: VersionedRecord): VersionedRecord {
  if (a.timestamp > b.timestamp) return a;
  if (b.timestamp > a.timestamp) return b;
  // Tiebreaker: higher node ID wins
  return a.nodeId > b.nodeId ? a : b;
}

// Application-level merge for a shopping cart
interface CartItem { productId: string; quantity: number; }

function mergeCart(cartA: CartItem[], cartB: CartItem[]): CartItem[] {
  const merged = new Map<string, number>();
  for (const item of [...cartA, ...cartB]) {
    const current = merged.get(item.productId) || 0;
    merged.set(item.productId, Math.max(current, item.quantity));
  }
  return Array.from(merged.entries()).map(([productId, quantity]) => ({
    productId,
    quantity,
  }));
}

Herramientas de Replicación y Ejemplos del Mundo Real

Base de Datos Método de Replicación Notas
PostgreSQLReplicación de streaming (WAL)Soporta sync, async y replicación lógica
MySQLReplicación binlogBasada en fila, en sentencia o mixta; soporta Group Replication para multi-maestro
MongoDBReplica setsFailover automático con elecciones; replicación basada en oplog
CassandraPeer-to-peer (sin maestro)Todos los nodos son iguales; consistencia ajustable con lecturas/escrituras de quórum
CockroachDBConsenso RaftSQL distribuido fuertemente consistente con replicación automática

Consejos para Entrevistas

  • Por defecto usa maestro-esclavo para la mayoría de problemas de entrevista a menos que escalar escrituras sea un requisito declarado
  • Siempre menciona el retraso de replicación y cómo tu diseño maneja la consistencia de lectura después de escritura
  • Conoce las implicaciones del teorema CAP: la replicación te obliga a elegir entre consistencia y disponibilidad durante particiones
  • Discute el failover: ¿qué pasa cuando el maestro cae? Promoción manual vs automática

Continuar Aprendiendo