Módulo 2 · Bases de datos

Lección 09 — Índices y planes de ejecución

EXPLAIN, consultas lentas y el problema N+1: la lección que resuelve la disponibilidad de TicketFlow.

Publicada
En esta lección
  1. Ejercicio 1
  2. Ejercicio 2
  3. Ejercicio 3
  4. Ejercicio 4
  5. Ejercicio 5
  6. Resumen del profesor

Ejercicio 1

  1. Sin índice: Seq Scan con filtro, coste alto, y filas estimadas ≈ reales si las estadísticas están frescas.
  2. Con índice: Index Scan (o Bitmap Heap Scan + Index), coste cae un orden de magnitud; si la tabla está en caché, shared hit domina en BUFFERS.
  3. Con baja selectividad (status casi uniforme), el planificador puede preferir Seq Scan: leer el índice + saltar a la tabla por el 40% de las filas es más caro que leer todo una vez. El índice brilla con selectividad alta (pocos resultados).

Ejercicio 2

  1. Esperado: Index Scan en seat por (event_id,...), subplan con Index Scan en reservation_item por seat_id (la UNIQUE condicional crea índice), y un Sort top-N (quicksort en memoria si son pocos). Filas reales vs estimadas con % de desviación razonable.
  2. El planificador suele elegir planes equivalentes; NOT EXISTS gana en claridad y a veces en plan con anti-join. La conclusión honesta: mide, no adivines — y escribe la que se lee mejor.

Ejercicio 3

  1. ~51 queries (1 + 50). 2. Con select_related: 1 (JOIN). 3. Añadir event sin arreglar: 51 de nuevo (ahora por evento); con select_related("user", "event"): 1. 4. Inversas: sin prefetch = N+1; con prefetch_related("items") = 2 queries. Regla: select_related para FK hacia adelante; prefetch para inversas y M2M.

Ejercicio 4

  1. (event_id, state, starts_at) — o (state, starts_at) si siempre filtras state primero; como event_id es igualdad y starts_at rango/orden: igualdades primero, orden último.
  2. (gift_card_id, created_at) — igualdad + orden: el índice devuelve el ledger ya ordenado sin Sort.
  3. (user_id, created_at DESC) — igualdad + rango reciente.
  4. code UNIQUE — además de índice, garantiza que el cupón no se duplique (regla de negocio).
  5. (status, expires_at) — ya existe de la 00b; es LA consulta caliente: igualdad + rango. Si el job solo procesa PENDING, valora parcial: CREATE INDEX... WHERE status = 'PENDING_PAYMENT' (más pequeño aún).

Ejercicio 5

  1. Los INSERTs bajan de velocidad (menos en tablas con muchos índices: cada árbol se inserta también). Números típicos: 10-30% más lento con un índice inútil de texto largo.
  2. Regla propuesta: un índice se crea con la consulta que lo justifica en la mano (o el constraint de integridad). Sin consulta, sin índice — y con pg_stat_user_indexes se audita el uso (idx_scan = 0 → candidato a borrar).

Resumen del profesor

  • B-tree para igualdad/rango/orden; compuestos: igualdades primero, rango/orden después; los prefijos importan.
  • EXPLAIN ANALYZE: scan tipo, estimación vs realidad, Sort en memoria/disco, buffers.
  • El N+1 se mata con select_related (JOIN) y prefetch_related (2 queries); se detecta contando queries.
  • Cada índice es una hipótesis: se crea con la consulta en la mano, se audita y se borra si nadie lo usa.

Después de la corrección: Lección 10 — Transacciones.