PostgreSQLがインデックス付きの列で順次スキャンを実行するのはなぜですか?


150

非常に単純な例-1つのテーブル、1つのインデックス、1つのクエリ:

CREATE TABLE book
(
  id bigserial NOT NULL,
  "year" integer,
  -- other columns...
);

CREATE INDEX book_year_idx ON book (year)

EXPLAIN
 SELECT *
   FROM book b
  WHERE b.year > 2009

私に与える:

Seq Scan on book b  (cost=0.00..25663.80 rows=105425 width=622)
  Filter: (year > 2009)

代わりにインデックススキャンを実行しないのはなぜですか?何が欠けていますか?

回答:


222

SELECTがテーブル内のすべての行の約5〜10%を超える場合、順次スキャンはインデックススキャンよりもはるかに高速です。

これは、インデックススキャンでは各行に複数の IO操作が必要なためです(インデックス内の行を検索し、ヒープから行を取得します)。一方、順次スキャンでは、行ごとに1つのIOのみが必要です。ディスク上のブロック(ページ)に複数の行が含まれているため、1回のIO操作で複数の行をフェッチできます。

ところで、これは他のDBMSにも当てはまります。「インデックスのみのスキャン」などの一部の最適化は除外されます(ただし、SELECT *の場合、このようなDBMSが「インデックスのみのスキャン」に使用することはほとんどありません)。


12
5-10%は、いくつかの構成設定とデータのストレージにも依存します。難しい数字ではありません。
フランクハイケンズ

6
@フランク:それが私が「おおよそ」と言った理由です:)しかし、それを指摘してくれてありがとう
a_horse_with_no_name

5
また、順次スキャンは、一度にヒープから複数のページを要求し、現在の1つで機能している間にカーネルに次のチャンクをフェッチするように要求することができます-インデックススキャンは一度に1ページをフェッチします。(ビットマップスキャンは2つの間で妥協します。通常、インデックススキャンには十分に選択的ではありませんが、フルテーブルスキャンに値するほど非選択的ではないクエリのプランに表示されます)
araqnid

4
興味深い質問は、最初にクエリを実行せずにクエリが返す行数をデータベースがどのように認識するかです。さまざまな値の数やテーブルサイズなどの統計をどこかに保存しますか?
LaurentGrégoire2016年

7
@LaurentGrégoire:はい、データベースには行数と値の分布に関する統計が保存されます。詳細はマニュアルを参照してください:postgresql.org/docs/current/static/planner-stats.html
a_horse_with_no_name


0

インデックススキャンでは、読み取りヘッドは1つの行から別の行にジャンプします(これは、次の物理ブロックの読み取りよりも1000倍遅いです(順次スキャン))。

したがって、(取得するレコードの数* 1000)がレコードの総数より少ない場合、インデックススキャンのパフォーマンスが向上します。


0

@a_horse_with_no_nameはそれをかなりよく説明しました。また、インデックススキャンを本当に使用したい場合は、通常、where句で境界付き範囲を使用する必要があります。例-年> 2019および年<2020。

多くの場合、統計はテーブルで更新されず、制約のために更新できない場合があります。この場合、オプティマイザーは、2019年を超える行数を知る必要はありません。したがって、完全な知識の代わりに順次スキャンを選択します。境界パーティションはほとんどの場合問題を解決します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.