En 09-01 vimos qué leer y en 09-02 dónde estudiar y practicar. Falta el tercer vértice, y es el que convierte lo demás en trabajo real: con qué. Esta lección es el inventario de herramientas con las que se trabaja de verdad con bases de datos —motores en tu propia máquina, clientes, diseño, migraciones, datos de prueba, diagnóstico y copias— y, sobre todo, el criterio de cuáles necesitas ya y cuáles no necesitas todavía.

Porque el error dominante en esta materia no es usar pocas herramientas, es acumularlas. Es fácil terminar con tres clientes gráficos instalados, dos herramientas de diagramas, un gestor de migraciones que no se usa y ninguna copia de seguridad configurada. El apartado final propone un kit mínimo de cinco piezas con el que se puede empezar mañana, y una lista explícita de lo que puedes ignorar sin remordimiento.

Todo lo que hay aquí está orientado a que puedas hacer lo que la última lección del módulo 8 te pedía: coger uno de los casos de VallBici o el esquema de BiblioRed, montarlo en tu máquina y romperlo.

Advertencia importante. Las versiones de los motores y de las herramientas avanzan constantemente; las opciones de línea de órdenes cambian; y las licencias y los modelos de negocio de las herramientas también cambian —hay proyectos que han pasado de libres a comerciales y al revés—. Los comandos de esta lección son correctos en su forma general, pero comprueba siempre la sintaxis y los nombres de paquete en la documentación de tu versión. Y la regla que gobierna todo lo demás: la documentación oficial manda sobre lo que diga cualquier curso, incluido este. Tampoco se cita ningún precio ni ninguna condición de plan gratuito, porque cambian sin aviso: verifica en la fuente oficial antes de contratar nada.

Contenido

  1. Motores en tu máquina: instalación y arranque
  2. Docker y Docker Compose: el entorno de 08-03 en un comando
  3. Bases de datos gestionadas en la nube
  4. Clientes de consola: psql y mongosh
  5. Clientes gráficos
  6. Diseño y diagramas
  7. Migraciones y control de versiones del esquema
  8. Datos de prueba
  9. Rendimiento y diagnóstico
  10. Copias de seguridad, monitorización y administración
  11. El kit mínimo
  12. Errores Comunes y Consejos
  13. Ejercicios
  14. Conclusión: el cierre del curso

  1. Motores en tu máquina: instalación y arranque

Tener el motor instalado localmente no es negociable. Puedes aprender mucho contra una base de datos gestionada, pero no puedes apagarla a mitad de una transacción para ver qué pasa, y ese tipo de experimento es exactamente lo que te falta.

Motor Cuándo instalarlo Peso Da continuidad a
PostgreSQL Siempre. Es el motor de referencia del curso y el que resuelve el 90 % de los casos Medio Todo el curso
SQLite Siempre; ya lo tienes casi seguro. Para pruebas rápidas, prototipos y análisis de ficheros Nulo 01-02, 01-04
MongoDB Si vas a trabajar con documentos o quieres rehacer 08-02 Medio 03-03, 08-02
Redis Si vas a rehacer 08-03 o a trabajar con caché y datos efímeros Bajo 03-02, 08-03
Elasticsearch Solo si vas a rehacer la parte de búsqueda de 08-03. Es el más pesado Alto 08-03
Neo4j / Cassandra Solo por curiosidad o por necesidad concreta. No los instales "por si acaso" Alto 03-02

PostgreSQL

# Debian / Ubuntu
sudo apt update
sudo apt install postgresql postgresql-contrib

# macOS con Homebrew
brew install postgresql@16
brew services start postgresql@16

# Comprobar que el servicio está vivo (Linux con systemd)
sudo systemctl status postgresql
sudo systemctl enable --now postgresql

Tras la instalación en Linux existe el usuario del sistema postgres, que es el superusuario del motor. El primer paso razonable es crear tu propio rol y tu propia base de datos, en vez de trabajar siempre como superusuario —justo lo que se argumentó en 06-04:

# Entrar como superusuario del motor
sudo -u postgres psql

# Dentro de psql: crear rol y base de datos para el proyecto
CREATE ROLE bibliored_app WITH LOGIN PASSWORD 'cámbiala';
CREATE DATABASE bibliored OWNER bibliored_app;
\q
# Conectarse ya con el rol de aplicación
psql -h localhost -U bibliored_app -d bibliored

El paquete postgresql-contrib merece una nota: trae extensiones que vas a querer, entre ellas pg_stat_statements (apartado 9), pg_trgm (búsqueda por similitud, la que se comparó con Elasticsearch en 08-03) y btree_gist (necesaria para las restricciones de exclusión sobre rangos).

SQLite

# Debian / Ubuntu
sudo apt install sqlite3

# macOS: viene con el sistema; para la versión más reciente
brew install sqlite

# Uso: la base de datos es un fichero, no hay servidor ni servicio
sqlite3 pruebas.db
-- Dentro de sqlite3
.databases
.tables
.schema prestamos
.mode box          -- salida legible en columnas
.headers on
.quit

SQLite es la herramienta más infravalorada del inventario. Para probar una idea de esquema, para analizar un CSV con SQL o para llevar datos de ejemplo en un repositorio, no hay nada más rápido. Recuerda lo de 01-02: su tipado es dinámico y su modelo de concurrencia es de un único escritor, así que no es un sustituto de PostgreSQL para una aplicación con varios usuarios escribiendo a la vez.

MongoDB y Redis

# MongoDB: los paquetes oficiales se instalan añadiendo el repositorio
# del fabricante; consulta la guía de instalación de tu sistema en
# https://www.mongodb.com/docs/
sudo systemctl enable --now mongod
mongosh

# Redis
sudo apt install redis-server        # Debian / Ubuntu
brew install redis                   # macOS
sudo systemctl enable --now redis-server
redis-cli ping                       # debe responder PONG

Para MongoDB y Redis, y aún más para Elasticsearch, la vía recomendable en una máquina de trabajo no es instalarlos como servicios del sistema, sino levantarlos con contenedores. Es lo que viene a continuación.

  1. Docker y Docker Compose: el entorno de 08-03 en un comando

Instalar cuatro motores como servicios del sistema significa cuatro servicios arrancando siempre, cuatro configuraciones que mantener y una desinstalación penosa el día que quieras limpiar. Con contenedores, todo el entorno es un fichero de texto que puedes versionar, compartir y destruir sin dejar rastro.

# Levantar un PostgreSQL suelto en 30 segundos
docker run --name pg-pruebas \
  -e POSTGRES_PASSWORD=secreto \
  -p 5432:5432 \
  -d postgres:16

# Conectarse desde el propio contenedor
docker exec -it pg-pruebas psql -U postgres

