Línea "Recheck Cond:" en los planes de consulta con un escaneo de índice de mapa de bits

21

Este es un spin-off de los comentarios a la pregunta anterior:

Usando PostgreSQL 9.4, siempre parece haber una Recheck Cond:línea después de los escaneos del índice de mapa de bits en los planes de consulta generados por EXPLAIN.

Como en el EXPLAINresultado de la pregunta referenciada:

->  Bitmap Heap Scan on table_three  (cost=2446.92..19686.74 rows=8159 width=7)
      Recheck Cond: (("timestamp" > (now() - '30 days'::interval)) AND (client_id > 0))
      ->  BitmapAnd  (cost=2446.92..2446.92 rows=8159 width=0)
            ->  Bitmap Index Scan on table_one_timestamp_idx  (cost=0.00..1040.00 rows=79941 width=0)
                  Index Cond: ("timestamp" > (now() - '30 days'::interval))
            ->  Bitmap Index Scan on fki_table_three_client_id  (cost=0.00..1406.05 rows=107978 width=0)
                  Index Cond: (client_id > 0)

O en la salida de EXPLAIN ANALYZEuna tabla simple y enorme (con muy poco work_mem):

EXPLAIN ANALYZE SELECT * FROM aa WHERE a BETWEEN 100000 AND 200000;
Bitmap Heap Scan on aa  (cost=107.68..4818.05 rows=5000 width=4) (actual time=27.629..213.606 rows=100001 loops=1)
  Recheck Cond: ((a >= 100000) AND (a <= 200000))
  Rows Removed by Index Recheck: 758222
  Heap Blocks: exact=693 lossy=3732
  ->  Bitmap Index Scan on aai  (cost=0.00..106.43 rows=5000 width=0) (actual time=27.265..27.265 rows=100001 loops=1)
        Index Cond: ((a >= 100000) AND (a <= 200000))

¿Eso significa que las condiciones del índice deben verificarse por segunda vez después de un escaneo del índice de mapa de bits?
¿Qué más podemos aprender de la EXPLAINsalida?

Erwin Brandstetter
fuente

Respuestas:

17

Como @Chris comentó correctamente sobre la pregunta referenciada :

una pequeña investigación parece indicar que la condición de verificación nuevamente se imprime siempre en EXPLAIN, pero en realidad solo se realiza cuando work_memes lo suficientemente pequeña como para que el mapa de bits se vuelva con pérdida. Pensamientos? http://www.postgresql.org/message-id/[email protected]

Si bien todo esto es cierto y el desarrollador principal Heikki Linnakangas es una fuente de primera clase, la publicación se remonta a 2007 (Postgres 8.2). Aquí hay una publicación de blog de Michael Paquier con una explicación detallada de Postgres 9.4 , donde la salida de EXPLAIN ANALYZEse ha mejorado con más información.

La Recheck Cond:línea siempre está ahí para los escaneos de índice de mapa de bits. La salida de basic EXPLAINno nos dirá más. Obtenemos información adicional EXPLAIN ANALYZEcomo se puede ver en la segunda cita de la pregunta:

Heap Blocks: exact=693 lossy=3732

De un total de 4425 páginas de datos (bloques), 693 almacenaron tuplas exactamente (incluidos los punteros de tuplas), mientras que las otras 3732 páginas fueron con pérdida (solo la página de datos) en el mapa de bits. Eso sucede cuando work_memno es lo suficientemente grande como para almacenar todo el mapa de bits creado a partir del escaneo del índice exactamente (sin pérdidas).

La condición de índice debe volver a comprobarse para las páginas del recurso compartido con pérdida, ya que el mapa de bits solo recuerda qué páginas buscar y no las tuplas exactas en la página. No todas las tuplas de la página pasarán necesariamente las condiciones de índice, es necesario realidad vuelva a verificar la condición.

Este es el hilo en los hackers pgsql donde se discutió la nueva adición . El autor Etsuro Fujita proporciona una fórmula sobre cómo calcular el mínimo work_mempara evitar entradas de mapa de bits con pérdida y las comprobaciones de condición subsiguientes. El cálculo no es confiable para casos complejos con múltiples escaneos de mapa de bits, por lo que no se utilizó para generar números reales EXPLAIN. Todavía puede servir como una estimación para casos simples.

Línea adicional BUFFERS:

Además, cuando se ejecuta con la BUFFERSopción: EXPLAIN (ANALYZE, BUFFERS) ...se agrega otra línea como:

Buffers: shared hit=279 read=79

