Síntomas comunes

  • Consultas que antes terminaban en milisegundos ahora toman segundos o minutos
  • La aplicación arroja errores de timeout de conexión o "connection refused"
  • Los usuarios reportan pantallas de carga prolongadas o errores intermitentes
  • pg_stat_activity muestra un número inusualmente alto de conexiones en estado active
  • Jobs nocturnos de reportes no terminan dentro de su ventana programada
  • El servidor muestra uso elevado de CPU, I/O de disco o memoria swap activa
  • Los logs de PostgreSQL registran muchas consultas lentas (log_min_duration_statement)

Riesgos para el negocio

  • Pérdida directa de ingresos si la plataforma de ventas o pagos está afectada
  • Incumplimiento de SLAs con clientes o socios de integración
  • Escalada a incidente crítico si la saturación de conexiones causa rechazo total (FATAL: sorry, too many clients)
  • Bloqueos en cadena que dejan transacciones de negocio en estado inconsistente
  • Reportes e indicadores de gestión que no están disponibles cuando se necesitan

Checklist técnico detallado

  • 1. Verificar sesiones bloqueadas Identifica qué proceso está bloqueando a otros. Una sesión en espera larga puede cascadear a docenas de conexiones.
  • 2. Revisar tablas con alto dead tuple count Un valor alto de n_dead_tup con last_autovacuum nulo o muy antiguo indica que autovacuum no está procesando la tabla.
  • 3. Verificar configuración de autovacuum Si autovacuum_vacuum_cost_delay es muy alto (por encima de 20ms), el autovacuum corre demasiado lento en tablas con mucha actividad.
  • 4. Revisar índices no utilizados Los índices sin uso ocupan espacio, ralentizan las escrituras y confunden al planificador. Candidatos a eliminar si idx_scan < 50 y la tabla lleva meses activa.
  • 5. Verificar hit ratio del buffer cache Un hit ratio por debajo de 95% indica que PostgreSQL está leyendo demasiado desde disco. Puede apuntar a shared_buffers insuficiente o tablas con bloat.
  • 6. Verificar parámetros de memoria clave shared_buffers recomendado: 25% de la RAM. work_mem se multiplica por número de conexiones activas; valores altos pueden causar OOM.
  • 7. Revisar locks activos (no solo bloqueados) Ver todos los locks en espera para detectar si hay conflicto en una tabla específica.
  • 8. Analizar plan de ejecución de la consulta lenta Usa EXPLAIN (ANALYZE, BUFFERS) en la consulta identificada en pg_stat_statements. Busca "Seq Scan" en tablas grandes o "Hash Join" inesperado.
  • 9. Verificar presión en checkpoints Si checkpoints_req es alto comparado con checkpoints_timed, el sistema está escribiendo WAL más rápido de lo que puede hacer checkpoint. Considera aumentar max_wal_size.
  • 10. Ejecutar ANALYZE manual si las estadísticas están muy desactualizadas Si last_autoanalyze lleva más de 24h en tablas con mucho movimiento, las estadísticas están viejas y el planificador elige planes subóptimos.

Cuándo escalar el incidente a un DBA especialista

  • El número de conexiones activas supera el 85% de max_connections y la aplicación comienza a recibir errores
  • Hay bloqueos en cadena que no se resuelven solos en 10 minutos
  • La base de datos dejó de aceptar conexiones nuevas (FATAL: sorry, too many clients)
  • Los logs muestran errores PANIC, FATAL o out of memory
  • El servidor alcanza swap activo o hay procesos OOM killer en los logs del SO
  • El equipo interno no logra identificar la causa raíz en 30-60 minutos con el negocio afectado

Preguntas frecuentes

¿Por qué PostgreSQL se volvió lento de repente sin cambios en el código?

Las causas más comunes sin cambio de código son: tablas que superaron el umbral de autovacuum con muchos dead tuples, estadísticas desactualizadas que hacen que el planificador escoja un plan ineficiente, o un cambio en el volumen de datos que hace que un índice ya no sea selectivo. También es frecuente que backups que llenan el disco fuercen a PostgreSQL a escribir en un sistema de archivos lleno, degradando el I/O.

¿Debo reiniciar PostgreSQL para resolver la lentitud?

En general, no. Reiniciar PostgreSQL elimina sesiones activas y puede resolver bloqueos colgados temporalmente, pero no corrige la causa raíz. Si la lentitud se debe a falta de vacuum o estadísticas desactualizadas, volverá a aparecer. El reinicio debe reservarse para situaciones de emergencia (proceso zombie, OOM) y siempre con un análisis previo del estado actual.

¿Qué hace autovacuum y por qué afecta el rendimiento si no corre?

El autovacuum limpia las versiones antiguas de filas (dead tuples) generadas por UPDATE y DELETE, y actualiza las estadísticas del planificador de consultas. Si no corre con suficiente frecuencia, las tablas acumulan bloat (espacio desperdiciado), el planificador toma decisiones incorrectas y los índices se vuelven menos eficientes. En tablas con alta concurrencia o muchos DELETE/UPDATE, normalmente es necesario ajustar autovacuum_vacuum_scale_factor y autovacuum_vacuum_cost_delay.

¿Cómo sé si el problema es la red, la aplicación o la base de datos?

Si pg_stat_activity muestra consultas con alto tiempo de ejecución (columna query_start muy distante del momento actual), el cuello de botella está en la base de datos. Si las consultas terminan rápido pero la aplicación es lenta, el problema puede estar en el connection pool, la serialización de datos o la red. Si pg_stat_activity muestra muchas sesiones en estado idle in transaction, el código de la aplicación no está cerrando transacciones correctamente.

¿Aumentar shared_buffers siempre mejora el rendimiento?

No necesariamente. La recomendación general es 25% de la RAM disponible. Más allá del 40% puede perjudicar el rendimiento al desplazar el page cache del sistema operativo, que PostgreSQL también usa para leer datos. Los ajustes de shared_buffers deben hacerse con medición real antes y después; un cambio mal dimensionado puede empeorar el problema.