Was bedeuten die Metriken für gemeinsam genutzte Puffer für verschiedene Arten von Index-Scans?

7

TL; DR: Wenn ein "EXPLAIN (BUFFERS)" einen "Index Scan" anzeigt, wird nicht die Anzahl der Seiten angezeigt, die aus dem Index gelesen werden müssen. Lässt es nur diese Nummer weg oder liest es tatsächlich keine Seiten (und ich verstehe falsch, wie der Index funktioniert)?

Wir haben eine große schreibgeschützte Tabelle, die so aussieht:

database=> \d my_table
    Table "my_table"
        Column         |       Type       | Modifiers
-----------------------+------------------+-----------
 id                    | integer          |
 date                  | date             |
 country_id            | smallint         |
 ...other columns...
Indexes:
    "my_table_id_date_idx" btree (id, date)

und eine typische Abfrage für diese Tabelle hat eine EXPLAIN wie diese ...

database=> EXPLAIN (BUFFERS, ANALYZE) SELECT id, date, country_id FROM my_table WHERE id = 50 AND date BETWEEN '2015-04-01' AND '2015-04-07';
                                                                                    QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on my_table  (cost=448.40..70400.85 rows=18810 width=10) (actual time=9.011..1447.817 rows=10224 loops=1)
   Recheck Cond: ((id = 50) AND (date >= '2015-04-01'::date) AND (date <= '2015-04-07'::date))
   Buffers: shared hit=232 read=9994
   ->  Bitmap Index Scan on my_table_id_date_idx  (cost=0.00..443.69 rows=18810 width=0) (actual time=6.467..6.467 rows=10224 loops=1)
         Index Cond: ((id = 50) AND (date >= '2015-04-01'::date) AND (date <= '2015-04-07'::date))
         Buffers: shared hit=2 read=30
 Total runtime: 1450.175 ms
(7 rows)

So analysiere ich das:

  1. Die Abfrage führt einen "Bitmap-Index-Scan" durch, bei dem 32 Seiten gelesen werden müssen. Bisher habe ich angenommen, dass diese 32 Seiten die Daten für den Index selbst enthalten. Am Ende wird eine Liste der zu lesenden Seiten erstellt.
  2. Die Abfrage führt dann den "Bitmap-Heap-Scan" durch, bei dem die Seiten gelesen und dann gelesen werden. Am Ende liest dies über 10.000 Seiten.

Wir haben diese (schreibgeschützte) Tabelle kürzlich in ihrem Index in CLUSTER geändert, wodurch die Anzahl der zu lesenden Seiten drastisch reduziert wurde. Jetzt sieht eine Erklärung so aus ...

database=> EXPLAIN (BUFFERS, ANALYZE) SELECT id, date, country_id FROM my_table WHERE id = 50 AND date BETWEEN '2015-04-01' AND '2015-04-07';
                                                                                              QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Index Scan using my_table_id_date_idx on my_table  (cost=0.57..36703.31 rows=20594 width=10) (actual time=0.029..4.830 rows=10224 loops=1)
   Index Cond: ((id = 50) AND (date >= '2015-04-01'::date) AND (date <= '2015-04-07'::date))
   Buffers: shared hit=160
 Total runtime: 6.658 ms
(4 rows)

In diesem Fall wird ein Index-Scan durchgeführt. Soweit ich weiß, besteht der Unterschied zwischen einem Index-Scan und einem Bitmap-Index / Heap-Scan darin, dass erstere Seiten in der durch den Index definierten Reihenfolge lesen, während letztere eine Bitmap von zu lesenden Seiten erstellen (möglicherweise aus mehreren Indizes) ), bestellen Sie die Ergebnisse und lesen Sie sie der Reihe nach. Es scheint mir, dass man in beiden Fällen die Seiten des Index lesen müsste, um die resultierenden zu lesenden Datenseiten tatsächlich zu bestimmen.

Im "Index-Scan" hat die Zeile für "Puffer" jedoch 160 Seiten. Dies basiert auf meinen anderen Tests auf der Anzahl der Seiten der tatsächlichen Daten und enthält nicht die 32 Seiten, die ich oben im Bitmap-Index-Scan für gesehen habe der Index selbst.

