web-dev-qa-db-ja.com

count(*)のUNIQUEインデックスでのインデックススキャン

約2,300万行のテーブルtがあります(サイズは4248 MB)。その中に_row_id_制約のある列_not null_があります。 t(row_id)の一意のインデックス_p1_。

テーブルのすべての行をカウントするためにselect count(*) from tを実行すると、プランナーから次のように通知されます。

_Seq Scan on t  (cost=0.00..686191.06 rows=23176906 width=0) 
_

高速な_Index Only Scan_(インデックスp1はわずか698 MB-6分の1しか占有しない)と思っていました。

_SET enable_seqscan = off_を実行した場合でも、プランナはテーブルの行の読み取りを要求します。

_QUERY PLAN
  ->  Bitmap Heap Scan on t  (cost=210923.32..897114.38 rows=23176906 width=0)
        ->  Bitmap Index Scan on p1  (cost=0.00..205129.09 rows=23176906 width=0)
_

この場合、一意のインデックスが無視されるのはなぜですか?キャッチは何ですか?

PostgreSQL 10.4を使用しています

クリーンルームテストでは、次のことを行いました。

_create table tmp
(
  row_id      varchar(15) unique not null,
   <10 original cols>
);

insert into tmp (row_id, <10 cols>) select row_id, <10 cols> from t;
commit;
analyze tmp;

set enable_seqscan = on;
explain (analyze, buffers) select count(*) from tmp;
QUERY PLAN
Aggregate  (cost=744070.45..744070.46 rows=1 width=8) (actual time=5631.501..5631.502 rows=1 loops=1)
  Buffers: shared hit=209109 read=245254
  ->  Seq Scan on tmp  (cost=0.00..686128.96 rows=23176596 width=0) (actual time=0.014..3481.967 rows=23176906 loops=1)
        Buffers: shared hit=209109 read=245254
Planning time: 0.064 ms
Execution time: 5631.531 ms


SET enable_seqscan = off;
explain (analyze, buffers) select count(*) from tmp;
QUERY PLAN
Aggregate  (cost=980282.14..980282.15 rows=1 width=8) (actual time=16224.408..16224.408 rows=1 loops=1)
  Buffers: shared hit=26285 read=542015
  ->  Bitmap Heap Scan on tmp  (cost=236211.69..922340.65 rows=23176596 width=0) (actual time=10030.115..14157.288 rows=23176906 loops=1)
        Heap Blocks: exact=454363
        Buffers: shared hit=26285 read=542015
        ->  Bitmap Index Scan on tmp_row_id_key  (cost=0.00..230417.54 rows=23176596 width=0) (actual time=9929.582..9929.582 rows=23176906 loops=1)
              Buffers: shared hit=26285 read=87652
Planning time: 0.051 ms
Execution time: 16229.303 ms
_

これまでのところ、並列インデックススキャンはありません。 PostgreSQLは、何らかの理由でテーブルへのアクセスを要求しています。

3
Ilya S.

SET enable_seqscan = offでインデックススキャンを取得できない理由については、インデックスのみのスキャンを取得する必要があります。あなたが提供したデータであなたの状況をまだ再現することはできません。これは確かにPostgreSQL 10.4で動作します。私はあなた自身のユースケースと話すことができません、そしてあなたがあなたがmay現実の世界でインデックススキャンを取得しない理由はたくさんあります。最終的に、これらの行に沿って質問をデバッグすると、結果は単に「計画者の見積もり」になりますが、SET enable_seqscan = offを使用した場合と使用しない場合の環境、構成、および計画に関する多くのデータが必要になります。

サンプルデータ

BEGIN;
  CREATE TABLE foo ( x int NOT NULL UNIQUE );
  INSERT INTO foo (x) SELECT generate_series(1,1e6);
COMMIT;

ANALYZE foo; -- don't forget to analyze

seq_scan

test=# EXPLAIN SELECT count(*) FROM foo;
                                      QUERY PLAN                                      
--------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=10633.55..10633.56 rows=1 width=8)
   ->  Gather  (cost=10633.33..10633.54 rows=2 width=8)
         Workers Planned: 2
         ->  Partial Aggregate  (cost=9633.33..9633.34 rows=1 width=8)
               ->  Parallel Seq Scan on foo  (cost=0.00..8591.67 rows=416667 width=0)
(5 rows)

"Parallel Seq Scan"を実行していることに注意してください

seq_scanなし

SET enable_seq_scan = off;

test=# EXPLAIN SELECT count(*) FROM foo;
                                                  QUERY PLAN                                                  
--------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=26616.97..26616.98 rows=1 width=8)
   ->  Gather  (cost=26616.76..26616.97 rows=2 width=8)
         Workers Planned: 2
         ->  Partial Aggregate  (cost=25616.76..25616.77 rows=1 width=8)
               ->  Parallel Index Only Scan using foo_x_key on foo  (cost=0.42..24575.09 rows=416667 width=0)
(5 rows)

"並列インデックスのみのスキャン"

3
Evan Carroll

コミュニティwikiの回答

あなたの質問は、PostgreSQL wikiページの 「count(*)」ははるかに速くなりましたか? セクションで対処されますインデックスのみのスキャン

PostgreSQLで作成された従来の不満は、一般に、MySQLと不利に比較した場合(少なくともMVCCを使用しないMyIsamストレージエンジンを使用した場合)、「count(*)が遅い」というものでした。インデックスのみのスキャンを使用して、返される行数を制限する述語がなく、インデックスを使用せずに、これらのクエリを満たすことができますタプルがインデックス付き列で順序付けられるように指定する。ただし、実際にはそれほど可能性は高くありません。

プランナーはクエリの総コストを最小限に抑えることに関心があることを理解することが重要です。データベースでは、通常、I/Oのコストが支配的です。そのため、「述語なしのcount(*)」クエリは、インデックスがテーブルよりも大幅に小さい場合にのみインデックスのみのスキャンを使用します。これは通常、テーブルの行幅が一部のインデックスよりもはるかに広い場合にのみ発生します。

可視性マップを調べても可視性を判別できない場合は、インデックスのみのスキャンでヒープタプルにアクセスする必要があります。どちらのケースであるかを確認するとき、大きな「それは依存する」ことがあります。最適な状況では、インデックスのみのスキャンが行われます。それ以外の場合、タプルの可視性をチェックする必要がある場合、最初にインデックスをチェックするオーバーヘッドがないため、順次スキャンがすぐに勝者となります。

bitmap_heap_scanenable seq_scan=offなので、プランナは宣伝どおりに動作します。

順次スキャンを完全に抑制することは不可能ですが、この変数をオフにすると、利用可能な他の方法がある場合にプランナがスキャンを使用しないようにします。

詳細は同じwikiページの他のセクションにあります。

1
user126897

インデックスのみのスキャンは、テーブルのページのほとんどがすべて表示可能としてマークされていない限り、あまり魅力的に見えません。したがって、テーブルをバキュームする必要があります。

PostgreSQLのデフォルトの自動バキューム設定は、インデックスのみのスキャンを適切に維持するためではなく、膨張を防ぐように調整されています。そのため、目的に合わせてテーブルを十分にバキュームするために手動の介入が必要になる場合があります。

それでも全表スキャンが優先される場合があることに注意してください。インデックスのみのスキャンは、テーブル全体のスキャンよりもCPUに負荷がかかり、そのIOはランダムで連続性が低くなる可能性があります。テーブルがインデックスよりも20倍大きい場合、おそらくインデックスのみのスキャンですが、6倍の大きさで、少しトスアップします。

0
jjanes