web-dev-qa-db-fra.com

Ligne "Recheck Cond:" dans les plans de requête avec un scan d'index bitmap

Ceci est un spin-off des commentaires à la question précédente:

Avec PostgreSQL 9.4, il semble toujours y avoir un Recheck Cond: ligne après les analyses d'index bitmap dans les plans de requête générés par EXPLAIN.

Comme dans la sortie EXPLAIN de la question référencée:

->  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)

Ou dans la sortie de EXPLAIN ANALYZE pour une table simple et immense (avec très peu de 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))

Cela signifie-t-il que les conditions d'index doivent être vérifiées une deuxième fois après un scan d'index bitmap?
Que pouvons-nous apprendre d'autre de la sortie EXPLAIN?

23
Erwin Brandstetter

Comme @ Chris a commenté correctement la question référencée :

une petite enquête semble indiquer que la condition de revérification est toujours imprimée dans le EXPLAIN, mais n'est en fait effectuée que lorsque work_mem est suffisamment petit pour que le bitmap devienne avec perte. Pensées? http://www.postgresql.org/message-id/[email protected]

Bien que cela soit vrai et que le développeur principal Heikki Linnakangas soit une source de première classe, la publication remonte à 2007 (Postgres 8.2). Voici un article de blog de Michael Paquier avec une explication détaillée de Postgres 9.4 , où la sortie de EXPLAIN ANALYZE A été améliorée avec plus d'informations.

La ligne Recheck Cond: Est toujours là pour les analyses d'index bitmap. La sortie de base EXPLAIN ne nous en dira pas plus. Nous obtenons des informations supplémentaires de EXPLAIN ANALYZE Comme on peut le voir dans la deuxième citation de la question:

Heap Blocks: exact=693 lossy=3732

Sur un total de 4425 pages de données (blocs), 693 tuples stockés exactement (y compris les pointeurs Tuple), tandis que le 3732 autres pages étaient lossy (juste la page de données) dans le bitmap. Cela se produit lorsque work_mem N'est pas assez grand pour stocker exactement la totalité du bitmap construit à partir de l'analyse d'index (sans perte).

La condition d'index doit être revérifiée pour les pages du partage avec perte, car le bitmap ne se souvient que des pages à récupérer et non des tuples exacts sur la page. Tous les tuples de la page ne passeront pas nécessairement les conditions d'index, il est nécessaire de en fait revérifier la condition.

Ceci est le thread sur les pirates pgsql où le nouvel ajout a été discuté . L'auteur Etsuro Fujita fournit une formule pour savoir comment calculer le minimum work_mem Pour éviter les entrées bitmap avec perte et les revérifications de condition qui s'ensuivent. Le calcul n'est pas fiable pour les cas complexes avec plusieurs analyses bitmap, il n'a donc pas été utilisé pour générer des nombres réels à partir de EXPLAIN. Il peut encore servir d'estimation pour des cas simples.

Ligne supplémentaire BUFFERS:

De plus, lors de l'exécution avec BUFFERS option: EXPLAIN (ANALYZE, BUFFERS) ... une autre ligne est ajoutée comme:

Buffers: shared hit=279 read=79

Cela indique la quantité de la table (et de l'index) sous-jacente qui a été lue dans le cache (shared hit=279) Et combien a dû être récupérée à partir du disque (read=79). Si vous répétez la requête, la partie "lecture" disparaît généralement pour les requêtes pas trop volumineuses, car tout est mis en cache maintenant après le premier appel. Le premier appel vous indique la quantité déjà mise en cache. Les appels suivants montrent combien votre cache peut gérer (actuellement).

Il y a plus d'options. Le manuel sur l'option BUFFERS:

Spécifiquement, incluez le nombre de blocs partagés frappés, lus, salis et écrits, le nombre de blocs locaux frappés, lus, salis et écrits et le nombre de blocs temporaires lus et écrits.

Lisez la suite, il y a plus.
Voici la liste des options de sortie dans le code source .

18
Erwin Brandstetter

Erwin, puisque c'était notre discussion dans le fil de commentaires d'avant, j'ai décidé de pousser un peu plus loin ...

J'ai une requête très simple à partir d'une table de taille raisonnable. J'ai généralement suffisamment de work_mem, mais dans ce cas j'ai utilisé les commandes

SET work_mem = 64;

pour définir un très petit work_mem et

SET work_mem = default;

pour définir mon work_mem retour à être suffisamment grand pour ma requête.

EXPLIQUER et revérifier la condition

Donc, exécuter ma requête avec seulement EXPLAIN comme

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

J'ai obtenu les résultats pour les niveaux bas et haut work_mem:

Faible 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)

Haute 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)

Pour faire court, pour EXPLAIN uniquement, comme prévu, le plan de requête indique qu'une condition de recontrôle est possible, mais nous ne pouvons pas savoir si elle sera réellement calculée.

EXPLIQUER L'ANALYSE ET REVérifier la condition

Lorsque nous incluons ANALYZE dans la requête, les résultats nous en disent plus sur ce que nous devons savoir.

Faible 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)

Haute 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)

Encore une fois, comme prévu, l'inclusion de ANALYZE nous révèle des informations très importantes. Dans le bas work_mem cas, nous voyons qu'il y a des lignes supprimées par la vérification de l'index, et que nous avons lossy des blocs de tas.

Conclusion? (ou l'absence de)

Malheureusement, il semble que EXPLAIN seul ne soit pas suffisant pour savoir si une nouvelle vérification de l'index être nécessaire car certains des identifiants de ligne sont supprimés au profit de la conservation des pages lors de l'analyse du tas bitmap.

En utilisant EXPLAIN ANALYZE convient parfaitement pour diagnostiquer les problèmes liés aux requêtes de longueur moyenne, mais dans le cas où une requête prend un temps extrêmement long, puis exécuter EXPLAIN ANALYZE pour découvrir que votre index bitmap est en cours de conversion en perte en raison d'une insuffisance work_mem est toujours une contrainte difficile. Je souhaite qu'il y ait un moyen d'avoir EXPLAIN d'estimer la probabilité de cet événement à partir des statistiques du tableau.

10
Chris