Solución:
El núcleo del problema se vuelve obvio aquí:
Seq Scan en la publicación (costo = 0.01..349652.84 filas = 744661 ancho = 8) (tiempo real = 2735.888..2841.393 filas = 1 bucles = 1)
Postgres estimados para devolver 744661 filas mientras, de hecho, resulta ser una sola fila. Si Postgres no sabe mejor qué esperar de la consulta, no puede planificar mejor. Necesitaríamos ver la consulta real oculta detrás (SELECT 9762715)
– y probablemente también conozca la definición de la tabla, restricciones, cardinalidades y distribución de datos. Obviamente, Postgres no puede predecir cómo pocos Las filas serán devueltas por él. Puede haber formas de reescribir la consulta, dependiendo de lo que es.
Si tu saber que la subconsulta nunca puede devolver más de n
filas, puede decirle a Postgres usando:
SELECT mtid
FROM publication
WHERE mtid IN (SELECT ... LIMIT n) -- OR last_modifier=21321
LIMIT 5000;
Si norte es lo suficientemente pequeño, Postgres cambiará a escaneos de índice (mapa de bits). Sin embargo, eso solo funciona para el caso simple. Deja de funcionar al agregar un OR
condición: el planificador de consultas actualmente no puede hacer frente a eso.
Rara vez uso IN (SELECT ...)
para empezar. Por lo general, existe una mejor manera de implementar lo mismo, a menudo con una EXISTS
semi-unión. A veces con un (LEFT
) JOIN
(LATERAL
) …
La solución obvia sería utilizar UNION
, pero lo descartaste. No puedo decir más sin conocer la subconsulta real y otros detalles relevantes.
Mi colega ha encontrado una manera de cambiar la consulta para que necesite una reescritura simple y haga lo que debe hacer, es decir, hacer la subselección en un paso y luego realizar las operaciones adicionales en el resultado:
SELECT mtid FROM publication
WHERE
mtid = ANY( (SELECT ARRAY(SELECT 9762715))::bigint[] )
OR last_modifier=21321
LIMIT 5000;
El análisis de explicar ahora es:
Limit (cost=92.58..9442.38 rows=2478 width=8) (actual time=0.071..0.074 rows=1 loops=1)
InitPlan 2 (returns $1)
-> Result (cost=0.01..0.02 rows=1 width=0) (actual time=0.010..0.011 rows=1 loops=1)
InitPlan 1 (returns $0)
-> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.002 rows=1 loops=1)
-> Bitmap Heap Scan on publication (cost=92.56..9442.36 rows=2478 width=8) (actual time=0.069..0.070 rows=1 loops=1)
Recheck Cond: ((mtid = ANY (($1)::bigint[])) OR (last_modifier = 21321))
Heap Blocks: exact=1
-> BitmapOr (cost=92.56..92.56 rows=2478 width=0) (actual time=0.060..0.060 rows=0 loops=1)
-> Bitmap Index Scan on publication_pkey (cost=0.00..44.38 rows=10 width=0) (actual time=0.046..0.046 rows=1 loops=1)
Index Cond: (mtid = ANY (($1)::bigint[]))
-> Bitmap Index Scan on publication_last_modifier_btree (cost=0.00..46.94 rows=2468 width=0) (actual time=0.011..0.011 rows=0 loops=1)
Index Cond: (last_modifier = 21321)
Planning time: 0.704 ms
Execution time: 0.153 ms
Parece que podemos crear un analizador simple que encuentre y reescriba todas las subselecciones de esta manera, y agregarlo a un gancho de hibernación para manipular la consulta nativa.