# Destruirlo sin dejar rastro
docker rm -f pg-pruebas

Y este es el fichero que levanta el entorno del caso políglota de 08-03: PostgreSQL como fuente de la verdad transaccional, MongoDB para telemetría y fichas, y Redis para la disponibilidad en tiempo real.

# docker-compose.yml — entorno del caso políglota de VallBici (08-03)
# Uso:  docker compose up -d      /  docker compose down -v  (borra los datos)
services:

  # --- PostgreSQL: fuente de la verdad del núcleo transaccional -------------
  postgres:
    image: postgres:16                 # fija la versión mayor; no uses "latest"
    container_name: vallbici-postgres
    environment:
      POSTGRES_USER: vallbici
      POSTGRES_PASSWORD: desarrollo    # solo para local; nunca en producción
      POSTGRES_DB: vallbici
    ports:
      - "5432:5432"                    # host:contenedor
    volumes:
      - pgdata:/var/lib/postgresql/data          # datos persistentes
      - ./sql:/docker-entrypoint-initdb.d:ro     # scripts .sql que se ejecutan
                                                 # solo la primera vez
    healthcheck:                       # para que otros servicios esperen
      test: ["CMD-SHELL", "pg_isready -U vallbici"]
      interval: 5s
      retries: 10

  # --- MongoDB: telemetría, fichas enriquecidas e incidencias ---------------
  mongo:
    image: mongo:7
    container_name: vallbici-mongo
    environment:
      MONGO_INITDB_ROOT_USERNAME: vallbici
      MONGO_INITDB_ROOT_PASSWORD: desarrollo
    ports:
      - "27017:27017"
    volumes:
      - mongodata:/data/db

  # --- Redis: disponibilidad en tiempo real, sesiones y reservas ------------
  redis:
    image: redis:7
    container_name: vallbici-redis
    command: ["redis-server", "--appendonly", "yes"]   # persistencia AOF
    ports:
      - "6379:6379"
    volumes:
      - redisdata:/data

volumes:
  pgdata:
  mongodata:
  redisdata:
docker compose up -d          # levantar todo
docker compose ps             # ver estado
docker compose logs -f postgres
docker compose down           # parar, conservando los volúmenes
docker compose down -v        # parar y BORRAR los datos

Cuatro detalles del fichero que merecen atención, porque son los que separan un compose de juguete de uno útil:

  1. Versión fijada (postgres:16, no postgres:latest). Con latest, un día actualizas la imagen sin querer y el formato de datos deja de ser compatible.
  2. Volúmenes con nombre. Sin ellos, docker compose down se lleva tus datos por delante. Con ellos, sobreviven hasta que pidas explícitamente -v.
  3. docker-entrypoint-initdb.d. Cualquier .sql que dejes en ./sql se ejecuta al crear la base de datos por primera vez. Es el sitio natural del esquema de BiblioRed o de VallBici: clonar el repositorio y docker compose up -d te deja el esquema creado.
  4. healthcheck. Permite que tu aplicación —o un contenedor de migraciones— espere a que PostgreSQL esté realmente aceptando conexiones, no solo arrancado.

Si además quieres Elasticsearch para reproducir la búsqueda de estaciones, añádelo como un servicio más; ten en cuenta que consume bastante más memoria que los otros tres juntos y que suele necesitar ajustes de memoria de la máquina anfitriona.

  1. Bases de datos gestionadas en la nube

Una base de datos gestionada es el mismo motor, operado por otro: copias de seguridad, actualizaciones, alta disponibilidad y monitorización vienen incluidas. Para aprender no las necesitas; para publicar un proyecto propio son muy cómodas.

Tipo de oferta Ejemplos consolidados Cuándo tiene sentido
PostgreSQL gestionado por los grandes proveedores Amazon RDS y Aurora, Google Cloud SQL, Azure Database for PostgreSQL Cuando ya trabajas en esa nube
PostgreSQL de proveedores especializados Neon, Supabase, Crunchy Bridge, entre otros Proyectos propios y prototipos; suelen tener capa gratuita
MongoDB gestionado MongoDB Atlas Rehacer 08-02 sin instalar nada; incluye conjuntos de datos de ejemplo
Redis gestionado Redis Cloud y equivalentes de cada nube Caché en un proyecto publicado
Búsqueda gestionada Elastic Cloud y equivalentes Cuando Elasticsearch local te resulta demasiado pesado

Sobre las capas gratuitas. Varias de estas ofertas tienen capa gratuita o crédito inicial, y son perfectamente adecuadas para un proyecto de aprendizaje publicado. Pero las condiciones —límites de almacenamiento, pausas por inactividad, caducidad— cambian con frecuencia, así que consúltalas en la web oficial del proveedor en el momento en que vayas a usarlas. Y dos cautelas prácticas: primera, activa siempre las alertas de gasto antes de crear nada, porque el modo de facturación por uso puede sorprenderte; segunda, una base de datos gestionada no te exime de saber administrar: si no entiendes qué es un VACUUM o qué implica el nivel de aislamiento, el panel bonito no te va a salvar.

  1. Clientes de consola: psql y mongosh

Empezamos por los clientes de consola y no por los gráficos deliberadamente. psql no es la opción básica: es la herramienta más potente de todas las que aparecen en esta lección. Todo lo que existe en un cliente gráfico existe en psql, y bastante de lo que hay en psql no existe en ningún cliente gráfico. Además está siempre disponible: en el servidor, dentro del contenedor, por SSH, en un script.

Metacomandos de psql que más se usan

Retoman lo que en 01-04 se llamó el catálogo del sistema: cada uno de estos metacomandos es, en realidad, una consulta al catálogo escrita por ti sin darte cuenta.

Metacomando Qué hace
\l Lista las bases de datos del servidor
\c bibliored Se conecta a otra base de datos
\dt Lista las tablas del esquema actual
\d prestamos Describe una tabla: columnas, tipos, índices, claves foráneas
\d+ prestamos Igual, con tamaño en disco, almacenamiento y descripciones
\di Lista los índices
\dn Lista los esquemas
\du Lista los roles y sus atributos (continuación directa de 06-04)
\df Lista las funciones
\sf nombre_funcion Muestra el código fuente de una función
\x Alterna la salida expandida (una columna por línea); imprescindible con tablas anchas
\timing Activa el tiempo de ejecución de cada consulta
\e Abre la última consulta en tu editor
\i fichero.sql Ejecuta un fichero SQL
\copy tabla FROM 'datos.csv' CSV HEADER Importa/exporta CSV desde el cliente (no requiere permisos de servidor)
\watch 2 Repite la última consulta cada 2 segundos; excelente para observar contadores
\? / \h CREATE INDEX Ayuda de metacomandos / ayuda de sintaxis SQL
\q Salir

