BTree
ここでの私の問題は、BTreeインデックスは重複した値を格納するために巨大になることです(テーブルが物理的にソートされているとは想定できないため、BTreeインデックスも持っています)。BTreeが巨大な場合、インデックスとインデックスが指すテーブルの部分の両方を読み取る必要があります...
必ずしもそうではありません— 「カバー」しているbtreeインデックスを持っているのが最速の読み取り時間であり、それだけが必要な場合(つまり、追加のストレージを購入できる場合)が最善の策です。
ブリン
私の理解では、役に立たないページを読むことを犠牲にして、ここに小さなインデックスを作成できるということです。小さな値を使用pages_per_range
すると、インデックスが大きくなり(インデックス全体を読み取る必要があるためBRINで問題になります)、大きな値を使用pages_per_range
すると、多くの無駄なページを読み取ることになります。
カバーするbtreeインデックスのストレージオーバーヘッドに余裕がない場合は、BRINが理想的です。これは、クラスタリングが既に配置されているためです(これは BRINが有用であるために重要です)。BRINインデックスは小さいので、適切な値を選択すると、すべてのページがメモリ内にある可能性がありますpages_per_range
。
これらのトレードオフを考慮したpages_per_rangeの適切な値を見つけるための魔法の式はありますか?
魔法の式はありませんが、平均値が占める平均サイズ(ページ単位)pages_per_range
よりもやや小さい値から始めa
ます。おそらく最小化しようとしているのは、一般的なクエリの場合、(スキャンされたBRINページの数)+(スキャンされたヒープページの数)です。Heap Blocks: lossy=n
実行計画で探し、pages_per_range=1
他の値と比較しますpages_per_range
つまり、スキャンされている不要なヒープブロックの数を確認します。
GIN / GiST
それらは主に全文検索に使用されるため、ここで関連性があるかどうかはわかりませんが、重複キーの処理に優れていると聞きました。うのいずれかGIN
/GiST
ここインデックスヘルプ?
GINは検討する価値があるかもしれませんが、おそらくGiSTではありません。ただし、自然なクラスタリングが本当に良い場合は、おそらくBRINの方が良いでしょう。
以下は、あなたのようなダミーデータのさまざまなインデックスタイプの比較例です。
テーブルとインデックス:
create table foo(a,b,c) as
select *, lpad('',20)
from (select chr(g) a from generate_series(97,122) g) a
cross join (select generate_series(1,100000) b) b
order by a;
create index foo_btree_covering on foo(a,b);
create index foo_btree on foo(a);
create index foo_gin on foo using gin(a);
create index foo_brin_2 on foo using brin(a) with (pages_per_range=2);
create index foo_brin_4 on foo using brin(a) with (pages_per_range=4);
vacuum analyze;
関係サイズ:
select relname "name", pg_size_pretty(siz) "size", siz/8192 pages, (select count(*) from foo)*8192/siz "rows/page"
from( select relname, pg_relation_size(C.oid) siz
from pg_class c join pg_namespace n on n.oid = c.relnamespace
where nspname = current_schema ) z;
名前| サイズ| ページ| 行/ページ
:----------------- | :------ | ----:| --------:
foo | 149 MB | 19118 | 135
foo_btree_covering | 56 MB | 7132 | 364
foo_btree | 56 MB | 7132 | 364
foo_gin | 2928 kB | 366 | 7103
foo_brin_2 | 264 kB | 33 | 78787
foo_brin_4 | 136 kB | 17 | 152941
btreeのカバー:
explain analyze select sum(b) from foo where a='a';
| クエリプラン|
| :------------------------------------------------- -------------------------------------------------- ------------------------------------------- |
| 集計(コスト= 3282.57..3282.58行= 1幅= 8)(実際の時間= 45.942..45.942行= 1ループ= 1)|
| -> fooのfoo_btree_coveringを使用したインデックスのみのスキャン(cost = 0.43..3017.80 rows = 105907 width = 4)(actual time = 0.038..27.286 rows = 100000 loops = 1)|
| インデックス条件:(a = 'a' :: text)|
| ヒープフェッチ:0 |
| 計画時間:0.099ミリ秒|
| 実行時間:45.968ミリ秒|
プレーンbtree:
drop index foo_btree_covering;
explain analyze select sum(b) from foo where a='a';
| クエリプラン|
| :------------------------------------------------- -------------------------------------------------- ----------------------------- |
| 集計(コスト= 4064.57..4064.58行= 1幅= 8)(実際の時間= 54.242..54.242行= 1ループ= 1)|
| -> fooでfoo_btreeを使用したインデックススキャン(cost = 0.43..3799.80 rows = 105907 width = 4)(actual time = 0.037..33.084 rows = 100000 loops = 1)|
| インデックス条件:(a = 'a' :: text)|
| 計画時間:0.135ミリ秒|
| 実行時間:54.280ミリ秒|
BRIN pages_per_range = 4:
drop index foo_btree;
explain analyze select sum(b) from foo where a='a';
| クエリプラン|
| :------------------------------------------------- -------------------------------------------------- ----------------------------- |
| 集計(コスト= 21595.38..21595.39行= 1幅= 8)(実際の時間= 52.455..52.455行= 1ループ= 1)|
| -> fooのビットマップヒープスキャン(cost = 888.78..21330.61 rows = 105907 width = 4)(実際の時間= 2.738..31.967 rows = 100000ループ= 1)|
| Condの再確認:(a = 'a' :: text)|
| インデックス再チェックによって削除された行:96 |
| ヒープブロック:lossy = 736 |
| -> foo_brin_4のビットマップインデックススキャン(cost = 0.00..862.30 rows = 105907 width = 0)(実際の時間= 2.720..2.720 rows = 7360ループ= 1)|
| インデックス条件:(a = 'a' :: text)|
| 計画時間:0.101ミリ秒|
| 実行時間:52.501ミリ秒|
BRIN pages_per_range = 2:
drop index foo_brin_4;
explain analyze select sum(b) from foo where a='a';
| クエリプラン|
| :------------------------------------------------- -------------------------------------------------- ----------------------------- |
| 集計(コスト= 21659.38..21659.39行= 1幅= 8)(実際の時間= 53.971..53.971行= 1ループ= 1)|
| -> fooのビットマップヒープスキャン(cost = 952.78..21394.61 rows = 105907 width = 4)(実際の時間= 5.286..33.492 rows = 100000ループ= 1)|
| Condの再確認:(a = 'a' :: text)|
| インデックス再チェックによって削除された行:96 |
| ヒープブロック:lossy = 736 |
| -> foo_brin_2のビットマップインデックススキャン(cost = 0.00..926.30 rows = 105907 width = 0)(actual time = 5.275..5.275 rows = 7360 loops = 1)|
| インデックス条件:(a = 'a' :: text)|
| 計画時間:0.095ミリ秒|
| 実行時間:54.016ミリ秒|
ジン:
drop index foo_brin_2;
explain analyze select sum(b) from foo where a='a';
| クエリプラン|
| :------------------------------------------------- -------------------------------------------------- ------------------------------ |
| 集計(コスト= 21687.38..21687.39行= 1幅= 8)(実際の時間= 55.331..55.331行= 1ループ= 1)|
| -> fooのビットマップヒープスキャン(cost = 980.78..21422.61 rows = 105907 width = 4)(実際の時間= 12.377..33.956 rows = 100000ループ= 1)|
| Condを再確認します:(a = 'a' :: text)|
| ヒープブロック:exact = 736 |
| -> foo_ginのビットマップインデックススキャン(cost = 0.00..954.30 rows = 105907 width = 0)(actual time = 12.271..12.271 rows = 100000 loops = 1)|
| インデックス条件:(a = 'a' :: text)|
| 計画時間:0.118ミリ秒|
| 実行時間:55.366ミリ秒|
ここに dbfiddle