🔎 Buscar

🗄️ Bases de datos

Qué es una base de datos, SQL y el modelo relacional, relaciones y normalización, índices, transacciones y ACID, aislamiento, y los tipos de bases de datos (relacionales, NoSQL, in-memory). Con código y práctica.

Fundamentos CS📖 Contenido

Bases de datos

Una base de datos (DB) es un sistema para guardar datos de forma persistente y consultarlos de forma eficiente y segura. Es el corazón de casi todo backend: usuarios, pedidos, mensajes, configuraciones… Todo acaba en una base de datos.

Esta página te da el mapa completo: el modelo relacional, SQL, índices, transacciones y los tipos de bases de datos. Las wikis del sitio profundizan: SQL, Índices, Transacciones y Bases de datos en backend.

El modelo relacional y SQL

La forma dominante son las bases de datos relacionales (PostgreSQL, MySQL, SQLite, Oracle): los datos viven en tablas con filas y columnas, y las tablas se relacionan entre sí por claves. El lenguaje para hablar con ellas es SQL (Structured Query Language).

CREATE TABLE usuarios (
    id       SERIAL PRIMARY KEY,
    nombre   TEXT NOT NULL,
    email    TEXT UNIQUE,
    creado_en TIMESTAMP DEFAULT now()
);
Término Significado
Tabla Colección de filas con la misma estructura
Columna / campo Una propiedad (nombre, email…)
Fila / registro Una entrada concreta
Clave primaria (PK) Identificador único de cada fila
Clave foránea (FK) Referencia a la PK de otra tabla
Esquema La estructura definida de las tablas

CRUD

Las 4 operaciones básicas:

-- CREATE (insertar)
INSERT INTO usuarios (nombre, email) VALUES ('Ana', 'ana@mail.com');

-- READ (leer)
SELECT id, nombre FROM usuarios WHERE email = 'ana@mail.com';

-- UPDATE (actualizar)
UPDATE usuarios SET nombre = 'Ana María' WHERE id = 1;

-- DELETE (borrar)
DELETE FROM usuarios WHERE id = 1;

SELECT: la operación reina

SELECT id, nombre          -- qué columnas
FROM usuarios              -- de qué tabla
WHERE edad >= 18           -- filtro
  AND pais = 'ES'
ORDER BY creado_en DESC    -- ordenar
LIMIT 10                   -- cuántas filas
OFFSET 20;                 -- saltar (paginación)

-- Uniones (JOIN): combinar tablas por relación
SELECT u.nombre, p.titulo
FROM usuarios u
JOIN pedidos p ON p.usuario_id = u.id
WHERE p.total > 100;

-- Agregaciones
SELECT pais, COUNT(*) AS cantidad, AVG(edad)
FROM usuarios
GROUP BY pais
HAVING COUNT(*) > 5;

💡 Orden de ejecución mental de SELECT: FROMJOINWHEREGROUP BYHAVINGSELECTORDER BYLIMIT. El WHERE filtra antes de agrupar; el HAVING filtra después.

Relaciones

El modelo relacional modela el mundo con relaciones entre tablas:

Relación Cómo se modela Ejemplo
1 a 1 FK única en un lado usuario ↔ perfil
1 a muchos FK en la tabla «muchos» usuario → pedidos
Muchos a muchos Tabla puente (join table) usuarios ↔ cursos
-- Muchos a muchos: tabla puente
CREATE TABLE inscripciones (
    usuario_id INT REFERENCES usuarios(id),
    curso_id   INT REFERENCES cursos(id),
    PRIMARY KEY (usuario_id, curso_id)
);

Normalización

La normalización elimina datos duplicados y anomalías. Las reglas (formas normales) más importantes en la práctica:

  • 1NF: cada celda tiene un solo valor (nada de listas en una columna).
  • 2NF: ninguna columna depende de parte de la clave.
  • 3NF: ninguna columna depende de otra columna no clave.
MAL (datos duplicados):
usuarios: id | nombre | direccion | pedido1 | pedido2

BIEN (normalizado):
usuarios: id | nombre | direccion
pedidos:  id | usuario_id | total | fecha

⚠️ La normalización completa no siempre es lo mejor: en sistemas de lectura pesada se desnormaliza a propósito (redundancia controlada) para evitar JOINs costosos. Es un equilibrio que verás en system design.

Índices: por qué las búsquedas son rápidas

Sin índice, una búsqueda recorre toda la tabla (full scan, O(n)). Un índice es una estructura auxiliar (normalmente un árbol B+) que permite buscar en O(log n) — la búsqueda binaria de las bases de datos.

-- Crear índice en una columna que se filtra mucho
CREATE INDEX idx_usuarios_email ON usuarios (email);

-- Búsqueda que lo usa: antes O(n), ahora O(log n)
SELECT * FROM usuarios WHERE email = 'ana@mail.com';
Sin índice:   usuarios —────────────────────▶ recorre 1.000.000 filas
Con índice:   idx_email (B+tree) ──▶ 20 comparaciones ──▶ fila

💡 El motor elige el índice automáticamente (usa el query planner). Pistas para decidir: indexa columnas usadas en WHERE, JOIN y ORDER BY. No indexes todo: cada índice ocupa espacio y encarece las inserciones. Los índices compuestos y las cubiertas son un tema entero — wiki: Índices.

⚠️ Un SELECT * ... WHERE sobre una columna sin índice en una tabla de 1M de filas es un full table scan: una de las causas #1 de “la base de datos va lenta”. Ejecuta EXPLAIN ANALYZE y verás si usa índice o scan.