Dos costumbres que valen mucho y cuestan poco: activar \timing siempre, para que cada consulta te diga lo que tarda; y usar \watch para observar en vivo un contador mientras otra sesión trabaja —es la forma más directa de ver los fenómenos de concurrencia de 06-02 sin ninguna herramienta adicional.

Un fichero ~/.psqlrc con tus preferencias hace el resto:

-- ~/.psqlrc
\set QUIET 1
\timing on
\x auto
\set HISTSIZE 10000
\set PROMPT1 '%[%033[1;32m%]%n@%/%[%033[0m%]%R%# '
\pset null '(null)'
\set QUIET 0

Ese \pset null '(null)' es más útil de lo que parece: por defecto un NULL y una cadena vacía se ven exactamente igual en la salida, y esa confusión ha costado muchas horas de depuración a mucha gente.

mongosh

// Metacomandos y operaciones habituales de mongosh
show dbs
use vallbici
show collections

db.trayectos.countDocuments({ estado: "cerrado" })
db.trayectos.findOne()
db.trayectos.find({ bicicleta_id: 417 }).sort({ inicio: -1 }).limit(5)

db.trayectos.getIndexes()
db.trayectos.createIndex({ bicicleta_id: 1, inicio: -1 })

// El equivalente de EXPLAIN: continuación directa de 06-03
db.trayectos.find({ bicicleta_id: 417 })
            .explain("executionStats")

db.stats()
db.trayectos.stats()

mongosh es un intérprete de JavaScript completo, no solo un cliente. Puedes escribir bucles, funciones y cargar ficheros con load('script.js'), lo que lo convierte en la herramienta natural para generar datos de prueba o para migraciones puntuales de documentos.

  1. Clientes gráficos

Un cliente gráfico aporta tres cosas reales: navegar un esquema desconocido mucho más rápido, ver los resultados en una rejilla cómoda y generar diagramas por ingeniería inversa. No aporta —y conviene no engañarse— ninguna capacidad que no tenga la consola.

