Rendimiento y optimización de PostgreSQL | Nicolás Garzón
Optimizar PostgreSQL es reducir el recurso dominante para un workload concreto sin trasladar un coste mayor a escrituras, memoria, operación o consistencia.
Texto
Copiar medir impacto
→ localizar espera/recurso
→ formular hipótesis
→ cambiar una variable
→ comparar
→ conservar o revertirNo empieza con “añadir un índice” ni con copiar parámetros de Internet.
P95/P99 de endpoint.
Tiempo total consumido por query.
Throughput.
Coste de infraestructura.
RPO/RTO.
Ventana de batch.
Una query de 500 ms ejecutada una vez al día puede importar menos que una de 5 ms ejecutada un millón de veces.
Texto
Copiar queue del pool
+ red
+ parse/plan
+ lock wait
+ ejecución CPU/I/O
+ transferencia
+ mapping/serializaciónAsegúrate de optimizar la capa correcta.
Eliminar N+1 y viajes innecesarios.
Reducir filas/columnas.
Corregir cardinalidad y predicates.
Añadir índice específico.
Mejorar estadísticas.
Reducir locks/transacciones.
Cambiar modelo/read model.
Ajustar configuración con evidencia.
Escalar hardware/arquitectura.
Filtros sargables.
Orden total y LIMIT.
Preagregar antes de joins N×M.
EXISTS para existencia.
Batch/COPY para volumen.
Evitar funciones costosas por fila.
Seleccionar columnas necesarias.
Parametrizar sin SQL universal lleno de OR cuando produce planes pobres.
Un índice correcto conecta:
Texto
Copiar predicados de igualdad
→ rangos/orden
→ columnas INCLUDE necesarias
→ selectividad y frecuencia
Storage.
WAL.
Cache.
INSERT/UPDATE/DELETE.
Vacuum/reindex.
Menos HOT updates.
Elimina redundancia solo después de estudiar uniqueness, operator class, prefix e infrequent workloads.
Si estimates difieren de actual:
ANALYZE.
Mayor statistics target en columnas concretas.
Extended stats para dependencias/MCV/ndistinct.
Tipos y casts correctos.
Parámetros/skew.
No fuerces join methods como solución permanente antes de corregir la estimación.
Shared reads altos.
Temp reads/writes.
Cache hit no explica todo.
Storage latency.
Checkpoint spikes.
Menos páginas por query.
Índices/partition pruning.
Más RAM efectiva.
Storage mejor.
Menos bloat.
Work_mem local para spills específicos.
Expresiones/regex/JSON costosos.
Hash/sort grandes.
Demasiadas conexiones activas.
Compresión/serialización.
Plans repetidos.
JIT no beneficioso para queries cortas.
Optimiza trabajo antes de aumentar CPU.
Una query puede ser rápida sola y lenta bajo carga. Reduce:
Duración transaccional.
Filas bloqueadas.
Orden inconsistente.
DDL en horas críticas.
Side effects externos.
Usa constraints y statements atómicos para evitar locks manuales amplios.
Tables/indexes grandes aumentan I/O. Diagnostica:
Update pattern.
Long transactions.
Autovacuum thresholds.
Fillfactor/HOT.
Index count.
No conviertas VACUUM FULL o REINDEX en cron sin corregir la causa.
Aceleran páginas reutilizadas, no queries mal diseñadas.
Útil para resultados costosos con invalidación y staleness definidos.
Key.
TTL/invalidation.
Consistency contract.
Stampede control.
Fallback.
Escalan lecturas que toleran lag. No arreglan queries individuales caras ni reducen write load. Añaden read-after-write, routing y failover complexity.
Aporta cuando queries y lifecycle se alinean con key. No es sustituto de índices. Muchas partitions pueden aumentar planning time.
Más conexiones no significa más throughput. Limita pool, aplica backpressure y mide active queries. CPU saturation con cientos de backends puede mejorar reduciendo concurrencia.
OLTP.
Reports.
Backfills.
Maintenance.
Mediante replicas, queues, resource scheduling, timeouts o ventanas. Un reporte sin límite puede degradar checkout.
Un benchmark válido incluye:
Dataset y distribución realistas.
Concurrencia.
Warm/cold cache definido.
Percentiles.
Duración suficiente.
Errores.
WAL/CPU/I/O.
Mismo entorno.
EXPLAIN ANALYZE de una ejecución no es benchmark de sistema.
Feature flag/query toggle.
Índice concurrente.
Canario.
Métricas antes/después.
Rollback.
Ventana de observación.
Registrar plan/config version.
Subir work_mem global puede multiplicarse por nodos y conexiones:
Texto
Copiar work_mem × sorts/hashes por query × queries concurrentesPuede causar OOM. Usa SET LOCAL en operaciones conocidas y dimensiona con concurrencia.
Plan bueno para tenant pequeño y malo para gigante.
Cache caliente oculta I/O.
Índice mejora read y degrada ingest.
Optimize average empeora P99.
Replica escala lectura pero lag viola contrato.
JIT añade overhead.
Cambio de versión altera planner.
Optimizar sin baseline.
Crear índices por cada query.
Culpar a PostgreSQL por pool/N+1.
Medir solo promedio.
Cambiar muchas variables juntas.
Benchmark con datos vacíos.
Escalar hardware antes de reducir trabajo.
Operación y SLO.
Frecuencia/tiempo total.
Pool/waits.
Plan con datos reales.
Estimates.
Buffers/temp/WAL.
Locks.
Cambio mínimo.
Impacto en writes/operación.
Rollback y validación.
¿Por qué una query rápida puede ser prioridad alta?
¿Qué riesgo tiene un índice extra?
¿Por qué work_mem global es peligroso?
¿Qué diferencia hay entre benchmark y EXPLAIN?
¿Cuándo una replica ayuda?
Ver respuestas
Por frecuencia y tiempo total acumulado.
Aumenta storage, WAL y coste de writes/vacuum.
Se multiplica por nodos y concurrencia.
EXPLAIN analiza una ejecución; benchmark mide sistema bajo workload.
En lecturas distribuibles que toleran lag.
Memoria y configuración del servidor ajusta límites globales sin convertir concurrencia en agotamiento.