database=> set enable_indexscan to false;
SET
database=> EXPLAIN (BUFFERS, ANALYZE) SELECT id, date, country_id FROM my_table WHERE id = 50 AND date BETWEEN '2015-04-01' AND '2015-04-07';
                                                                                    QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on my_table  (cost=491.14..76781.03 rows=20594 width=10) (actual time=1.003..3.873 rows=10224 loops=1)
   Recheck Cond: ((id = 50) AND (date >= '2015-04-01'::date) AND (date <= '2015-04-07'::date))
   Buffers: shared hit=160
   ->  Bitmap Index Scan on my_table_id_date_idx  (cost=0.00..486.00 rows=20594 width=0) (actual time=0.981..0.981 rows=10224 loops=1)
         Index Cond: ((id = 50) AND (date >= '2015-04-01'::date) AND (date <= '2015-04-07'::date))
         Buffers: shared hit=32
 Total runtime: 5.595 ms
(7 rows)

Bedeutet dies, dass der Index-Scan nicht tatsächlich aus dem Index liest? Woher weiß es dann, welche Seiten zu lesen sind? Wird die Anzahl der gelesenen Indexseiten in der Erläuterungsausgabe des Index-Scans einfach weggelassen? Oder vielleicht ist mein Verständnis dessen, was dieser Wert im Bitmap-Index-Scan darstellt, falsch.

Mark Hildreth
quelle

Antworten:

5

In diesem Fall wird ein Index-Scan durchgeführt. Soweit ich weiß, besteht der Unterschied zwischen einem Index-Scan und einem Bitmap-Index / Heap-Scan darin, dass erstere Seiten in der vom Index definierten Reihenfolge lesen, während letztere eine Bitmap von zu lesenden Seiten erstellen (möglicherweise aus mehreren Indizes) ), ordnen Sie die Ergebnisse und lesen Sie sie in der Reihenfolge [Heap].

Richtig.

Es gibt auch Nur-Index-Scans, bei denen der Index gelesen wird, um die Abfrage direkt zu erfüllen, und für die meisten gelesenen Indexseiten gibt es keinen Heap-Abruf. Heap-Abrufe können weiterhin erforderlich sein, wenn das System nicht sicher sein kann, dass die Seiten für alle Transaktionen sichtbar sind. Dies ist also wie ein Index-Scan mit einer Verknüpfung.

Es scheint mir, dass man in beiden Fällen die Seiten des Index lesen müsste, um die resultierenden zu lesenden Datenseiten tatsächlich zu bestimmen.

Richtig.

Im "Index-Scan" hat die Zeile für "Puffer" jedoch 160 Seiten. Dies basiert auf meinen anderen Tests auf der Anzahl der Seiten der tatsächlichen Daten und enthält nicht die 32 Seiten, die ich oben im Bitmap-Index-Scan für gesehen habe der Index selbst.

Ich denke, das ist das Missverständnis. Diese Zeile ist die Summe der Seiten, die aus dem Index und aus dem Heap gelesen wurden. Nicht alle Indexseiten und nicht alle Heap-Seiten müssen gelesen werden.

Ein weiterer wichtiger Hinweis ist, dass die "Puffer" -Metriken wie andere Metriken in einer EXPLAIN-Datei kumulativ sind: Die Anzahl der im übergeordneten Knoten gelesenen Seiten umfasst die der untergeordneten Knoten. Somit enthalten die 160 getroffenen Puffer die Indexlesevorgänge. Tatsächlich wurden in beiden letzten Fällen nur 128 Seiten vom Heap gelesen.

Craig Ringer
quelle
Danke für die Antwort. Ich habe meine Frage geändert, um die Erklärung der neuen Tabellenstruktur anzuzeigen, die einen Bitmap-Index-Scan erzwingt. Es zeigt 160 Seiten auf der Ebene "Bitmap-Heap", genau wie der Index-Scan. Wäre es richtig zu sagen, dass diese "gelesenen Seiten" wie die Timing-Metriken tatsächlich eine Summe des aktuellen Knotens und aller untergeordneten Knoten sind? Wenn ich also 160 Seiten auf der Ebene "Bitmap-Heap-Scan" anzeige, enthält diese Zahl die 32 Seiten der Indexseiten des untergeordneten Knotens und liest also wirklich 128 Seiten vom Heap?
Mark Hildreth
Ich habe eine Antwort auf die Antwort eingereicht, die die Tatsache enthält, dass Erklärungen "kumulativ" sind, was meiner Meinung nach wirklich den Kern meines Missverständnisses ausmacht.
Mark Hildreth