Herramienta Motores soportados Licencia Punto fuerte Para quién
DBeaver Community (https://dbeaver.io/) Muchísimos, vía JDBC: PostgreSQL, MySQL, SQLite, Oracle, SQL Server, y NoSQL en la edición comercial Libre (edición Community) El navegador universal: un solo cliente para todo, con diagramas por ingeniería inversa Quien toca varios motores distintos
pgAdmin (https://www.pgadmin.org/) Solo PostgreSQL Libre Cobertura total de PostgreSQL, incluida administración: roles, copias, estadísticas Quien vive en PostgreSQL y hace administración
MongoDB Compass (desde https://www.mongodb.com/) Solo MongoDB Gratuito del fabricante Explorar documentos sin esquema conocido, construir canalizaciones de agregación visualmente y ver explain() gráfico Cualquiera que trabaje con MongoDB
TablePlus (https://tableplus.com/) Varios, relacionales y algunos NoSQL Comercial, con modo de prueba limitado Rapidez e interfaz muy cuidada Quien pasa el día en un cliente y valora la ergonomía
Beekeeper Studio (https://www.beekeeperstudio.io/) Varios relacionales Comunidad libre + edición comercial Alternativa ligera y sencilla Quien quiere algo simple y libre
DataGrip (JetBrains) Muchos Comercial Integración con el resto de herramientas de JetBrains, refactorización y autocompletado excelentes Quien ya usa ese ecosistema
Extensiones del editor de código (por ejemplo, extensiones de bases de datos para VS Code) Según extensión Variable No salir del editor para lanzar una consulta Quien solo necesita consultas rápidas junto al código

Recomendación honesta. Instala uno. Si tocas varios motores, DBeaver. Si solo PostgreSQL y haces administración, pgAdmin. Si trabajas con MongoDB, Compass además del anterior porque hace cosas que ningún otro hace. Tener tres clientes relacionales instalados es una señal casi infalible de que no se ha aprendido bien ninguno.

Y una advertencia que se repite en el tiempo: las licencias de las herramientas cambian. Proyectos que empezaron libres han pasado a modelos comerciales y algunos al revés. Comprueba la licencia vigente antes de apoyar un flujo de trabajo de equipo en una herramienta concreta.

  1. Diseño y diagramas

En 04-02 dibujaste diagramas entidad-relación y en 04-03 los transformaste en esquemas. Estas son las herramientas con las que eso se hace fuera de un curso.

Herramienta Enfoque Licencia / modelo Punto fuerte Para quién
Mermaid (https://mermaid.js.org/) Diagrama como texto, dentro del repositorio Libre El diagrama vive junto al código, se versiona y se ve en las plataformas de repositorios Todo el mundo; es lo que se ha usado en este curso
dbdiagram.io (https://dbdiagram.io/) Diagrama como texto en un lenguaje propio, en el navegador Comercial con nivel gratuito Rapidísimo para bocetar y exportar el SQL de creación Bocetos y discusiones de diseño
DrawSQL (https://drawsql.app/) Editor visual en el navegador Comercial con nivel gratuito Diagramas presentables para compartir con no técnicos Documentación de cara al equipo
pgModeler (https://pgmodeler.io/) Modelador de escritorio específico de PostgreSQL Código abierto; binarios de pago Modelado completo con generación y sincronización de esquema Modelado serio y continuado sobre PostgreSQL
SchemaSpy (https://schemaspy.org/) Ingeniería inversa: documentación HTML desde una base existente Libre Documentar un esquema heredado que nadie entiende Quien aterriza en un proyecto sin documentación
DBeaver / pgAdmin (diagramas incluidos) Ingeniería inversa integrada Ver apartado 5 Ver el diagrama de lo que ya existe sin instalar nada más Uso diario

El criterio. Para un diagrama que debe vivir en el tiempo —en el repositorio, revisado en cada cambio— usa Mermaid: es texto, se versiona y se lee en el README. Para un boceto de una tarde, dbdiagram.io. Para entender un esquema ajeno de sesenta tablas, ingeniería inversa con DBeaver o SchemaSpy y a partir de ahí decides.

Este es el erDiagram de Mermaid con el que puedes empezar a documentar tu propio esquema; es exactamente el formato que has visto durante todo el curso:

erDiagram
    SOCIO ||--o{ PRESTAMO : "realiza"
    EJEMPLAR ||--o{ PRESTAMO : "es objeto de"
    MATERIAL ||--o{ EJEMPLAR : "tiene"
    BIBLIOTECA ||--o{ EJEMPLAR : "custodia"
    PRESTAMO ||--o| MULTA : "puede generar"

    SOCIO {
        int socio_id PK
        text nombre
        text email UK
        date fecha_alta
        bool activo
    }
    MATERIAL {
        int material_id PK
        text titulo
        text isbn UK
        int anio_publicacion
    }
    EJEMPLAR {
        int ejemplar_id PK
        int material_id FK
        int biblioteca_id FK
        text estado
    }
    PRESTAMO {
        int prestamo_id PK
        int socio_id FK
        int ejemplar_id FK
        timestamptz fecha_prestamo
        date fecha_devolucion_prevista
        timestamptz fecha_devolucion_real
    }
    MULTA {
        int multa_id PK
        int prestamo_id FK
        numeric importe
        bool pagada
    }

Guarda ese bloque en el README.md de tu proyecto y actualízalo en la misma confirmación en la que cambies el esquema. Es la aplicación literal del "documentar y versionar el esquema" de 04-01, y cuesta dos minutos.

  1. Migraciones y control de versiones del esquema

Este apartado es el más importante de la lección, y el que más gente se salta.

En 04-01 se dijo que el esquema hay que documentarlo y versionarlo. Una migración es la forma profesional de hacerlo: cada cambio del esquema es un fichero, numerado y guardado en el repositorio junto al código, que se aplica en orden y del que la base de datos lleva registro. La consecuencia es que el esquema deja de ser el resultado de una serie de ALTER TABLE sueltos en la consola de alguien y pasa a ser una secuencia reproducible.

Por qué esto no es opcional. Cuatro razones, todas comprobables el primer día que trabajas en equipo:

  1. Reproducibilidad. Cualquiera clona el repositorio, ejecuta las migraciones y obtiene exactamente tu esquema. Sin migraciones, "montar el entorno" es preguntar a un compañero.
  2. El esquema y el código viajan juntos. La confirmación que añade la columna fecha_devolucion_real contiene también el código que la usa. Volver atrás una versión revierte las dos cosas.
  3. Repetibilidad entre entornos. Lo que se aplicó en desarrollo se aplica idéntico en preproducción y en producción, sin que nadie escriba nada a mano bajo presión.
  4. Historia y auditoría. «¿Cuándo se añadió este índice y por qué?» tiene respuesta: la confirmación, su fecha y su mensaje.
Herramienta Formato Ecosistema Punto fuerte
Flyway (https://flywaydb.org/) SQL puro numerado (V1__...sql) JVM, pero usable desde línea de órdenes con cualquier lenguaje Simplicidad: son ficheros .sql y se ejecutan en orden
Liquibase (https://www.liquibase.org/) XML, YAML, JSON o SQL JVM y línea de órdenes Cambios descritos de forma abstracta, con reversión automática
Alembic Python SQLAlchemy Genera migraciones a partir de la diferencia con los modelos
Migraciones integradas en marcos de trabajo Según el marco Django, Rails, Laravel, Entity Framework, Prisma, Ecto… Ya las tienes: úsalas y no añadas otra herramienta
Herramientas ligeras (golang-migrate, dbmate, sqitch…) SQL puro Agnósticas Un binario y ficheros SQL, sin dependencias

Cómo elegir. Si tu marco de trabajo ya trae migraciones, usa esas y punto. Si no usas marco, o quieres SQL explícito y controlado, Flyway o una herramienta ligera equivalente. Liquibase compensa en entornos con varios motores distintos y necesidad de reversión formal.

Un ejemplo mínimo con el formato de ficheros SQL numerados, aplicado a BiblioRed:

-- V3__prestamos_indice_socio_fecha.sql
--
-- Contexto: la consulta del historial de préstamos de un socio hacía
-- recorrido secuencial sobre 500.000 filas (ver EXPLAIN en el ticket #142).
-- El orden de las columnas no es arbitrario: socio_id es el filtro de
-- igualdad y va primero; fecha_prestamo da el orden y va después.
-- Ver lección 06-03 y «SQL Performance Explained».

CREATE INDEX CONCURRENTLY idx_prestamos_socio_fecha
    ON prestamos (socio_id, fecha_prestamo DESC);
-- V4__reservas_sala_sin_solapes.sql
--
-- Sustituye la comprobación de solapamiento que estaba en la aplicación
-- por una restricción del motor. Motivo: bajo concurrencia la comprobación
-- en la aplicación falla (lección 06-02, escritura sesgada).

CREATE EXTENSION IF NOT EXISTS btree_gist;

ALTER TABLE reservas_sala
    ADD COLUMN periodo tstzrange;

UPDATE reservas_sala
   SET periodo = tstzrange(inicio, fin, '[)');

ALTER TABLE reservas_sala
    ALTER COLUMN periodo SET NOT NULL,
    ADD CONSTRAINT reservas_sala_sin_solapes
        EXCLUDE USING gist (sala_id WITH =, periodo WITH &&);

Tres reglas de oro sobre migraciones, aprendidas todas por las malas:

  1. Una migración aplicada no se modifica jamás. Si estaba mal, se corrige con una migración nueva. Cambiar un fichero ya aplicado rompe la suma de verificación de la herramienta y desincroniza los entornos.
  2. Toda migración debe poder ejecutarse sobre datos reales. Añadir una columna NOT NULL sin valor por defecto a una tabla con dos millones de filas falla; hay que hacerlo en pasos.
  3. CREATE INDEX CONCURRENTLY en producción. Un CREATE INDEX normal bloquea las escrituras de la tabla mientras dura. Es el ejemplo perfecto de cómo lo que aprendiste en 06-02 sobre bloqueos se traduce en una decisión operativa concreta.

  1. Datos de prueba

Un esquema con doce filas no enseña nada. El planificador de consultas hará recorrido secuencial siempre, cualquier índice parecerá inútil y ningún problema de concurrencia se manifestará. Para aprender rendimiento necesitas volumen, y para eso hay tres caminos.

Camino 1: generate_series, sin instalar nada

Es la vía más rápida y no requiere ninguna herramienta externa. Genera medio millón de préstamos verosímiles para BiblioRed:

-- 500.000 préstamos repartidos entre 20.000 socios y 80.000 ejemplares,
-- a lo largo de los últimos tres años
INSERT INTO prestamos (socio_id, ejemplar_id, fecha_prestamo,
                       fecha_devolucion_prevista, fecha_devolucion_real)
SELECT
    1 + floor(random() * 20000)::int                        AS socio_id,
    1 + floor(random() * 80000)::int                        AS ejemplar_id,
    ts                                                      AS fecha_prestamo,
    (ts + interval '21 days')::date                         AS fecha_dev_prevista,
    CASE WHEN random() < 0.9                                -- 90 % devueltos
         THEN ts + (random() * interval '35 days')
         ELSE NULL
    END                                                     AS fecha_dev_real
FROM generate_series(
        now() - interval '3 years',
        now(),
        interval '3 minutes'
     ) AS ts;

-- Imprescindible después de una carga masiva: sin estadísticas frescas,
-- el planificador toma decisiones con información falsa
ANALYZE prestamos;

-- Comprobación
SELECT count(*), min(fecha_prestamo), max(fecha_prestamo) FROM prestamos;

Ese ANALYZE final no es un adorno. Es la causa número uno de "he creado el índice y sigue sin usarlo" en pruebas caseras: el planificador de 06-03 decide con estadísticas, y tras una carga masiva las estadísticas están obsoletas.

Una advertencia sobre el realismo: random() produce una distribución uniforme, y los datos reales nunca son uniformes. En BiblioRed unos pocos títulos concentran la mayoría de los préstamos, y esa asimetría es justo lo que hace interesantes los índices y los planes. Si quieres pruebas realistas, sesga la distribución a propósito.

Camino 2: generadores de datos ficticios

Herramienta Qué es Cuándo usarla
Faker (bibliotecas para Python, JavaScript, PHP, Ruby…) Genera nombres, direcciones, correos, fechas y textos verosímiles, con localización al español Cuando necesitas datos que parezcan reales para capturas o demostraciones
Mockaroo (https://mockaroo.com/) Generador en el navegador que exporta CSV, JSON o SQL Prototipos rápidos sin escribir código
pgbench (viene con PostgreSQL) Genera un esquema de prueba y ejecuta cargas concurrentes Medir el motor y observar concurrencia real

pgbench merece una mención aparte porque hace algo que los otros no: carga concurrente. Con pgbench -c 20 -T 60 tienes veinte sesiones escribiendo a la vez durante un minuto, que es la forma de ver de verdad lo que en 06-02 viste con dos terminales.

Camino 3: bases de datos de ejemplo públicas

A veces no quieres montar nada: quieres practicar consultas sobre un esquema no trivial que ya existe.

Base de datos de ejemplo Dominio Motor habitual Buena para
Pagila Alquiler de películas (versión PostgreSQL de Sakila) PostgreSQL El estándar de facto para practicar SQL en PostgreSQL; esquema rico y bien normalizado
Sakila Alquiler de películas MySQL Lo mismo, en el mundo MySQL
Chinook Tienda de música PostgreSQL, SQLite, y otros Muy portable; excelente con SQLite para practicar sin servidor
Northwind Distribuidora de alimentos Varios El clásico veterano; útil por la cantidad de ejercicios publicados que lo usan
Conjuntos de ejemplo de MongoDB Atlas Varios (restaurantes, vuelos, cine) MongoDB Practicar canalizaciones de agregación sin crear datos

Se encuentran buscando su nombre; suelen distribuirse como un fichero SQL que se carga con psql -f. Con Chinook en SQLite tienes práctica de consultas en menos de un minuto y sin instalar ningún servidor.

  1. Rendimiento y diagnóstico

Continuación directa de 06-03, donde leíste tu primer plan de ejecución.

Herramienta Motor Qué resuelve
EXPLAIN / EXPLAIN (ANALYZE, BUFFERS) PostgreSQL El plan estimado y el real, con tiempos y accesos a disco
Visualizadores de planes (por ejemplo https://explain.depesz.com/ y los visualizadores gráficos integrados en pgAdmin y DBeaver) PostgreSQL Convertir un plan de 200 líneas en algo legible, señalando dónde se va el tiempo
pg_stat_statements PostgreSQL La consulta más importante de todas: qué sentencias consumen más tiempo acumulado en tu servidor
auto_explain PostgreSQL Registrar automáticamente el plan de las consultas que superen un umbral
pgBadger PostgreSQL Análisis de los registros del servidor con informes de consultas lentas, errores y esperas
pg_stat_activity PostgreSQL Qué está ejecutando cada sesión ahora, y quién está bloqueando a quién
.explain("executionStats") MongoDB El equivalente de EXPLAIN ANALYZE: qué índice se usó y cuántos documentos se examinaron
Perfilador de base de datos de MongoDB MongoDB Registrar operaciones lentas para analizarlas después
mongostat / mongotop MongoDB Actividad en vivo del servidor

pg_stat_statements merece su ficha porque cambia la forma de trabajar. Sin ella, optimizas la consulta que alguien se ha quejado de que va lenta. Con ella, optimizas la que más tiempo total consume, que muchas veces es una consulta rápida ejecutada cien mil veces al día y de la que nadie se queja.

-- Activarla: requiere añadirla a shared_preload_libraries y reiniciar
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- Las diez consultas que más tiempo total consumen en el servidor
SELECT
    substring(query, 1, 80)          AS consulta,
    calls,
    round(total_exec_time::numeric, 1) AS ms_total,
    round(mean_exec_time::numeric, 2)  AS ms_media,
    rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
-- Y la consulta de diagnóstico en caliente: quién bloquea a quién
SELECT pid, state, wait_event_type, wait_event,
       now() - query_start AS duracion,
       substring(query, 1, 60) AS consulta
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY duracion DESC;

Esa segunda consulta es la que ejecutarás el día en que la aplicación "se ha quedado colgada". Casi siempre la respuesta está ahí: una transacción abierta desde hace veinte minutos que nadie ha confirmado, bloqueando al resto. Es 06-02 en la vida real.

  1. Copias de seguridad, monitorización y administración

Continuación de 06-04. Aquí solo hay una regla que importe: una copia de seguridad que no se ha restaurado nunca no es una copia de seguridad, es una esperanza.

Herramienta Para qué Nivel
pg_dump / pg_restore Copia lógica de una base de datos; portable entre versiones Imprescindible
pg_dumpall Incluye roles y objetos globales del clúster Imprescindible si administras
pg_basebackup Copia física del clúster completo Medio
pgBackRest (https://pgbackrest.org/) Copias físicas incrementales, retención, verificación y recuperación a un instante Producción
Barman Alternativa consolidada para lo mismo Producción
mongodump / mongorestore Copias lógicas de MongoDB Imprescindible con MongoDB
Exportadores de métricas + Prometheus + Grafana Paneles de conexiones, tamaño, consultas lentas, retraso de réplica Producción
pgwatch y equivalentes Monitorización específica de PostgreSQL con paneles listos Producción

Lo mínimo, que puedes tener funcionando esta tarde:

# Copia lógica comprimida y en formato personalizado (permite restaurar
# tablas sueltas, a diferencia del volcado en texto plano)
pg_dump -h localhost -U bibliored_app -d bibliored \
        -Fc -f bibliored_$(date +%F).dump

# Restaurar sobre una base de datos NUEVA: la prueba de que la copia sirve
createdb bibliored_prueba
pg_restore -d bibliored_prueba bibliored_2026-08-02.dump

# Comprobación mínima: ¿están las filas?
psql -d bibliored_prueba -c "SELECT count(*) FROM prestamos;"
# MongoDB
mongodump  --uri="mongodb://localhost:27017/vallbici" --out=./copia
mongorestore --uri="mongodb://localhost:27017/vallbici_prueba" ./copia/vallbici

Automatizarlo es un par de líneas más —una tarea programada que ejecute el volcado, lo comprima y lo copie a otro sitio— pero la parte que la gente omite no es la automatización, es la restauración de prueba. Pon en el calendario una prueba de restauración trimestral. Es la única forma de saber que existe la copia.

  1. El kit mínimo

Todo lo anterior es el inventario completo. Esto es lo que necesitas de verdad para empezar mañana.

# Herramienta Por qué es imprescindible
1 PostgreSQL local (nativo o en contenedor) Sin motor propio no hay experimentación posible
2 psql Es la herramienta más potente de la lista y está siempre disponible
3 Docker + un docker-compose.yml Entornos reproducibles, desechables y versionados
4 Un cliente gráfico (DBeaver o pgAdmin, uno) Navegar esquemas ajenos y ver resultados cómodamente
5 Una herramienta de migraciones (la de tu marco, o Flyway) El esquema versionado no es opcional
6 EXPLAIN + pg_stat_statements Sin medir, optimizar es adivinar

Y esto es lo que no necesitas todavía, dicho explícitamente para que no pierdas tiempo:

Prescindible por ahora Cuándo dejará de serlo
Un segundo y un tercer cliente gráfico Nunca
Elasticsearch, Neo4j o Cassandra instalados Cuando tengas un problema concreto que los pida
Herramienta de modelado de escritorio (pgModeler) Cuando modeles esquemas grandes de forma continuada
pgBackRest, Barman, Prometheus y Grafana Cuando operes una base de datos de la que dependa alguien
Suscripción a un cliente comercial Cuando el gratuito te estorbe de verdad, no antes
Herramientas de generación de datos con interfaz generate_series te cubre casi todo

El consejo que resume el apartado: no acumules herramientas antes de necesitarlas. Cada herramienta instalada tiene un coste de mantenimiento, de actualización y de atención. Una herramienta se adopta cuando tienes un problema que te duele y ella lo resuelve, no cuando la ves recomendada en una lista, incluida esta.

Errores Comunes y Consejos

Error 1: no tener un motor local. Trabajar solo contra una base de datos compartida o gestionada significa no poder experimentar: no puedes matar el proceso, llenar el disco, provocar un interbloqueo ni restaurar una copia encima. Todo lo interesante de este curso requiere una base de datos que puedas destruir.

Error 2: usar latest en las imágenes de contenedor. Un día actualizas y el formato de datos ya no es compatible con la versión anterior. Fija siempre la versión mayor.

Error 3: cambiar el esquema a mano en la consola. El ALTER TABLE que escribiste directamente en producción no está en ningún sitio: ni en el repositorio, ni en el entorno de tu compañero, ni en tu memoria dentro de tres semanas. Todo cambio de esquema, migración.

Error 4: probar el rendimiento con doscientas filas. Con ese volumen el recorrido secuencial siempre gana y no aprenderás nada sobre índices. Genera cientos de miles de filas y ejecuta ANALYZE.

Error 5: olvidar ANALYZE tras una carga masiva. Es la causa más frecuente de "el índice está creado pero no se usa". El planificador decide con estadísticas, y tras insertar medio millón de filas están obsoletas.

Error 6: confiar en una copia de seguridad no restaurada. Restaura sobre una base de datos nueva y cuenta las filas. Trimestralmente. Sin excepción.

Error 7: dejar transacciones abiertas en el cliente gráfico. Varios clientes trabajan en modo de transacción manual: abres una pestaña, ejecutas un UPDATE, te vas a comer, y el bloqueo se queda ahí parando a todo el mundo. Comprueba en qué modo está tu cliente y revisa pg_stat_activity cuando algo se atasque.

Error 8: instalar cuatro motores y no usar ninguno. El entusiasmo del módulo 3 lleva a instalar Cassandra y Neo4j "para probarlos". Instálalos cuando vayas a hacer algo concreto con ellos, y bórralos si en un mes no lo has hecho.

Consejo 1: versiona todo lo que rodea a la base de datos. El docker-compose.yml, las migraciones, el README con el erDiagram de Mermaid, los scripts de datos de prueba y el de copia de seguridad. Que clonar el repositorio y ejecutar dos comandos deje el entorno funcionando es un objetivo alcanzable y muy rentable.

Consejo 2: aprende psql antes que ningún cliente gráfico. El gráfico se aprende en veinte minutos cuando ya sabes lo que estás haciendo; al revés, el gráfico esconde lo que ocurre y frena el aprendizaje.

Consejo 3: ten siempre a mano un fichero de consultas de diagnóstico. Las de pg_stat_activity, pg_stat_statements, tamaño de tablas e índices y uso de índices. El día del incidente no es el momento de escribirlas.

Consejo 4: y de nuevo, la documentación oficial manda. Las versiones avanzan, las opciones cambian de nombre y los valores por defecto se revisan de una versión a otra. Ante cualquier duda entre lo que dice esta lección y lo que dice el manual de tu versión, el manual tiene razón.

Ejercicios

Estos ejercicios son de práctica real: se resuelven con la máquina encendida, no de memoria. No hay una única respuesta correcta; las soluciones son respuestas modelo razonadas y tu versión puede ser distinta y mejor.

Ejercicio 1: monta el entorno políglota y rómpelo

Levanta con Docker Compose el entorno de 08-03 (PostgreSQL + MongoDB + Redis), carga en PostgreSQL el esquema de VallBici o de BiblioRed y después:

  1. Comprueba que los tres motores responden desde su cliente de consola.
  2. Inserta al menos 300.000 filas en la tabla de trayectos o de préstamos con generate_series, y ejecuta ANALYZE.
  3. Rómpelo a propósito: para el contenedor de Redis con la aplicación en marcha y documenta qué deja de funcionar y qué sigue funcionando. Después, ejecuta docker compose down (sin -v) y vuelve a levantar: comprueba que los datos siguen ahí.
  4. Ejecuta docker compose down -v y vuelve a levantar: comprueba que no están, y explica por qué.
  5. Deja el resultado en un repositorio con docker-compose.yml, los .sql de inicialización y un README que permita a otra persona reproducirlo en menos de diez minutos.

Ejercicio 2: versiona tu esquema con migraciones

Coge el esquema de BiblioRed tal como lo dejaste en el módulo 5 y conviértelo en una secuencia de migraciones:

  1. Elige una herramienta y justifica la elección en dos líneas.
  2. Escribe V1 con el esquema base y, al menos, tres migraciones posteriores que representen cambios reales: un índice nuevo justificado por un EXPLAIN, una restricción de integridad que hoy está en la aplicación, y una columna nueva sobre una tabla que ya tiene datos.
  3. La migración de la columna nueva debe poder ejecutarse sobre las 300.000 filas del ejercicio 1 sin dejar la tabla bloqueada un tiempo inaceptable. Explica cómo lo consigues.
  4. Documenta en cada fichero por qué se hace el cambio, no solo qué se hace.
  5. Comprueba lo esencial: borra la base de datos, ejecuta las migraciones desde cero y verifica que obtienes el mismo esquema.

Ejercicio 3: elige tu kit y justifícalo

Sin instalar nada nuevo todavía, escribe tu kit personal de herramientas para los próximos seis meses. Para cada pieza:

  1. Qué herramienta eliges y para qué tarea concreta.
  2. Contra qué alternativa la elegiste y por qué —una razón real, no "es la más popular".
  3. Qué herramienta de la lección descartas explícitamente y en qué condición la reconsiderarías.
  4. Añade una prueba de fuego: describe la tarea concreta con la que verificarás en un mes que la elección fue acertada.

Soluciones

Respuestas modelo, no las únicas correctas. Lo que se evalúa es el razonamiento y que la máquina realmente haga lo que dices.

Solución 1 (respuesta modelo)

Puntos 1 y 2. Comprobación de los tres motores y carga de volumen:

docker compose up -d
docker compose exec postgres psql -U vallbici -d vallbici -c "SELECT version();"
docker compose exec mongo mongosh --quiet --eval "db.adminCommand({ping:1})"
docker compose exec redis redis-cli ping

La carga se hace con el generate_series del apartado 8, seguido siempre de ANALYZE. Verificación: EXPLAIN (ANALYZE) de una consulta filtrada por socio debe pasar de recorrido secuencial a búsqueda por índice después de crear el índice y ejecutar ANALYZE; si no cambia, casi seguro falta el ANALYZE.

Punto 3 — romperlo. Con docker compose stop redis:

Qué pasa Por qué
La pantalla de disponibilidad en tiempo real deja de actualizarse Redis es la fuente del contador rápido de bicis por estación
Las sesiones activas se pierden y hay que volver a autenticarse Las sesiones viven en Redis
El desbloqueo y el cobro de un trayecto siguen funcionando Se resuelven contra PostgreSQL, que es la fuente de la verdad
El histórico y las facturas siguen consultables Están en PostgreSQL

Esa tabla es la comprobación empírica de la tesis de 08-03: «que Redis pueda caerse sin impedir un solo cobro es la prueba de que el reparto está bien hecho». Si al parar Redis dejara de poderse cobrar, el reparto estaría mal y habría que revisarlo.

Puntos 4 y 5. docker compose down para los contenedores pero conserva los volúmenes con nombre (pgdata, mongodata, redisdata), así que al volver a levantar los datos están. down -v elimina esos volúmenes y, con ellos, los datos; además, al recrearse el volumen de PostgreSQL vacío, se vuelven a ejecutar los scripts de docker-entrypoint-initdb.d, que solo corren cuando el directorio de datos está sin inicializar. Entender esa asimetría es justo el objetivo del ejercicio: el contenedor es desechable, el volumen no.

Solución 2 (respuesta modelo)

1. Elección: ficheros SQL numerados con Flyway o una herramienta ligera equivalente. Razón: BiblioRed no usa ningún marco de trabajo con migraciones propias, el esquema se piensa en SQL y quiero que lo que se aplica sea exactamente lo que leo, sin capa de abstracción intermedia.

2 y 3. La migración delicada —añadir una columna NOT NULL a una tabla de 300.000 filas— se hace en tres pasos, no en uno:

-- V5__prestamos_canal_alta.sql
--
-- Añade el canal por el que se formalizó el préstamo (mostrador, web, app).
-- Se hace en tres pasos para no bloquear la tabla: añadir la columna como
-- nullable es una operación de metadatos y es instantánea; rellenar y
-- después imponer NOT NULL evita reescribir la tabla entera bajo bloqueo.

-- Paso 1: columna nullable (instantáneo, solo metadatos)
ALTER TABLE prestamos ADD COLUMN canal_alta text;

-- Paso 2: relleno por lotes (aquí, simplificado; en producción, por bloques
-- de N filas con confirmación intermedia para no crear una transacción larga)
UPDATE prestamos SET canal_alta = 'mostrador' WHERE canal_alta IS NULL;

-- Paso 3: ya sin filas nulas, imponer la restricción
ALTER TABLE prestamos
    ALTER COLUMN canal_alta SET NOT NULL,
    ADD CONSTRAINT prestamos_canal_alta_valido
        CHECK (canal_alta IN ('mostrador', 'web', 'app'));

El índice justificado por EXPLAIN es el V3 del apartado 7, con CONCURRENTLY y con el orden de columnas razonado. La restricción que sube de la aplicación al motor es el V4 de las reservas de salas sin solapes.

4 y 5. El comentario de cabecera de cada fichero explica el porqué —el ticket, el plan de ejecución que lo motivó, la lección del curso que lo fundamenta— porque dentro de un año el "qué" se lee en el SQL y el "por qué" no está en ninguna parte. La verificación final es la que da sentido a todo el ejercicio: dropdb bibliored && createdb bibliored, aplicar las migraciones desde cero y comparar el esquema resultante (pg_dump --schema-only) con el de la base de datos original. Si no coinciden, hay algún cambio que se hizo a mano y no está en ninguna migración: exactamente el problema que este apartado viene a resolver.

Solución 3 (respuesta modelo)

Pieza Elección Frente a Por qué Prueba de fuego a un mes
Motor local PostgreSQL 16 en contenedor Instalación nativa Puedo tener dos versiones a la vez y destruirlo sin residuos Levantar el entorno en una máquina nueva en menos de diez minutos
Cliente principal psql con .psqlrc propio Cliente gráfico Está en el servidor, en el contenedor y en el guion de despliegue Resolver un incidente sin abrir interfaz gráfica
Cliente gráfico DBeaver pgAdmin También abro SQLite y MongoDB, y DBeaver los cubre con un solo cliente Explorar un esquema ajeno de 40 tablas y entenderlo en una tarde
Migraciones Flyway con SQL puro Liquibase No necesito reversión automática ni varios motores; quiero SQL legible Reconstruir la base de datos desde cero y que el esquema coincida
Diagramas Mermaid en el README dbdiagram.io El diagrama debe versionarse con el esquema, no vivir en otra web Que el diagrama siga correcto tras tres cambios de esquema
Diagnóstico EXPLAIN + pg_stat_statements Herramienta comercial de monitorización Vienen con el motor y responden el 90 % de las preguntas Identificar la consulta que más tiempo total consume y mejorarla

Descartes explícitos. Elasticsearch y Neo4j: no los instalo hasta tener un requisito de búsqueda con tolerancia a erratas o de recorrido de grafos que PostgreSQL con pg_trgm o consultas recursivas no cubra. pgBackRest: no hasta que administre una base de datos de la que dependa alguien que no sea yo; mientras tanto, pg_dump automatizado y restaurado trimestralmente. Cliente comercial de pago: no hasta que DBeaver me estorbe de verdad.

Conclusión: el cierre del curso

Empecemos por lo pequeño y acabemos por lo grande.

Lo pequeño: de todo el inventario de esta lección, seis piezas bastan —PostgreSQL local, psql, Docker Compose, un cliente gráfico, una herramienta de migraciones y EXPLAIN con pg_stat_statements—. Con eso puedes diseñar, medir, versionar y recuperar, que es todo lo que hace falta para trabajar bien. Añade herramientas cuando un problema real te las pida, y no antes. Y recuerda las dos advertencias que gobiernan todo el módulo 9: versiones, licencias, precios y planes gratuitos cambian sin aviso, y la documentación oficial manda sobre cualquier curso, incluido este.

Y ahora lo grande, porque con esta lección se cierra el curso entero.

Han sido nueve módulos y treinta y seis lecciones. Merece la pena mirar atrás el recorrido completo, porque desde dentro no siempre se ve.

Empezaste en 01-01 con una pregunta que parecía ingenua: qué es una base de datos y por qué no basta con una hoja de cálculo. La respuesta ocupó el módulo 1 entero —tipos de bases de datos, cincuenta años de historia desde los sistemas jerárquicos hasta hoy, y la arquitectura por dentro de un gestor—. En el módulo 2 aprendiste el modelo relacional y SQL de verdad: no solo SELECT, sino uniones de varias tablas, agregación, subconsultas correlacionadas e integridad referencial, con la idea que lo vertebra todo: si la regla es de integridad, vive en la base de datos. El módulo 3 te sacó de ahí a propósito para enseñarte NoSQL, sus cuatro familias y su modelado, y sobre todo para que vieras el modelo relacional desde fuera y entendieras que es una elección y no una ley natural.

Los módulos 4 y 5 fueron los del oficio silencioso: diseñar. Diagramas entidad-relación, transformación a esquemas, tipos de datos y restricciones, dependencias funcionales, formas normales hasta la de Boyce-Codd, y —la parte que muchos cursos no cuentan— desnormalizar a propósito y saber justificar por qué. El módulo 6 puso el sistema bajo presión: transacciones y ACID, dos terminales abiertos viendo con tus propios ojos una actualización perdida, planes de ejecución leídos línea a línea, permisos y copias de seguridad. El módulo 7 fueron cuatro lecciones de ejercicios sin red. Y el módulo 8 te hizo construir de cero: BiblioRed en relacional, VallBici en documental, y finalmente cuatro motores repartiéndose un servicio municipal con una tabla que declaraba, campo a campo, quién manda sobre qué.

Qué sabes hacer ahora. Puedes sentarte delante de un dominio que no conoces, hacer las preguntas correctas, dibujar el modelo, transformarlo en tablas con sus tipos y sus restricciones, normalizarlo, decidir dónde conviene romper la normalización y defenderlo. Puedes escribir consultas multitabla con agregación sin buscar la sintaxis. Puedes leer un plan de ejecución, decidir un índice y justificar el orden de sus columnas. Puedes razonar qué nivel de aislamiento necesita una operación y qué anomalía estás aceptando. Puedes decidir si un problema pide un documento, una clave-valor, un grafo o una tabla, y —más importante— cuándo no lo pide. Y puedes montar el entorno, versionarlo y recuperarlo.

Eso no es poco. Es, con bastante precisión, lo que se espera de alguien que trabaja con datos con criterio.

Qué falta, dicho con honestidad: la experiencia. Ninguna lección da lo que da haber tenido una base de datos en producción con gente dependiendo de ella. Nadie entiende del todo por qué las copias se prueban hasta que ha necesitado una; nadie interioriza los niveles de aislamiento hasta que ha visto dos cobros duplicados un martes por la mañana. Eso llega con el tiempo, y este curso lo único que ha hecho —que ya es bastante— es prepararte para reconocerlo cuando pase, en lugar de sufrirlo sin entenderlo.

Así que la invitación final es la misma que cerraba 08-03, y ahora tienes todas las herramientas para aceptarla: elige uno de los casos —el BiblioRed relacional de los módulos 4 y 5, el VallBici documental de 08-02, o el políglota de 08-03—, móntalo en tu máquina con el docker-compose.yml de esta lección, cárgalo con medio millón de filas, y rómpelo. Mata el contenedor de Redis y mira qué sigue funcionando. Abre dos terminales y provoca un interbloqueo. Borra la base de datos entera y restáurala desde tu copia. Crea un índice que no sirva para nada y averigua con EXPLAIN por qué no sirve. Cada una de esas cosas te enseñará más que releer la lección correspondiente, porque el conocimiento que se queda es el que se paga con un error propio.

Gracias por llegar hasta aquí. Treinta y seis lecciones son muchas, y terminar un curso completo —no abandonarlo en el módulo 3, que es lo que le pasa a la mayoría de la gente con la mayoría de los cursos— dice algo real sobre cómo trabajas. Las bases de datos son una de las pocas áreas de la informática donde lo que aprendes envejece despacio: el artículo de Codd tiene más de cincuenta años y sigue siendo la base de lo que has estudiado, y las decisiones que ahora sabes tomar te seguirán sirviendo cuando cambien los lenguajes, los marcos de trabajo y las modas.

Ya no hay siguiente lección. Hay una base de datos vacía esperando a que decidas qué va dentro. Suerte con ella.

Fundamentos de Bases de Datos

Módulo 1: Introducción a las Bases de Datos

Módulo 2: Bases de Datos Relacionales

Módulo 3: Bases de Datos No Relacionales

Módulo 4: Diseño de Esquemas

Módulo 5: Normalización

Módulo 6: Transacciones, Rendimiento y Seguridad

Módulo 7: Ejercicios Prácticos

Módulo 8: Casos de Estudio

Módulo 9: Recursos Adicionales

© Copyright 2026. Todos los derechos reservados