web-dev-qa-db-ja.com

EXPLAINがインデックススキャンのヒープフェッチを表示しない理由

Bツリーインデックスと単純なbツリーインデックスをカバーすることの間の潜在的なパフォーマンスの違いを比較しようとしましたが、EXPLAIN(ANALYZE,BUFFERS)出力と混同されました。

テスト環境

-- function to fill test table
CREATE OR REPLACE FUNCTION fillTable (n INTEGER)
   RETURNS INTEGER AS $rowsCount$
DECLARE
   counter INTEGER := 0 ;
BEGIN

   IF (n < 1) THEN
      RETURN 0 ;
   END IF;

   LOOP
      EXIT WHEN counter = n ;
      counter := counter + 1 ;
      insert into key_value_test(key, value) VALUES (counter,counter);
   END LOOP ;

   return counter;

END ;
$rowsCount$
LANGUAGE plpgsql;

単純なbツリーインデックスのテストケース

drop table key_value_test;
create table key_value_test
(
    key bigint not null,
    value integer not null,
    test VARCHAR(100),
    CONSTRAINT pk_key_value_test
    PRIMARY KEY(key)
);

SELECT  fillTable(1000000);

vacuum;

EXPLAIN( ANALYZE,BUFFERS ) SELECT  key, value
FROM key_value_test
WHERE  key = 740080;

テスト実行結果:

Index Scan using pk_key_value_test on key_value_test  (cost=0.42..8.44 rows=1 width=12) (actual time=0.013..0.014 rows=1 loops=1)   
Index Cond: (key = 913680)   
Buffers: shared hit=4 
Planning Time: 0.062 ms 
Execution Time: 0.026 ms

bツリーインデックスをカバーするためのテストケース

drop table if exists key_value_test;
create table key_value_test
(
    key bigint not null,
    value integer not null,
    CONSTRAINT pk_key_value_test
      PRIMARY KEY(key) include(value)
);
SELECT  fillTable(1000000);

EXPLAIN( ANALYZE,BUFFERS ) SELECT  key, value
FROM key_value_test
WHERE  key = 740080;

vacuum; 

EXPLAIN( ANALYZE,BUFFERS ) SELECT  key, value
FROM key_value_test
WHERE  key = 740080;

テスト実行結果

真空前:

Index Only Scan using pk_key_value_test on key_value_test  (cost=0.43..8.45 rows=1 width=12) (actual time=0.127..0.128 rows=1 loops=1)
  Index Cond: (key = 740080)
  Heap Fetches: 1
  Buffers: shared hit=4
Planning Time: 0.078 ms
Execution Time: 0.146 ms

真空後:

Index Only Scan using pk_key_value_test on key_value_test  (cost=0.42..4.44 rows=1 width=12) (actual time=0.143..0.144 rows=1 loops=1)
  Index Cond: (key = 740080)
  Heap Fetches: 0
  Buffers: shared hit=4
Planning Time: 0.273 ms
Execution Time: 0.156 ms

私のqiestionはなぜHeap Fetchesはインデックスのみのスキャンの場合のみ表示されますが、インデックススキャンの場合は情報が非表示になりますか?これで、単純なインデックスとカバリングインデックスの共有バッファへの同じリクエスト数があることがわかります。では、この状況でインデックスをカバーするためのパフォーマンスへの影響はどこにあるのでしょうか。 EXPLAINを使用してパフォーマンスへの潜在的な影響を測定できないことは、本当に奇妙です。

3
Frank59

インデックススキャンは、すべての行のヒープからフェッチします。これが、インデックスのみのスキャンではない理由です。インデックスのみのスキャンのカウントを表示することは、情報を提供する唯一のケースであるため、意味があります。

「Buffers:」という行は、一般に情報が豊富な場合があります(複数行が問題となっている現実的な場合)。ただし、IOSの場合、この行はどちらの場合も4を示しています。これが変更されない理由は、バキューム処理を行う前は、テーブルに可視性マップがないため、ページがないためです。バッファに読み込まれます。VMが存在しないことを認識し、代わりにテーブルページにヒットします。バキュームの後、(現在存在する)VMページにヒットします。これにより、テーブルにヒットする必要がないことが通知されます。したがって、常に4つのバッファーヒットを取得します。3つはインデックス、1つはvmまたはテーブルのいずれかです。

テーブルに部分的にデータを入力し、VMが存在するようにバキュームし、別のレコードを挿入してから、そのレコードをクエリすると、IOSが5つのバッファにヒットすることがわかります。3インデックスの場合、ページがすべて表示されないことを示す可視性マップページ用に1つ、テーブル用に1つ。

インデックスを何度もヒットする大きなクエリがある場合、バッファヒットカウントの解釈は非常に難しくなります。単一のクエリ実行内で、最近使用された可視性マップページにピンを保持します。ピンを押したまま同じページを繰り返しヒットしても、新しいバッファヒットとしてはカウントされません。 VMの別のページに切り替える必要がある場合のみ、新しいバッファーヒットとしてカウントされます。

このすべてが非常に効果的である可能性は低いです。現実的なハードウェアで現実的なサイズの現実的なデータセットを作成し、それに対して現実的なクエリを実行して、実際のパフォーマンスを測定します。説明が必要な正当なパフォーマンスの違いがわかったら、詳細を調べて説明を見つけることができます。ただし、1行のクエリから外挿しようとしてもあまり役に立ちません。

3
jjanes