Transacciones y ACID

Una transacción es un grupo de operaciones que se ejecutan como una sola unidad: o todas, o ninguna. El ejemplo clásico: transferir dinero (restar de A, sumar a B). Si el sistema se cae entre medias, no puede quedar a medias.

BEGIN;                                  -- inicia la transacción
UPDATE cuentas SET saldo = saldo - 100 WHERE id = 1;
UPDATE cuentas SET saldo = saldo + 100 WHERE id = 2;
COMMIT;                                 -- confirma (ambas operaciones)
-- ROLLBACK;  -- en caso de error: deshace todo

ACID

Letra Significado En una frase
A Atomicidad O todo o nada
C Consistencia Los datos quedan válidos según las reglas
I Aislamiento Las transacciones concurrentes no se pisan
D Durabilidad Lo confirmado sobrevive a un apagón

Niveles de aislamiento

Cuanto más aisladas las transacciones, más seguras pero más lentas. Los niveles estándar (en orden de seguridad):

Nivel Fenómenos que evita Nota
Read Uncommitted Nada (puedes leer datos sin confirmar) Casi nunca se usa
Read Committed Lecturas sucias El default de PostgreSQL y la mayoría
Repeatable Read Lecturas no repetibles El default de MySQL
Serializable Todos El más seguro y lento

Fenómenos: lectura sucia (lees datos de una transacción sin confirmar), lectura no repetible (lees lo mismo dos veces y cambia), phantom (una fila nueva aparece en medio de una consulta).

⚠️ Terminología: commit (confirmar), rollback (deshacer), deadlock (dos transacciones se bloquean mutuamente — el motor mata a una y la rehaces), isolation (aislamiento), snapshot isolation (cada transacción ve una foto consistente de la DB — como hace PostgreSQL con MVCC).

MVCC

PostgreSQL y MySQL usan MVCC (Multi-Version Concurrency Control): cada transacción trabaja sobre una versión (snapshot) de los datos, sin bloquear a las demás. Lectores no bloquean a escritores y viceversa. Detalle completo en wiki: Transacciones.

Tipos de bases de datos

No todo es SQL. Según la forma de los datos, hay sistemas especializados:

Tipo Modelo Ejemplos Ideal para
Relacional (SQL) Tablas con relaciones PostgreSQL, MySQL, SQLite Datos estructurados con relaciones, transacciones
Documentos (NoSQL) JSON/BSON anidados MongoDB, CouchDB Datos con forma variable, prototipos
Clave-valor Mapas in-memory o en disco Redis, Memcached, DynamoDB Caché, sesiones, contadores
Columnar (OLAP) Columnas, no filas ClickHouse, BigQuery Analytics, agregaciones sobre millones de filas
Grafo Nodos y aristas Neo4j Relaciones profundas (social, recomendaciones)
Búsqueda Índice invertido Elasticsearch, OpenSearch Búsqueda full-text y logs
Time-series Series temporales TimescaleDB, InfluxDB Métricas, sensores, monitoreo

💡 SQL vs NoSQL: elige SQL si tus datos son relaciones y necesitas transacciones fuertes (la mayoría de los casos). Elige NoSQL cuando el shape del dato es variable, la escala exige particionado horizontal fácil, o la latencia exige in-memory. «NoSQL» significa «Not Only SQL»: no excluye SQL.

Lo que debes saber de cada uno en el plan

  • PostgreSQL: el relacional de referencia del plan. En Backend: bases de datos y en Indices.
  • Redis: la caché in-memory estándar (keys con TTL, estructuras, pub/sub).
  • MongoDB: el documento NoSQL más popular.
  • Elasticsearch: búsqueda y logs (ELK).
  • SQLite: la DB embebida (archivo local, sin servidor) — perfecta para aprendizaje y prototipos.

Terminología que debes dominar

Término En una frase
DBMS Sistema gestor de base de datos (el motor)
Query Una consulta
Query planner El que decide cómo ejecutar tu SELECT (usa índices o no)
EXPLAIN Ver el plan de ejecución de una consulta
Índice Estructura auxiliar (B+tree) para buscar rápido
Full table scan Recorrer toda la tabla (lento)
Transacción Unidad de operaciones todo-o-nada
Commit / Rollback Confirmar / deshacer
ACID Atomicidad, Consistencia, Aislamiento, Durabilidad
Normalización Eliminar duplicados y anomalías
JOIN Combinar tablas por relación
Agregación COUNT, SUM, AVG, GROUP BY
Clave primaria / foránea ID de la fila / referencia a otra tabla
Modelo (ORM) Capa que traduce objetos ↔ SQL

Práctica propuesta

  1. Instala SQLite (viene con Python: import sqlite3). Crea una tabla usuarios y haz CRUD completo.
  2. Modela una relación muchos-a-muchos (usuarios ↔ cursos) con una tabla puente. Haz JOINs.
  3. Crea 2 tablas con 1.000.000 de filas (genera con un bucle). Ejecuta una búsqueda sin índice y otra con índice. Compara tiempos con EXPLAIN QUERY PLAN.
  4. Haz una transferencia bancaria con transacciones (BEGIN/COMMIT/ROLLBACK). Rompe el COMMIT a mitad y verifica que el rollback funciona.
  5. Simula una carrera de datos: dos clientes incrementan un contador a la vez. Compara con/sin transacción.
  6. Aprende a leer EXPLAIN ANALYZE de una consulta: ¿usa índice o scan?

Para profundizar

Estudio · Recursos de todo el mundo (inglés, chino, japonés, español, francés, ruso…) curados y traducidos al español.