PostgreSQLには6種類以上のインデックスがあり、B-Treeindexが最も一般的に使用されています。 PostgreSQLのB-Treeindexesの詳細については、以下をお読みください。
PostgreSQLのインデックスは、CREATE TABLEステートメントのPRIMARYKEYおよびUNIQUEに対して作成されたものや、CREATE INDEXステートメントで明示的に作成されたものと同様に、特定の「タイプ」です(ただし、技術的には「indexaccessメソッド」と呼ぶ必要があります)。
>PostgreSQLには次の組み込みのインデックスタイプが付属しています:
- Bツリー
- ハッシュ
- GIN –一般化転置インデックス
- BRIN –ブロック範囲インデックス(v9.5以降のみ)
- GiST –一般化された逆探索木
- SP-GiST –スペースパーティション化されたGiST
Bツリーはデフォルトであり、最も一般的に使用されるインデックスタイプです。 CREATE TABLEステートメント内でプライマリキーまたは一意のキーを指定すると、PostgreSQLはBツリーインデックスを作成します。 USING句のないCREATEINDEXステートメントは、Bツリーインデックスも作成します。
-- the default index type is btree
CREATE INDEX ix_year ON movies (year);
-- equivalent, explicitly lists the index type
CREATE INDEX ix_year ON movies USING btree (year);
Bツリーインデックスは本質的に順序付けられています。 PostgreSQLは、インデックス付きの式でソートするのではなく、この順序を利用できます。たとえば、80年代のすべての映画のタイトルをタイトルで並べ替えるには、並べ替えが必要です。
idxdemo=# explain select title from movies where year between 1980 and 1989 order by title asc;
QUERY PLAN
----------------------------------------------------------------------------------
Sort (cost=240.79..245.93 rows=2056 width=17)
Sort Key: title
-> Index Scan using ix_year on movies (cost=0.29..127.65 rows=2056 width=17)
Index Cond: ((year >= 1980) AND (year <= 1989))
(4 rows)
ただし、インデックス付きの列(年)で並べ替える場合は、追加の並べ替えは必要ありません。
idxdemo=# explain select title from movies where year between 1980 and 1989 order by year asc;
QUERY PLAN
----------------------------------------------------------------------------
Index Scan using ix_year on movies (cost=0.29..127.65 rows=2056 width=21)
Index Cond: ((year >= 1980) AND (year <= 1989))
(2 rows)
更新されないテーブルの場合は、「フィルファクター」をデフォルトの90から増やすことができます。これにより、インデックスが少し小さくなり、高速になります。逆に、インデックス付きパラメータを含むテーブルが頻繁に更新される場合は、フィルファクタを小さくすることができます。これにより、インデックスがわずかに大きくなりますが、挿入と更新が高速になります。
CREATE INDEX ix_smd ON silent_movies (director) WITH (fillfactor = 100);
Bツリーインデックスは、テキストのプレフィックスマッチングに役立ちます。クエリを実行して、「T」の文字で始まるすべての映画を一覧表示してみましょう。
idxdemo=> explain select title from movies where title like 'T%';
QUERY PLAN
-------------------------------------------------------------
Seq Scan on movies (cost=0.00..1106.94 rows=8405 width=17)
Filter: (title ~~ 'T%'::text)
(2 rows)
この計画では、テーブルの完全な順次スキャンが必要です。 movies.titleにBツリーインデックスを追加するとどうなりますか?
idxdemo=> create index ix_title on movies (title);
CREATE INDEX
idxdemo=> explain select title from movies where title like 'T%';
QUERY PLAN
-------------------------------------------------------------
Seq Scan on movies (cost=0.00..1106.94 rows=8405 width=17)
Filter: (title ~~ 'T%'::text)
(2 rows)
まあ、それはまったく役に立ちませんでした。ただし、Postgresに必要なことを実行させるために振りかけることができる魔法のピクシーダストの形式があります:
idxdemo=> create index ix_title2 on movies (title text_pattern_ops);
CREATE INDEX
idxdemo=> explain select title from movies where title like 'T%';
QUERY PLAN
-----------------------------------------------------------------------------
Bitmap Heap Scan on movies (cost=236.08..1085.19 rows=8405 width=17)
Filter: (title ~~ 'T%'::text)
-> Bitmap Index Scan on ix_title2 (cost=0.00..233.98 rows=8169 width=0)
Index Cond: ((title ~>=~ 'T'::text) AND (title ~<~ 'U'::text))
(4 rows)
計画ではインデックスを使用するようになり、コストが削減されました。ここでの魔法は「text_pattern_ops」です。これにより、「テキスト」式のBツリーインデックスをパターン演算子(LIKEおよび正規表現)に使用できます。 「text_pattern_ops」はOperatorClassと呼ばれます。
これは、テキストプレフィックスが固定されたパターンでのみ機能するため、「%Angry%」または「%Men」は機能しないことに注意してください。 PostgreSQLの全文検索を使用して、高度なテキストクエリを実行します。
カバーするインデックスがv11のPostgreSQLに追加されました。カバーインデックスを使用すると、1つ以上の式の値を、インデックス付きの式とともにインデックス内に含めることができます。
すべての映画のタイトルをリリース年順にクエリしてみましょう:
idxdemo=# explain select title from movies order by year asc;
QUERY PLAN
--------------------------------------------------------------------
Sort (cost=3167.73..3239.72 rows=28795 width=21)
Sort Key: year
-> Seq Scan on movies (cost=0.00..1034.95 rows=28795 width=21)
(3 rows)
これには、テーブルの完全な順次スキャンと、それに続く一種の投影された列が含まれます。まず、movies.yearに通常のインデックスを追加しましょう:
idxdemo=# create index ix_year on movies (year);
CREATE INDEX
idxdemo=# explain select title from movies order by year asc;
QUERY PLAN
------------------------------------------------------------------------------
Index Scan using ix_year on movies (cost=0.29..1510.22 rows=28795 width=21)
(1 row)
ここで、Postgresはインデックスを使用して、テーブルからエントリを目的の順序で直接引き出すことにしました。インデックスには「year」の値とテーブル内のタプルへの参照のみが含まれているため、テーブルを検索する必要があります。
インデックス内にも「title」の値を含めると、テーブルルックアップを完全に回避できます。新しい構文を使用して、このようなインデックスを作成しましょう。
idxdemo=# create index ix_year_cov on movies (year) include (title);
CREATE INDEX
Time: 92.618 ms
idxdemo=# drop index ix_year;
DROP INDEX
idxdemo=# explain select title from movies order by year asc;
QUERY PLAN
---------------------------------------------------------------------------------------
Index Only Scan using ix_year_cov on movies (cost=0.29..2751.59 rows=28795 width=21)
(1 row)
Postgresは現在IndexOnlyScanを使用しています。これは、テーブルルックアップが完全に回避されることを意味します。 Postgresはこのクエリにix_yearではなくix_year_covを選択しなかったため、古いインデックスを削除する必要があったことに注意してください。
PostgreSQLは、他のRDBMSの「クラスター化インデックス」とは異なり、テーブル内の行の自動物理順序付けをサポートしていないことで有名です。ほとんどのクエリがほとんど静的なテーブルのほとんどの行を固定された順序で引き出す場合は、物理テーブルストレージをその順序でレイアウトし、シーケンシャルスキャンを使用することをお勧めします。インデックスで指定された順序でテーブルを物理的に並べ替えるには、次を使用します。
CLUSTER VERBOSE movies USING ix_year;
通常、Bツリーインデックスを使用してテーブルを再クラスター化します。これは、テーブル内のすべての行に完全な順序を提供するためです。
インデックスはどのくらいのディスクスペースを占有しますか? pg_relation_sizefunctionは次のように答えることができます:
idxdemo=# select * from pg_relation_size('ix_year');
pg_relation_size
------------------
663552
(1 row)
これにより、インデックスで使用されるディスク容量がバイト単位で返されます。
インデックスに関する詳細情報は、標準のextensionpgstattupleを使用して収集できます。以下の関数を使用する前に、CREATE EXTENSION pgstattuple;
を実行する必要があります。 スーパーユーザーとして関連するデータベースにあります。これらの機能を使用するには、スーパーユーザー権限も必要です。
pgstattuple
関数は、特に未使用の(free_space
を返します )および再利用可能(dead_tuple_len
)インデックス内のディスクスペース。これは、REINDEX
を実行するかどうかを決定するのに非常に役立ちます。 インデックスの膨張を減らすため。
idxdemo=# select * from pgstattuple('ix_year'::regclass);
-[ RECORD 1 ]------+-------
table_len | 663552
tuple_count | 28795
tuple_len | 460720
tuple_percent | 69.43
dead_tuple_count | 0
dead_tuple_len | 0
dead_tuple_percent | 0
free_space | 66232
free_percent | 9.98
pgstattuple
関数は、ツリーのレベルなど、Bツリー固有の情報を返します。
idxdemo=# select * from pgstatindex('ix_year'::regclass);
-[ RECORD 1 ]------+-------
version | 2
tree_level | 1
index_size | 663552
root_block_no | 3
internal_pages | 1
leaf_pages | 79
empty_pages | 0
deleted_pages | 0
avg_leaf_density | 89.72
leaf_fragmentation | 0
これは、インデックスの曲線因子を調整するかどうかを決定するために使用できます。
Bツリーインデックスの内容の調査
拡張ページ検査を使用して、Bツリーの内容を直接調べることができます。この拡張機能を使用するには、スーパーユーザー権限が必要です。
インデックスの1ページ(ここでは13ページ目)のプロパティは次のとおりです。
idxdemo=# select * from bt_page_stats('ix_year', 13);
-[ RECORD 1 ]-+-----
blkno | 13
type | l
live_items | 367
dead_items | 0
avg_item_size | 16
page_size | 8192
free_size | 808
btpo_prev | 12
btpo_next | 14
btpo | 0
btpo_flags | 1
そして、これがページの各アイテムの実際の内容です(ここでは5つに制限されています):
idxdemo=# select * from bt_page_items('ix_year', 13) limit 5;
itemoffset | ctid | itemlen | nulls | vars | data
------------+----------+---------+-------+------+-------------------------
1 | (104,40) | 16 | f | f | 86 07 00 00 00 00 00 00
2 | (95,38) | 16 | f | f | 86 07 00 00 00 00 00 00
3 | (95,39) | 16 | f | f | 86 07 00 00 00 00 00 00
4 | (95,40) | 16 | f | f | 86 07 00 00 00 00 00 00
5 | (96,1) | 16 | f | f | 86 07 00 00 00 00 00 00
(5 rows)
また、各ページに何かを集約するクエリを作成することを検討している場合は、リレーション内のページの総数も必要になります。これは、pg_relpages
から取得できます。 pgstattuple
から 拡張子:
idxdemo=# select pg_relpages('ix_year');
pg_relpages
-------------
81
(1 row)
Bツリーインデックスは、クエリを最適化するための用途の広いツールです。少し実験と計画を立てることで、アプリケーションの応答時間を大幅に改善し、ジョブを報告することができます。
PostgreSQLの他のインデックスタイプも有用であり、特定の場合にはBツリーよりも効率的でパフォーマンスが高くなる可能性があります。この記事では、すべてのタイプの概要を説明します。
共有したいインデックスに関するヒントがありますか?以下にコメントとして残してください!