PostgreSQL
Prepared statements y plan cache
Prepared statements, protocolo extendido y plan cache, incluyendo planes genéricos o personalizados, invalidación y relación con pools de conexiones.
- Última actualización
- Actualizada
- Nivel
- Profundización
PostgreSQL
Prepared statements, protocolo extendido y plan cache, incluyendo planes genéricos o personalizados, invalidación y relación con pools de conexiones.
Prepared statements reutilizan parse y, a veces, planificación. El beneficio depende de la vida de la sesión y de si un plan promedio representa bien todos los valores posibles.
PREPARE orders_by_status(text) AS
SELECT id, created_at
FROM orders
WHERE status = $1;
EXECUTE orders_by_status('pending');El extended query protocol separa:
Parse → SQL y tipos
Bind → valores y portal
Execute → ejecuciónLos drivers pueden usar este protocolo sin un PREPARE SQL explícito.
Un custom plan considera los valores actuales. Un generic plan se reutiliza sin conocerlos.
Si:
pending = 0.1%
paid = 95%el mejor acceso puede ser índice para pending y sequential scan para paid. Un generic plan promedio puede ser malo para uno de ellos.
PostgreSQL puede ejecutar planes custom inicialmente y después comparar si un generic plan parece suficientemente barato. El detalle depende de versión y costes.
plan_cache_mode permite diagnóstico:
SET LOCAL plan_cache_mode = force_custom_plan;No lo configures globalmente sin evidencia.
Parámetros ambiguous, especialmente NULL, necesitan casts:
WHERE created_at >= $1::timestamptzEl tipo elegido durante parse forma parte de la statement. Enviar valores con tipos inconsistentes produce errores o casts no deseados.
Cambios de schema, estadísticas u objetos pueden invalidar/replanificar statements. No asumas que un prepared statement conserva para siempre el mismo plan.
Una statement nombrada pertenece a una sesión. Con pool:
Transaction pooling requiere compatibilidad específica del proxy/driver; verifica versión y configuración.
Esto ya es vulnerable antes de prepararse:
const sql = `SELECT ... ORDER BY ${userInput}`;Preparar solo reutiliza estructura. La estructura debe construirse con whitelist y los valores parametrizarse.
Puede aportar poco en queries triviales o conexiones efímeras.
Síntomas:
force_custom_plan.Diagnóstico:
EXPLAIN (ANALYZE, BUFFERS) literal/custom/generic.Para filtros opcionales muy distintos, varias statements pueden ser mejores que un SQL universal:
sin status → statement A
con status → statement BEsto permite planes e índices específicos sin concatenar valores.
Miles de nombres dinámicos pueden llenar memoria de sesión y complicar operación. Usa nombres estables por shape, no por valor.
En transaction pooling, named prepared statements históricamente han tenido limitaciones; versiones modernas pueden ofrecer tracking/rewrite. No generalices: prueba combinación exacta de PgBouncer, driver y protocolo.
plan_cache_mode?Migraciones de esquema y expand-contract coordina varias versiones de aplicación sin bloquear ni destruir datos prematuramente.