Reglas de Índices y Consultas
Los estándares de consultas e índices evitan regresiones en producción cuando los ORM generan SQL que los revisores nunca ven hasta que la latencia p99 se dispara.
Receta
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT o.id, o.status, o.placed_at
FROM app.orders o
WHERE o.customer_id = $1
AND o.status = 'open'
ORDER BY o.placed_at DESC
LIMIT 50;Puerta de enlace: No usar bucle anidado sobre escaneo secuencial de un millón de filas para rutas de acceso de API. Requerir un plan amigable con índices o una excepción documentada en la PR.
Cuándo recurrir a esto: Revisión de consultas de API, políticas de acceso de BI y aprobación de SME para SQL generado por ORM.
Ejemplo de Trabajo
-- Consulta de respaldo del endpoint de lista de API
CREATE INDEX idx_orders_customer_status_placed
ON app.orders (customer_id, status, placed_at DESC)
WHERE status IN ('open', 'pending');
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, status, placed_at
FROM app.orders
WHERE customer_id = 42
AND status IN ('open', 'pending')
ORDER BY placed_at DESC
LIMIT 50;Lista de verificación de revisión para PR:
-
LIMITo rango de fechas acotado presente -
EXPLAINusa escaneo de índice o escaneo de índice de mapa de bits en tabla grande - No
SELECT *en tablas JSONB amplias en rutas de acceso críticas - Claves de unión indexadas en ambos lados para uniones frecuentes
- Línea base del tiempo medio de
pg_stat_statementsregistrada para la consulta modificada
Análisis Profundo
Reglas de un Vistazo
| Regla | Razón |
|---|---|
| EXPLAIN antes de fusionar | Detecta sorpresas del planificador pre-producción |
| Recuentos de filas acotados en APIs | Evita OOM y facturas descontroladas |
| Columnas de predicado de índice más a la izquierda | Coincidencia de prefijo Btree |
| Evitar funciones en columnas indexadas en WHERE | A menos que exista un índice de expresión |
| Revisar patrones ORM N+1 | Cientos de búsquedas de una sola fila |
Antipatrones de Consultas de API
-- MALO: ilimitado
SELECT * FROM app.events WHERE tenant_id = $1;
-- BUENO: paginación por cursor o keyset
SELECT id, created_at, type
FROM app.events
WHERE tenant_id = $1 AND created_at < $2
ORDER BY created_at DESC
LIMIT 100;Cuándo un Escaneo Secuencial Está Bien
- Tablas pequeñas (< unos pocos miles de filas) según la estimación de filas de
EXPLAIN - Trabajos por lotes analíticos fuera de la réplica con
statement_timeoutexplícito - Post-migración antes de
ANALYZE- temporal, no aceptado para API de producción
Disciplina de Índices
-- Un propósito claro por índice
CREATE INDEX idx_events_tenant_created
ON app.events (tenant_id, created_at DESC);- Eliminar índices redundantes
(tenant_id)cuando(tenant_id, created_at)existe - Índices parciales para colas filtradas por estado
Trampas Comunes
- ORM oculta LIMIT - El tamaño de página predeterminado es ilimitado en las vistas de administración. Solución: Tamaño máximo de página del lado del servidor de 100-500.
- LIKE '%foo' - El comodín inicial no puede usar btree. Solución: GIN trigrama o búsqueda de texto completo.
- Orden incorrecto de índice compuesto - Índice
(status, customer_id)pero la consulta filtracustomer_idprimero. Solución: Coincidir con el orden de selectividad del filtro. - Estadísticas obsoletas después de carga masiva - El planificador elige escaneo secuencial. Solución:
ANALYZEen el pipeline de migración. - Plan genérico de sentencia preparada - PG 12+ puede cambiar a un plan genérico subóptimo. Solución: Monitorear después del despliegue; ajustar
plan_cache_modesi es necesario.
Alternativas
| Alternativa | Usar Cuándo | No Usar Cuándo |
|---|---|---|
| Vista materializada | Patrón de lectura costoso y estable | Se requiere consistencia en tiempo real |
| Enrutamiento de réplica de lectura | Informes pesados | Necesidad de las últimas escrituras |
INCLUDE de índice de cobertura | Ganancia de escaneo solo con índice | Amplificación de escritura demasiado alta |
Preguntas Frecuentes
¿Es suficiente EXPLAIN sin ANALYZE?
Para consultas nuevas en tablas grandes, use ANALYZE en staging con un volumen de datos realista. BUFFERS muestra la amplificación de lectura.
¿Quién es responsable de la revisión de consultas?
Autor de la aplicación + DBA/SME en rutas de acceso críticas. Automatizar alertas de regresión de pg_stat_statements en equipos maduros.
¿Excepción de SQL crudo para ORM?
No, el SQL crudo todavía necesita las reglas de EXPLAIN y LIMIT.
Relacionado
- Rendimiento de RLS - índices de columnas de políticas
- Lista de Verificación de Reglas del Proyecto Postgres - reglas 6-10
- Mejores Prácticas del Cliente psql - hábitos del analista
Versiones de Stack: Esta página fue escrita para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, y PostGIS 3.5+.