Esto indica cuánto de la tabla subyacente (e índice) se leyó de la memoria caché ( shared hit=279) y cuánto se tuvo que recuperar del disco ( read=79). Si repite la consulta, la parte "leer" generalmente desaparece para consultas no demasiado grandes, porque todo se almacena en caché ahora después de la primera llamada. La primera llamada le indica cuánto se almacenó en caché. Las llamadas posteriores muestran cuánto puede manejar su caché (actualmente).

Hay mas opciones El manual sobre la BUFFERSopción:

Específicamente, incluya el número de bloques compartidos golpeados, leídos, sucios y escritos, el número de bloques locales golpeados, leídos, sucios y escritos, y el número de bloques temporales leídos y escritos.

Sigue leyendo, hay más.
Aquí está la lista de opciones de salida en el código fuente .

Erwin Brandstetter
fuente
10

Erwin, dado que esta era nuestra discusión en el hilo de comentarios de antes, decidí analizarlo un poco más ...

Tengo una consulta muy simple de una tabla de tamaño razonable. Normalmente tengo suficiente work_mem, pero en este caso usé los comandos

SET work_mem = 64;

para establecer un muy pequeño work_memy

SET work_mem = default;

para hacer que mi work_memespalda sea lo suficientemente grande para mi consulta.

EXPLICAR y volver a comprobar la condición

Por lo tanto, el funcionamiento de mi consulta, con sólo EXPLAINcomo

EXPLAIN 
SELECT * FROM olap.reading_facts
WHERE meter < 20;

Obtuve los resultados tanto para bajo como para alto work_mem:

Bajo work_mem

Bitmap Heap Scan on reading_facts  (cost=898.92..85632.60 rows=47804 width=32)
  Recheck Cond: (meter < 20)
  ->  Bitmap Index Scan on idx_meter_reading_facts  (cost=0.00..886.96 rows=47804 width=0)
        Index Cond: (meter < 20)

Alto work_mem

Bitmap Heap Scan on reading_facts  (cost=898.92..85632.60 rows=47804 width=32)
  Recheck Cond: (meter < 20)
  ->  Bitmap Index Scan on idx_meter_reading_facts  (cost=0.00..886.96 rows=47804 width=0)
        Index Cond: (meter < 20)

Larga historia corta, EXPLAINsolo, como se esperaba, el plan de consulta indica que es posible una condición Recheck, pero no podemos saber si realmente se calculará.

EXPLIQUE ANÁLISIS Y Vuelva a comprobar la condición

Cuando incluimos ANALYZEen la consulta, los resultados nos dicen más sobre lo que necesitamos saber.

Bajo work_mem

Bitmap Heap Scan on reading_facts  (cost=898.92..85632.60 rows=47804 width=32) (actual time=3.130..13.946 rows=51840 loops=1)
  Recheck Cond: (meter < 20)
  Rows Removed by Index Recheck: 86727
  Heap Blocks: exact=598 lossy=836
  ->  Bitmap Index Scan on idx_meter_reading_facts  (cost=0.00..886.96 rows=47804 width=0) (actual time=3.066..3.066 rows=51840 loops=1)
        Index Cond: (meter < 20)

Alto work_mem

Bitmap Heap Scan on reading_facts  (cost=898.92..85632.60 rows=47804 width=32) (actual time=2.647..7.247 rows=51840 loops=1)
  Recheck Cond: (meter < 20)
  Heap Blocks: exact=1434
  ->  Bitmap Index Scan on idx_meter_reading_facts  (cost=0.00..886.96 rows=47804 width=0) (actual time=2.496..2.496 rows=51840 loops=1)
        Index Cond: (meter < 20)

Nuevamente, como se esperaba, la inclusión de ANALYZEnos revela información muy importante. En el work_memcaso bajo , vemos que hay filas eliminadas por la revisión del índice, y que tenemos lossybloques de montón.

¿Conclusión? (o la falta de ello)

Desafortunadamente, parece que EXPLAINpor sí solo no es suficiente para saber si realmente será necesario volver a verificar el índice porque algunos de los ID de fila se descartan a favor de retener páginas durante la exploración de montón de mapa de bits.

El uso EXPLAIN ANALYZEestá bien para diagnosticar los problemas con consultas de longitud moderada, pero en el caso de que una consulta demore mucho tiempo en completarse, luego ejecutarse EXPLAIN ANALYZEpara descubrir que su índice de mapa de bits se está convirtiendo en con pérdida debido a que work_memes insuficiente . Desearía que hubiera una manera de EXPLAINestimar la probabilidad de que esto ocurra a partir de las estadísticas de la tabla.

Chris
fuente