PostgreSQL viene con configuración conservadora: los defaults asumen un servidor pequeño y una carga modesta. En máquinas modernas con decenas de GB de RAM, dejar los valores originales desperdicia recursos y degrada el rendimiento. Esta lección recorre los parámetros con mayor impacto y muestra cómo aplicarlos sin causar incidentes.
Memoria: los cuatro parámetros clave
La gestión de memoria en PostgreSQL se reparte entre cuatro parámetros principales que conviene calibrar conjuntamente:
-
shared_buffers: caché compartida de bloques de tabla e índice. Recomendación: 25 % de la RAM. Valor demasiado bajo provoca lecturas constantes a disco; valor demasiado alto deja sin memoria al sistema y awork_mem. -
effective_cache_size: estimación de la caché disponible (PostgreSQL + sistema operativo). No reserva memoria, solo informa al planificador. Recomendación: 50-75 % de la RAM. -
work_mem: memoria por operación de sort y hash. Por conexión, no global. Cada query puede usar varioswork_memsi tiene varios sorts. Recomendación inicial: 4 a 16 MB. -
maintenance_work_mem: memoria para VACUUM, CREATE INDEX, REINDEX. Recomendación: 5-10 % de la RAM, hasta 1-2 GB.
ALTER SYSTEM SET shared_buffers = '8GB';
ALTER SYSTEM SET effective_cache_size = '24GB';
ALTER SYSTEM SET work_mem = '16MB';
ALTER SYSTEM SET maintenance_work_mem = '1GB';
SELECT pg_reload_conf();
shared_buffersrequiere reinicio. Los demás se aplican conpg_reload_conf(). Si arrancas un cluster nuevo, programa los valores antes de conectar usuarios.
Cálculo de work_mem por conexión
El cálculo correcto de work_mem depende del número de conexiones simultáneas. Si tu servidor tiene 100 conexiones activas y cada una puede consumir hasta 4 sorts de work_mem, el peor caso es:
work_mem * 4 sorts/query * 100 conexiones = consumo maximo
Con work_mem = 16MB, el peor caso son 6.4 GB. Si el servidor tiene 32 GB y shared_buffers = 8GB, queda margen suficiente. Si subes work_mem = 256MB con esas mismas conexiones, el peor caso son 102 GB y el OOM killer se llevará el postmaster.
En entornos con muchas conexiones, conectar mediante PgBouncer o similar reduce drásticamente la presión sobre
work_mem. Una pool de 50 conexiones reales con 1000 conexiones de cliente es preferible a 1000 conexiones reales.
Detectar derrames a disco
Cuando una operación supera work_mem, PostgreSQL derrama a disco temporal. Esto puede ralentizar la query 10-100 veces. Detectarlos es esencial:
-- Activar log de archivos temporales
ALTER SYSTEM SET log_temp_files = '0';
SELECT pg_reload_conf();
Con log_temp_files = 0, cualquier archivo temporal se registra en logs. También se puede ver en EXPLAIN ANALYZE:
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM pedidos ORDER BY total DESC;
-- Sort Method: external merge Disk: 50000kB
-- ^^^ derrame a disco, work_mem insuficiente
Si esa query se ejecuta a menudo, conviene subir work_mem solo para esa sesión:
SET work_mem = '256MB';
SELECT * FROM pedidos ORDER BY total DESC;
RESET work_mem;
Paralelismo
PostgreSQL puede paralelizar sequential scans, joins, agregaciones y CREATE INDEX. Los parámetros principales:
ALTER SYSTEM SET max_worker_processes = 16; -- limite global
ALTER SYSTEM SET max_parallel_workers = 8; -- para queries
ALTER SYSTEM SET max_parallel_workers_per_gather = 4; -- por query
ALTER SYSTEM SET max_parallel_maintenance_workers = 4; -- CREATE INDEX
max_parallel_workers_per_gather es el límite por query: una query analítica puede usar 4 workers + el proceso principal = 5 procesos. En un servidor con 16 vCPUs y 50 conexiones, 4 workers por gather es razonable.
El paralelismo solo se activa si el coste estimado de la query supera
min_parallel_table_scan_size(8MB por defecto) yparallel_setup_cost(1000) másparallel_tuple_cost(0.1) por tupla.
JIT compilation
PostgreSQL 11+ incorpora JIT (Just-In-Time compilation) basado en LLVM. Compila expresiones complejas a código nativo, acelerando queries analíticas pesadas. Activado por defecto desde la versión 12:
SHOW jit; -- on por defecto
SHOW jit_above_cost; -- 100000 por defecto
SHOW jit_inline_above_cost; -- 500000
SHOW jit_optimize_above_cost; -- 500000
JIT añade overhead inicial. Para queries OLTP rápidas (< 100 ms) conviene desactivarlo:
ALTER SYSTEM SET jit = 'off';
Para entornos analíticos (data warehouses, reporting), JIT acelera consultas largas. Medir antes de cambiar.
Checkpoints y WAL
Los checkpoints sincronizan los buffers modificados a disco. Frecuencia y tamaño afectan al rendimiento:
ALTER SYSTEM SET checkpoint_timeout = '15min';
ALTER SYSTEM SET max_wal_size = '8GB';
ALTER SYSTEM SET min_wal_size = '2GB';
ALTER SYSTEM SET checkpoint_completion_target = 0.9;
ALTER SYSTEM SET wal_compression = 'on';
Checkpoints muy frecuentes saturan el disco con escrituras. Checkpoints muy espaciados generan muchos WAL pendientes, retrasan recovery y aumentan el riesgo de pérdida tras crash.
wal_compression = onreduce el tamaño del WAL en 2-3x con un coste de CPU mínimo. Suele compensar siempre.
Logging útil para tuning
Logs bien configurados son la base del tuning empírico:
ALTER SYSTEM SET log_min_duration_statement = '500ms'; -- queries > 500ms
ALTER SYSTEM SET log_checkpoints = 'on';
ALTER SYSTEM SET log_connections = 'on';
ALTER SYSTEM SET log_disconnections = 'on';
ALTER SYSTEM SET log_lock_waits = 'on';
ALTER SYSTEM SET log_temp_files = '0';
ALTER SYSTEM SET log_autovacuum_min_duration = '1s';
Combinado con pg_stat_statements, ofrece una imagen completa del comportamiento real.
Validación con pgbench
pgbench es la herramienta oficial para benchmarking. Antes y después de cualquier cambio importante, mide el efecto:
# Inicializar dataset escalado (factor 100 = ~1.5 GB)
pgbench -i -s 100 -d produccion
# Test de 5 minutos con 10 clientes y 4 threads
pgbench -c 10 -j 4 -T 300 -P 30 -d produccion
# transaction type: <builtin: TPC-B (sort of)>
# scaling factor: 100
# number of clients: 10
# number of threads: 4
# duration: 300 s
# tps = 4523.123456 (excluding connections establishing)
Compara TPS antes y después del cambio. Las mejoras reales suelen ser del 10-30 % por iteración; cambios "espectaculares" suelen indicar que el original estaba muy mal configurado.
Generadores de configuración
Hay herramientas que sugieren valores razonables a partir de RAM y tipo de carga. PGTune y pgconfig.org son las más usadas. Generan recomendaciones para:
- Tipo de carga: OLTP, Data Warehouse, Mixto, Desktop.
- Tamaño total de RAM.
- Conexiones máximas.
- Número de CPUs.
- Tipo de almacenamiento (HDD o SSD).
Estas herramientas son un buen punto de partida. El tuning fino requiere medir con tu carga real y ajustar iterativamente. Nunca apliques sus sugerencias sin reiniciar en horario controlado.
Connection pooling
Es uno de los ajustes con mayor impacto y a menudo se descuida. Cada conexión PostgreSQL consume memoria del proceso backend (típicamente 5-15 MB) más work_mem cuando ejecuta queries. Mil conexiones cuesta varios GB solo de overhead.
PgBouncer o pgcat mantienen un pool reducido (50-100 conexiones reales) y multiplexan miles de conexiones de cliente. Beneficios:
- Reduce memoria total.
- Permite subir
work_memcon seguridad. - Acelera la apertura percibida de conexión (no hay fork).
- Aísla picos de carga.
# pgbouncer.ini
[databases]
produccion = host=localhost dbname=produccion
[pgbouncer]
pool_mode = transaction
default_pool_size = 50
reserve_pool_size = 10
max_client_conn = 1000
Lista de chequeo antes de subir a producción
Antes de declarar un cluster listo para producción, verifica:
shared_buffersentre 15 y 25 % de RAM.work_memcalculado para tu número máximo de conexiones reales.effective_cache_sizeestimado correctamente.max_wal_sizesuficiente para evitar checkpoints muy frecuentes bajo carga.autovacuumhabilitado y con thresholds ajustados por tabla caliente.- PgBouncer configurado si la aplicación abre muchas conexiones.
log_min_duration_statementactivo para detectar slow queries.pg_stat_statementsinstalado y consultado regularmente.shared_preload_librariesincluyendo extensiones necesarias (auto_explain, pg_stat_statements, timescaledb si aplica).
flowchart LR
A[RAM 32GB] --> B[shared_buffers 8GB]
A --> C[work_mem 16MB x conn]
A --> D[effective_cache_size 24GB]
A --> E[maintenance_work_mem 1GB]
F[100 conn maxima] --> G[PgBouncer pool 50]
H[Carga real] --> I[pgbench y pg_stat_statements]
I --> J[Iteracion ajustes]
El tuning no es magia. Es el resultado de medir, cambiar un parámetro, medir de nuevo y documentar. Los valores que funcionan en tu cluster pueden ser distintos de los del cluster vecino. Lo único universal es la metodología.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, SQL es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de SQL
Explora más contenido relacionado con SQL y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Calcular shared_buffers, work_mem y effective_cache_size apropiados para un servidor. Aplicar cambios con ALTER SYSTEM y pg_reload_conf vs reinicio. Habilitar y ajustar parallel queries y JIT compilation. Validar el efecto del tuning con pgbench y queries reales. Diagnosticar errores OOM y derrames a disco con temp_files.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje