Lección 3 de 311 min
Cómo elegir una base de datos
El teorema CAP, ACID frente a BASE, replicación, sharding y normalización.
Tu app empezó con cien usuarios y una base de datos en un servidor pequeño. Ahora tiene un millón, la página principal va lenta en hora punta y alguien propone «pasar a NoSQL» o «hacer sharding». Antes de decidir, conviene entender qué se gana y qué se pierde con cada opción.
Esta lección reúne los conceptos con los que se razona al elegir y hacer crecer una base de datos: qué pasa cuando la red falla entre servidores, qué significa que los datos sean «eventualmente» consistentes, cómo se escala, se replica y se reparte, y cuándo conviene duplicar datos a propósito.
Consistencia y disponibilidad
En cuanto tus datos viven en más de una máquina, aparece una pregunta incómoda: si esas máquinas dejan de poder hablar entre sí, ¿qué prefieres, responder siempre o responder solo cuando estás seguro de tener el dato más reciente?
El teorema CAP
Si la red se parte, un sistema distribuido elige entre consistencia y disponibilidad.
- Formulado por:
- Eric Brewer
- Desde:
- 2000
El teorema CAP, que formuló Eric Brewer en 2000 y demostraron Seth Gilbert y Nancy Lynch en 2002, habla de tres propiedades de un sistema con datos copiados en varios nodos. Consistencia: toda lectura ve la última escritura, o devuelve un error. Disponibilidad: toda petición recibe una respuesta, aunque quizá no la más reciente. Tolerancia a particiones: el sistema sigue funcionando aunque se pierdan mensajes entre nodos.
En una red real las particiones ocurren, así que no puedes renunciar a la P. La elección de verdad es qué hacer mientras dura una partición: un sistema CP rechaza peticiones para no dar datos incoherentes; uno AP sigue respondiendo y reconcilia después. Muchas bases de datos permiten elegir por operación: DynamoDB, por ejemplo, ofrece lecturas fuertemente consistentes o eventuales.
Úsalo cuando
Para razonar qué debe pasar en tu sistema cuando falla la red: un banco prefiere rechazar una operación; un contador de «me gusta», seguir funcionando.
Ten en cuenta
El resumen «elige dos de tres» engaña, porque la P no es opcional. Y la C de CAP no es la C de ACID: aquí significa que todos los nodos coinciden en el valor más reciente. Además, CAP no dice nada de la latencia, que es el precio de la consistencia incluso cuando la red va bien.
// Por defecto, consistencia eventual: más barata, quizá no la última versión
await db.send(new GetCommand({
TableName: "Tienda",
Key: { PK: "CLIENTE#7", SK: "PERFIL" },
}));
// Fuertemente consistente: ve siempre la última escritura confirmada
await db.send(new GetCommand({
TableName: "Tienda",
Key: { PK: "CLIENTE#7", SK: "PERFIL" },
ConsistentRead: true,
}));ACID frente a BASE
Coherencia inmediata o disponibilidad, aceptando que las copias tarden en coincidir.
- BASE:
- Basically Available, Soft state, Eventually consistent
ACID, que viste en la lección de bases relacionales, garantiza que cada transacción deja los datos coherentes y que cualquier lectura posterior ve lo confirmado. BASE (Basically Available, Soft state, Eventually consistent) prioriza que el sistema esté siempre disponible y acepta la consistencia eventual: si dejan de llegar escrituras, todas las copias acabarán coincidiendo, pero justo después de escribir alguna lectura puede devolver el valor antiguo.
El DNS, que viste en el módulo 1, es el ejemplo clásico: cambias un registro y durante un tiempo unos servidores responden lo nuevo y otros lo viejo. La mayoría de los sistemas reales mezclan los dos enfoques: ACID para pagos y pedidos, consistencia eventual para el contador de visitas, el buscador o los paneles de analítica.
Úsalo cuando
BASE encaja cuando ver un dato con unos segundos de retraso no causa ningún daño y la disponibilidad o la escala importan más. Si un dato desfasado cuesta dinero, ACID.
Ten en cuenta
La consistencia eventual traslada complejidad a tu código: mensajes duplicados, escrituras en conflicto (con «gana la última», una de ellas se pierde) y usuarios que no ven su propio cambio al recargar.
t = 0 ms Ana cambia su foto de perfil → se escribe en el nodo A
t = 50 ms Luis abre el perfil de Ana → lee del nodo B: foto antigua
t = 300 ms El cambio llega al nodo B → desde aquí, todos ven la nuevaCrecer: escalar, replicar y repartir
Escalado vertical y horizontal
Una máquina más grande, o más máquinas repartiéndose el trabajo.
- Vertical:
- Máquina más grande
- Horizontal:
- Más máquinas
Escalar en vertical es dar más recursos a la misma máquina: más CPU, más memoria, discos más rápidos. No cambia nada en tu código y, con un servicio gestionado, puede ser casi un clic. Pero tiene un techo, el coste crece muy deprisa en las máquinas grandes y sigues teniendo un único punto de fallo.
Escalar en horizontal es añadir máquinas que se reparten el trabajo: réplicas para las lecturas, shards para los datos. No tiene un techo claro y tolera la caída de una máquina, pero introduce todos los problemas de los sistemas distribuidos que ves en esta lección.
Úsalo cuando
Con una base relacional, escala primero en vertical: un servidor moderno aguanta muchísimo. Antes incluso de eso, revisa los índices y las consultas lentas.
Ten en cuenta
Pasar a horizontal no es gratis ni reversible con facilidad: añade latencia de red, consistencia eventual y más piezas que vigilar. Hazlo cuando lo pidan los datos, no por si acaso.
Replicación
Copias de los datos en varios servidores: un primario escribe, las réplicas leen.
- Esquema típico:
- Un primario y varias réplicas
En el esquema más habitual, un servidor primario recibe todas las escrituras y las envía a una o varias réplicas, que sirven lecturas. Así multiplicas la capacidad de lectura, puedes ejecutar informes pesados sin molestar al primario y, si el primario cae, promover una réplica para que ocupe su lugar (failover).
Casi siempre la replicación es asíncrona: el primario confirma la escritura sin esperar a las réplicas. Eso provoca el retraso de réplica (replica lag): durante unos milisegundos, o segundos si hay mucha carga, una réplica tiene datos antiguos. La replicación síncrona lo evita, a cambio de que cada escritura espere a la red.
Úsalo cuando
Cuando hay muchas más lecturas que escrituras, o para tener una copia lista si falla el primario.
Ten en cuenta
La replicación no es una copia de seguridad: un DELETE sin WHERE se replica a todas las copias al instante. Y si un usuario acaba de guardar algo, lee de vuelta del primario o verá sus datos antiguos.
const primario = new Pool({ host: "db-primario.interno" });
const replica = new Pool({ host: "db-replica-1.interno" });
async function cambiarNombre(id, nombre) {
await primario.query("UPDATE usuarios SET nombre = $1 WHERE id = $2", [nombre, id]);
// Acabas de escribir: lee del primario para no ver el dato antiguo
return primario.query("SELECT * FROM usuarios WHERE id = $1", [id]);
}
async function ultimosPosts() {
// Unos segundos de retraso aquí no importan: lee de la réplica
return replica.query("SELECT titulo FROM posts ORDER BY creado DESC LIMIT 20");
}Particionado y sharding
Dividir una tabla en trozos, en el mismo servidor o repartidos entre varios.
- La decisión clave:
- La clave de shard
Particionar es dividir una tabla grande en trozos según una clave, por ejemplo una partición por mes. En un solo servidor, como permite PostgreSQL, las consultas por fecha solo leen las particiones que importan y borrar un mes entero es instantáneo.
El sharding reparte esos trozos entre varios servidores: cada shard guarda una parte de los datos, por ejemplo los usuarios según un hash de su id. Es la única forma de escalar también las escrituras. MongoDB lo trae integrado, DynamoDB lo hace por ti sin que lo veas y para MySQL o PostgreSQL existen herramientas como Vitess o Citus.
Todo depende de la clave de shard: tiene que repartir los datos y el tráfico de forma uniforme y aparecer en casi todas las consultas. Una consulta que no la incluye tiene que preguntar a todos los shards.
Úsalo cuando
Particiona cuando una tabla crece sin parar y se consulta o se purga por rangos. Haz sharding cuando los datos o las escrituras ya no caben en un solo servidor, por grande que sea.
Ten en cuenta
Una mala clave crea shards calientes, que reciben casi todo el tráfico mientras los demás esperan ociosos. Las uniones y transacciones entre shards son caras, y cambiar la clave después obliga a mover todos los datos.
CREATE TABLE eventos (
creado TIMESTAMPTZ NOT NULL,
tipo TEXT NOT NULL,
datos JSONB
) PARTITION BY RANGE (creado);
CREATE TABLE eventos_2026_11 PARTITION OF eventos
FOR VALUES FROM ('2026-11-01') TO ('2026-12-01');
-- Borrar un mes entero es instantáneo: se elimina su partición
DROP TABLE eventos_2026_10;Si dudas, empieza simple
Para un proyecto nuevo, una base relacional como PostgreSQL en un solo servidor, con buenos índices, cubre muchísimo. Añade réplicas cuando las lecturas lo pidan, Redis cuando una caché lo justifique y una base especializada cuando un patrón de acceso concreto no encaje. Cada pieza nueva es algo más que operar.
Organizar los datos
Normalización y desnormalización
Cada dato en un solo sitio, o duplicado a propósito para leer más deprisa.
- Formas normales:
- 1FN, 2FN, 3FN…
Normalizar es organizar las tablas para que cada dato se guarde una sola vez: el nombre del cliente vive en clientes, y cada pedido solo guarda su cliente_id. Las formas normales (primera, segunda, tercera…) son las reglas que lo formalizan. Así un cambio se hace en un solo sitio y no aparecen copias contradictorias.
Desnormalizar es duplicar datos a propósito para que las lecturas frecuentes no necesiten uniones: guardar el nombre del cliente dentro del pedido, o un contador num_comentarios en cada post. Las lecturas se aceleran, pero cada copia hay que mantenerla al día, desde tu código, con triggers o con eventos.
Ojo: no toda copia es desnormalización. El precio guardado en la línea de un pedido es el precio en el momento de la compra, un dato distinto del precio actual del producto, y no debe cambiar si el producto se encarece.
Úsalo cuando
Normaliza por defecto en una base relacional. Desnormaliza una lectura concreta cuando hayas medido que es frecuente y lenta. En NoSQL, desnormalizar es lo normal, siguiendo los patrones de acceso.
Ten en cuenta
Cada dato duplicado es una promesa de mantenerlo sincronizado. Si no tienes claro quién actualiza cada copia y cuándo, acabarás con datos que se contradicen.
-- Normalizado: el nombre del cliente vive en un solo sitio
SELECT p.id, c.nombre
FROM pedidos p JOIN clientes c ON c.id = p.cliente_id;
-- Desnormalizado: se copia en el pedido para evitar el JOIN
ALTER TABLE pedidos ADD COLUMN nombre_cliente TEXT;
-- …pero si Ana cambia de nombre, hay que actualizar dos sitios
UPDATE clientes SET nombre = 'Ana María' WHERE id = 7;
UPDATE pedidos SET nombre_cliente = 'Ana María' WHERE cliente_id = 7;Interactivo
Tres formas de crecer
Activa o desactiva opciones para compararlas cara a cara.
| Característica | Escalar en vertical | Réplicas de lectura | Sharding |
|---|---|---|---|
| Qué mejora | Todo: CPU, memoria y disco de una máquina | Las lecturas y la disponibilidad | Lecturas, escrituras y volumen de datos |
| Cambios en la app | Ninguno | Enviar lecturas a las réplicas y tolerar el retraso | Incluir la clave de shard en las consultas |
| Límite | La máquina más grande que puedas pagar | Las escrituras siguen yendo a un solo primario | Casi ninguno, si la clave reparte bien |
| Riesgo típico | Un único punto de fallo | Leer datos antiguos justo después de escribir | Shards calientes y consultas entre shards costosas |
| Cuándo | Lo primero que deberías probar | Muchas más lecturas que escrituras | Cuando los datos o las escrituras no caben en una máquina |
Ponlo en práctica
Saber qué es cada cosa es la mitad del trabajo. La otra mitad es reconocer cuándo usarla.
Interactivo
¿Qué usarías?
Lee cada situación y elige la opción que mejor la resuelve. Fallar aquí no cuenta: es para practicar.
Tu PostgreSQL está al 90 % de CPU. Casi todo el tráfico son lecturas del catálogo de productos, que cambia pocas veces al día.
Para recordar
- El teorema CAP dice que, cuando la red se parte, un sistema distribuido tiene que elegir entre responder siempre o responder solo con datos al día.
- ACID garantiza coherencia inmediata; BASE prioriza la disponibilidad y acepta que las copias tarden en coincidir.
- Escala primero en vertical y optimiza las consultas; las réplicas multiplican las lecturas y el sharding reparte también las escrituras, a cambio de complejidad.
- La replicación no es una copia de seguridad, y el retraso de réplica puede mostrar datos antiguos justo después de escribir.
- Normaliza por defecto y desnormaliza solo donde una lectura frecuente lo justifique, sabiendo quién mantendrá cada copia al día.
Comprueba lo aprendido
Cuestionario
Responde las 5 preguntas para completar la lección.
1.Según el teorema CAP, durante una partición de red un sistema distribuido tiene que elegir entre…
2.Un usuario cambia su nombre, recarga la página y ve el nombre antiguo. Las lecturas van a una réplica asíncrona. ¿Qué ha pasado, probablemente?
3.¿Por qué la replicación no sustituye a las copias de seguridad?
4.Repartes los pedidos en shards usando el país del cliente como clave, y la gran mayoría de tus clientes son de un mismo país. ¿Qué problema tendrás?
5.Guardas el precio de cada producto en la línea del pedido, aunque también está en la tabla
productos. ¿Es una mala desnormalización?