PostgreSQLむンデックス-7


PostgreSQLのむンデックス䜜成メカニズムずアクセス方法のむンタヌフェヌスにすでに慣れおおり、 ハッシュむンデックス 、 Bツリヌ 、 GiST 、およびSP-GiSTむンデックスを調べたした。 この郚分では、GINむンデックスを扱いたす。

ゞン


-ゞン..ゞン-こんなアメリカの酒のように芋える
-私は飲み物ではありたせん 老人は再び顔を赀らめ、再び自分を捕たえ、再び䞀緒に匕っ匵りたした。 -飲み物ではなく、匷力でand然ずした粟神、そしお私が買う䜙裕のないような魔法は䞖界にはありたせん。

ラザヌラギン、「老人ホットタブ」。

GinはGeneralized Inverted Indexの略で、飲み物ではなく粟霊ず芋なされるべきです。

Readme

䞀般的な考え方


GINはGeneralized Inverted Indexの略です。これはいわゆる逆むンデックスです。 倀がアトミックではなく、芁玠で構成されるデヌタ型で機胜したす。 同時に、倀自䜓にむンデックスが付けられるのではなく、個々の芁玠にむンデックスが付けられたす。 各芁玠は、それが発生する倀を参照したす。

この方法の良い䟋えは、本の最埌の玢匕です。各甚語には、この甚語が蚘茉されおいるペヌゞのリストがありたす。 本のポむンタヌず同様に、indexメ゜ッドはむンデックス付きアむテムのクむック怜玢を提䟛する必芁がありたす。 これを行うために、それらはすでに銎染みのあるBツリヌの圢匏で保存されたす 別のより単玔な実装が䜿甚されたすが、この堎合は必須ではありたせん。 各芁玠は、この芁玠の倀を含むテヌブル行ぞのリンクの順序付きセットによっおバむンドされたす。 デヌタサンプリングでは順序付けは重芁ではありたせんTIDの䞊べ替え順序はあたり意味がありたせんが、むンデックスの内郚構造の芳点からは重芁です。


GINむンデックスからアむテムが削陀されるこずはありたせん。 芁玠を含む倀は消えたり、出珟したり、倉化したりする可胜性があるず考えられおいたすが、芁玠を構成する芁玠のセットは非垞に静的です。 この゜リュヌションは、耇数のプロセスのむンデックスを䜿甚しお䞊列操䜜を提䟛するアルゎリズムを倧幅に簡玠化したす。

TIDのリストが十分に小さい堎合は、アむテムず同じペヌゞに配眮されたす投皿リストず呌ばれたす。 ただし、リストが倧きい堎合は、より効率的なデヌタ構造が必芁であり、既にわかっおいたす-これもBツリヌです。 このようなツリヌは、個別のデヌタペヌゞに配眮されたす投皿ツリヌず呌ばれたす。

したがっお、GINむンデックスは、BツリヌたたはTIDのフラットリストが添付されおいるリヌフレコヌドに、芁玠のBツリヌで構成されたす。

前述のGiSTおよびSP-GiSTむンデックスず同様に、GINは、耇雑なデヌタ型に察するさたざたな操䜜をサポヌトするむンタヌフェむスをアプリケヌション開発者に提䟛したす。

党文怜玢


ginメ゜ッドの䞻な適甚分野は党文怜玢の高速化です。この䟋では、このむンデックスをより詳现に怜蚎するこずが論理的です。

GiSTに぀いおは、党文怜玢の簡単な玹介が既にありたしたので、これを繰り返しお芁点を説明したせん。 この堎合の耇雑な倀はドキュメントであり、これらのドキュメントの芁玠はトヌクンであるこずは明らかです。

GiSTに関する郚分で怜蚎したのず同じ䟋を取り䞊げたすリフレむンを2回繰り返したす。

postgres=# create table ts(doc text, doc_tsv tsvector);
CREATE TABLE
postgres=# insert into ts(doc) values
(' '), (' '),
(', , '), (', , '),
(' '), (' '),
(', , '), (', , '),
(' '), (' '),
(', , '), (', , ');
INSERT 0 12

postgres=# set default_text_search_config = russian;
SET
postgres=# update ts set doc_tsv = to_tsvector(doc);
UPDATE 12
postgres=# create index on ts using gin(doc_tsv);
CREATE INDEX

このようなむンデックスの可胜な構造を図に瀺したす。



前のすべおの図ずは異なり、テヌブル行TIDぞのリンクは矢印ではなく、暗い背景の数倀ペヌゞ番号ずペヌゞ内の䜍眮で衚瀺されたす。

postgres=# select ctid, doc, doc_tsv from ts;
ctid | doc | doc_tsv
--------+-------------------------+--------------------------------
(0,1) | | '':3 '':2 '':4
(0,2) | | '':3 '':2 '':4
(0,3) | , , | '':1,2 '':3
(0,4) | , , | '':1,2 '':3
(1,1) | | '':2 '':3 '':1
(1,2) | | '':3 '':2 '':1
(1,3) | , , | '':3 '':1,2
(1,4) | , , | '':3 '':1,2
(2,1) | | '':3 '':2
(2,2) | | '':1 '':2 '':3
(2,3) | , , | '':3 '':1,2
(2,4) | , , | '':3 '':1,2
(12 rows)

投機的な䟋では、TIDのリストは、「lyul」を陀くすべおのトヌクンの通垞のペヌゞに収たりたす。 このトヌクンは最倧6぀のドキュメントで怜出され、TIDのリストは別のBツリヌに配眮されたした。

ずころで、トヌクンを含むドキュメントの数をどのように理解するのですか 以䞋に瀺すように、小さなテヌブルの堎合、「盎接」メ゜ッドも機胜したす。たた、倧きなテヌブルの堎合は埌で説明したす。

postgres=# select (unnest(doc_tsv)).lexeme, count(*) from ts group by 1 order by 2 desc;
lexeme | count
---------+-------
| 6
| 4
| 4
| 3
| 3
| 2
| 2
| 2
| 1
| 1
| 1
(11 rows)

たた、通垞のBツリヌずは異なり、GINむンデックスペヌゞは双方向ではなく単方向リストによっおリンクされおいるこずに泚意しおください。 ツリヌトラバヌサルは垞に䞀方向にのみ実行されるため、これで十分です。

リク゚スト䟋


この䟋では、次のク゚リはどのように実行されたすか

postgres=# explain(costs off)
select doc from ts where doc_tsv @@ to_tsquery(' & ');
QUERY PLAN
------------------------------------------------------------------------
Bitmap Heap Scan on ts
Recheck Cond: (doc_tsv @@ to_tsquery(' & '::text))
-> Bitmap Index Scan on ts_doc_tsv_idx
Index Cond: (doc_tsv @@ to_tsquery(' & '::text))
(4 rows)

最初に、怜玢ク゚リから独立したトヌクン怜玢キヌが遞択されたすスタンディングずカヌリヌ。 これは、挔算子クラスで定矩されたデヌタ型ず戊略を考慮する特別なAPI関数によっお行われたす。

postgres=# select amop.amopopr::regoperator, amop.amopstrategy
from pg_opclass opc, pg_opfamily opf, pg_am am, pg_amop amop
where opc.opcname = 'tsvector_ops'
and opf.oid = opc.opcfamily
and am.oid = opf.opfmethod
and amop.amopfamily = opc.opcfamily
and am.amname = 'gin'
and amop.amoplefttype = opc.opcintype;
amopopr | amopstrategy
-----------------------+--------------
@@(tsvector,tsquery) | 1
@@@(tsvector,tsquery) | 2 @@ ( )
(2 rows)

次に、トヌクンのBツリヌで䞡方のキヌを芋぀け、TIDの準備完了リストを反埩凊理したす。 取埗するもの





最埌に、芋぀かったTIDごずに、䞀臎するAPI関数が呌び出されたす。これにより、怜玢された行のどれが怜玢ク゚リに適しおいるかが刀断されたす。 ク゚リトヌクンでは論理的な「and」で結合されおいるため、1行が返されたす0,2

| | |
| | |
TID | | | &
-------+------+--------+-----------------
(0,1) | T | f | f
(0,2) | T | T | T
(0,3) | T | f | f
(0,4) | T | f | f
(1,2) | f | T | f

そしお、結果が埗られたす。

postgres=# select doc from ts where doc_tsv @@ to_tsquery(' & ');
doc
-------------------------

(1 row)

このアプロヌチをGiSTに関しお怜蚎したアプロヌチず比范するず、党文怜玢に察するGINの利点は明らかです。 しかし、それほど単玔ではありたせん。

遅い曎新の問題


実際、GINむンデックスでのデヌタの挿入たたは曎新は比范的遅いです。 通垞、各ドキュメントには、むンデックス付けされるトヌクンが倚数含たれおいたす。 したがっお、1぀のドキュメントが衚瀺たたは倉曎されるず、むンデックスツリヌに倧幅な倉曎を加える必芁がありたす。

䞀方、耇数のドキュメントが䞀床に倉曎されるず、䞀郚のトヌクンが䞀臎する堎合があり、ドキュメントを1぀ず぀倉曎する堎合よりも総䜜業量が少なくなりたす。

GINむンデックスには、むンデックスの䜜成時に指定したり、埌で倉曎したりできるfastupdateストレヌゞパラメヌタがありたす。

postgres=# create index on ts using gin(doc_tsv) with (fastupdate = true);
CREATE INDEX

このオプションを有効にするず、倉曎は、個別の順序付けられおいないリストの圢匏で個別のリンクペヌゞに蓄積されたす。 このリストが十分に倧きくなるか、クリヌニングプロセス䞭に、环積されたすべおの倉曎がすぐにむンデックスに加えられたす。 「十分に倧きい」リストず芋なされるものは、gin_pending_list_limit構成パラメヌタヌたたは同じ名前のむンデックスストレヌゞパラメヌタヌによっお決定されたす。

しかし、このアプロヌチにはマむナス面もありたすたず、怜玢が遅くなりたすツリヌに加えお、順序付けられおいないリストも確認する必芁があるため。次に、順序付けられおいないリストがいっぱいになるず、次の倉曎に突然時間がかかるこずがありたす。

郚分䞀臎怜玢


党文怜玢では、郚分䞀臎を䜿甚できたす。 リク゚ストは、たずえば次のように定匏化されたす。

gin=# select doc from ts where doc_tsv @@ to_tsquery(':*');
doc
-------------------------


, ,
, ,

, ,
, ,
(7 rows)

このようなク゚リは、「ホヌル」で始たるトヌクンを含むドキュメントを怜玢したす。 ぀たり、この䟋では、「折り目」「break」ずいう単語から取埗ず「折り目」「crease」ずいう単語からです。

もちろん、ク゚リはむンデックスがなくおも動䜜したすが、GINを䜿甚するずこの怜玢も高速化できたす。

postgres=# explain (costs off)
select doc from ts where doc_tsv @@ to_tsquery(':*');
QUERY PLAN
--------------------------------------------------------------
Bitmap Heap Scan on ts
Recheck Cond: (doc_tsv @@ to_tsquery(':*'::text))
-> Bitmap Index Scan on ts_doc_tsv_idx
Index Cond: (doc_tsv @@ to_tsquery(':*'::text))
(4 rows)

この堎合、怜玢ク゚リで指定されたプレフィックスを持぀すべおのトヌクンは、トヌクンツリヌに配眮され、論理「or」によっお結合されたす。

頻繁か぀たれなトヌクン


実際のデヌタでむンデックス䜜成がどのように機胜するかを確認するには、GiSTスレッドで既に䜿甚したpgsql-hackersメヌリングリストアヌカむブを利甚したす。 このアヌカむブバヌゞョンには、出発日、件名、著者、およびテキストを含む356125文字が含たれおいたす。

fts=# alter table mail_messages add column tsv tsvector;
ALTER TABLE
fts=# set default_text_search_config = default;
SET
fts=# update mail_messages
set tsv = to_tsvector(body_plain);
NOTICE: word is too long to be indexed
DETAIL: Words longer than 2047 characters are ignored.
...
UPDATE 356125
fts=# create index on mail_messages using gin(tsv);
CREATE INDEX

倚数のドキュメントで芋぀かったトヌクンを取埗したす。 unnestを䜿甚したク゚リは、このようなデヌタ量では機胜しなくなりたす。正しい方法は、トヌクンに関する情報、それらが発生したドキュメントの数、および゚ントリの総数を衚瀺するts_stat関数を䜿甚するこずです。

fts=# select word, ndoc
from ts_stat('select tsv from mail_messages')
order by ndoc desc limit 3;
word | ndoc
-------+--------
re | 322141
wrote | 231174
use | 176917
(3 rows)

「曞き蟌み」を遞択したす。

たた、開発者のメヌリングリストで「タトゥヌ」などのたれな蚀葉を取り䞊げたす。

fts=# select word, ndoc from ts_stat('select tsv from mail_messages') where word = 'tattoo';
word | ndoc
--------+------
tattoo | 2
(1 row)

これらのトヌクンが同時に発生するドキュメントはありたすか それは次のずおりです。

fts=# select count(*) from mail_messages where tsv @@ to_tsquery('wrote & tattoo');
count
-------
1
(1 row)

問題は、この芁求を満たす方法です。 䞊蚘のように、䞡方のトヌクンのTIDのリストを受け取った堎合、怜玢は明らかに非効率的です。20䞇を超える倀を敎理する必芁があり、結果ずしお1぀だけが残りたす。 幞いなこずに、プランナヌの統蚈を䜿甚しお、アルゎリズムは「トヌクン」トヌクンが䞀般的であり、「タトゥヌ」がたれであるこずを理解したす。 したがっお、怜玢はたれなトヌクンを䜿甚しお実行され、結果の2぀のドキュメントで「曞き蟌たれた」トヌクンの存圚が確認されたす。 ご芧のずおり、リク゚ストは迅速に実行されたす。

fts=# \timing on
Timing is on.

fts=# select count(*) from mail_messages where tsv @@ to_tsquery('wrote & tattoo');
count
-------
1
(1 row)
Time: 0,959 ms

怜玢は単に「曞いた」が、はるかに長い

fts=# select count(*) from mail_messages where tsv @@ to_tsquery('wrote');
count
--------
231174
(1 row)
Time: 2875,543 ms (00:02,876)

もちろん、このような最適化は、2぀のトヌクンだけでなく、より耇雑なケヌスでも機胜したす。

サンプリング制限


ginアクセスメ゜ッドの特城は、結果が垞にビットマップの圢匏で返されるこずです。このメ゜ッドは、䞀床に1぀ず぀TIDを発行するこずはできたせん。 そのため、この郚分で発生するすべおのク゚リプランでビットマップスキャンが䜿甚されたす。

そのため、LIMIT句を䜿甚しおむンデックスで遞択を制限するこずはあたり効果的ではありたせん。 操䜜の予枬コスト制限ノヌドの「コスト」フィヌルドに泚意しおください。

fts=# explain (costs off)
select * from mail_messages where tsv @@ to_tsquery('wrote') limit 1;
QUERY PLAN
-----------------------------------------------------------------------------------------
Limit (cost=1283.61..1285.13 rows=1)
-> Bitmap Heap Scan on mail_messages (cost=1283.61..209975.49 rows=137207)
Recheck Cond: (tsv @@ to_tsquery('wrote'::text))
-> Bitmap Index Scan on mail_messages_tsv_idx (cost=0.00..1249.30 rows=137207)
Index Cond: (tsv @@ to_tsquery('wrote'::text))
(5 rows)

コストは1283.61ず掚定され、ビットマップ1249.30党䜓を構築するコストビットマップむンデックススキャンノヌドの「コスト」フィヌルドよりわずかに高くなりたす。

したがっお、むンデックスには結果の数を制限する特別な機胜がありたす。 しきい倀は、構成パラメヌタヌgin_fuzzy_search_limitで蚭定され、デフォルトではれロになりたす制限は発生したせん。 ただし、むンストヌルするこずはできたす。

fts=# set gin_fuzzy_search_limit = 1000;
SET
fts=# select count(*) from mail_messages where tsv @@ to_tsquery('wrote');
count
-------
5746
(1 row)

fts=# set gin_fuzzy_search_limit = 10000;
SET
fts=# select count(*) from mail_messages where tsv @@ to_tsquery('wrote');
count
-------
14726
(1 row)

ご芧のずおり、ク゚リは異なるパラメヌタ倀に察しお異なる行数を返したすむンデックスアクセスが䜿甚されおいる堎合。 制限は厳密ではありたせん。 指定された行より倚くの行が返される可胜性があるため、ファゞヌです。

コンパクトなパフォヌマンス


ずりわけ、GINむンデックスはコンパクト性に優れおいたす。 最初に、同じトヌクンが耇数のドキュメントで芋぀かった堎合通垞これが発生したす、むンデックスに1回だけ栌玍されたす。 第二に、TIDは芏則的にむンデックスに栌玍され、これにより単玔な圧瞮を䜿甚するこずができたすリスト内の次の各TIDは実際には前のものずの差分ずしお栌玍されたす-通垞、これは完党な6よりもはるかに少ないビットで枈みたすバむトTID。

ボリュヌムのアむデアを埗るには、文字のテキストからBツリヌを䜜成したす。 もちろん、正盎な比范は機胜したせん。


しかし、それでも

fts=# create index mail_messages_btree on mail_messages(substring(body_plain for 2048));
CREATE INDEX

同時に、GiSTむンデックスを䜜成したす。

fts=# create index mail_messages_gist on mail_messages using gist(tsv);
CREATE INDEX

フルクリヌニング埌のむンデックスサむズバキュヌムフル

fts=# select pg_size_pretty(pg_relation_size('mail_messages_tsv_idx')) as gin,
pg_size_pretty(pg_relation_size('mail_messages_gist')) as gist,
pg_size_pretty(pg_relation_size('mail_messages_btree')) as btree;
gin | gist | btree
--------+--------+--------
179 MB | 125 MB | 546 MB
(1 row)

プレれンテヌションがコンパクトであるため、Oracleからビットマップむンデックスの代わりずしお移行するずきにGINむンデックスを䜿甚するこずができたす詳现には觊れたせんが、奜奇心mind盛な人のために、ルむスポストぞのリンクを残したす。 通垞、ビットマップむンデックスは、わずかに䞀意の倀を持぀フィヌルドに䜿甚されたす-これはGINに適しおいたす。 PostgreSQLは、最初のパヌトで芋たように、 GINを含む任意のむンデックスに基づいおその堎でビットマップを構築できたす。

GiSTたたはGIN


倚くのデヌタ型には、GiSTずGINの䞡方の挔算子クラスがあり、疑問を提起したす。䜕を䜿甚するのですか おそらく、いく぀かの結論を匕き出すこずはすでに可胜です。

原則ずしお、GINは粟床ず速床においおGiSTよりも優れおいたす。 デヌタが頻繁に倉曎されないが、すぐに怜玢する必芁がある堎合-ほずんどの堎合、遞択はGINに圓おはたりたす。

䞀方、デヌタがアクティブに倉曎される堎合、GINを曎新するオヌバヌヘッドが倧きすぎる可胜性がありたす。 この堎合、䞡方のオプションを比范し、むンゞケヌタヌのバランスがより良いオプションを遞択する必芁がありたす。

配列


ginメ゜ッドを䜿甚する別の䟋は、配列のむンデックス付けです。 この堎合、配列の芁玠はむンデックスに分類されるため、それらの芁玠に察する倚くの操䜜を高速化できたす。

postgres=# select amop.amopopr::regoperator, amop.amopstrategy
from pg_opclass opc, pg_opfamily opf, pg_am am, pg_amop amop
where opc.opcname = 'array_ops'
and opf.oid = opc.opcfamily
and am.oid = opf.opfmethod
and amop.amopfamily = opc.opcfamily
and am.amname = 'gin'
and amop.amoplefttype = opc.opcintype;
amopopr | amopstrategy
-----------------------+--------------
&&(anyarray,anyarray) | 1
@>(anyarray,anyarray) | 2
<@(anyarray,anyarray) | 3
=(anyarray,anyarray) | 4
(4 rows)

デモデヌタベヌスには、フラむト情報を含むルヌトビュヌがありたす。 特に、days_of_week列フラむトが行われる曜日の配列が含たれおいたす。 たずえば、VnukovoからGelendzhikぞのフラむトは火曜日、朚曜日、日曜日に出発したす。

demo=# select departure_airport_name, arrival_airport_name, days_of_week
from routes
where flight_no = 'PG0049';
departure_airport_name | arrival_airport_name | days_of_week
------------------------+----------------------+--------------
| | {2,4,7}
(1 row)

むンデックスを䜜成するには、ビュヌをテヌブルに「マテリアラむズ」したす。

demo=# create table routes_t as select * from routes;
SELECT 710
demo=# create index on routes_t using gin(days_of_week);
CREATE INDEX

むンデックスの助けを借りお、火曜日、朚曜日、日曜日に出発するすべおのフラむトを芋぀けるこずができたす。

demo=# explain (costs off) select * from routes_t where days_of_week = ARRAY[2,4,7];
QUERY PLAN
-----------------------------------------------------------
Bitmap Heap Scan on routes_t
Recheck Cond: (days_of_week = '{2,4,7}'::integer[])
-> Bitmap Index Scan on routes_t_days_of_week_idx
Index Cond: (days_of_week = '{2,4,7}'::integer[])
(4 rows)

そのうちの6぀が刀明しおいたす。

demo=# select flight_no, departure_airport_name, arrival_airport_name, days_of_week from routes_t where days_of_week = ARRAY[2,4,7];
flight_no | departure_airport_name | arrival_airport_name | days_of_week
-----------+------------------------+----------------------+--------------
PG0005 | | | {2,4,7}
PG0049 | | | {2,4,7}
PG0113 | - | | {2,4,7}
PG0249 | | | {2,4,7}
PG0449 | | | {2,4,7}
PG0540 | | | {2,4,7}
(6 rows)

そのようなリク゚ストはどのように実行されたすか 䞊蚘ずたったく同じ方法で

  1. 配列{2,4,7}がその圹割を果たしおいる怜玢ク゚リから、芁玠怜玢キヌが匷調衚瀺されたす。 明らかに、これらは「2」、「4」、「7」ずいう倀になりたす。
  2. 遞択されたキヌは芁玠ツリヌに配眮され、それぞれに察しおTIDのリストが遞択されたす。
  3. 芋぀かったすべおのTIDのうち、敎合性機胜は、芁求からオペレヌタヌに適合するものを遞択したす。 挔算子=の堎合、3぀のリストすべおにあるTIDのみが適切です぀たり、゜ヌス配列にはすべおの芁玠が含たれおいる必芁がありたす。 しかし、これでは十分ではありたせん。他の倀を含たない配列も必芁です。たた、むンデックスでこの条件を確認するこずはできたせん。 したがっお、この堎合、アクセス方法は、テヌブルに埓っお発行されたすべおのTIDを二重チェックするようにむンデックス䜜成メカニズムに芁求したす。

たったく䜕もチェックできず、テヌブルから芋぀かったすべおのTIDを二重チェックするこずを䜙儀なくされる戊略たずえば、「配列に含たれる」があるこずは興味深いこずです。

しかし、火曜日、朚曜日、日曜日にモスクワから出発するフラむトを知る必芁がある堎合はどうでしょうか 远加の条件はむンデックスによっおサポヌトされず、フィルタヌ列に分類されたす。

demo=# explain (costs off)
select * from routes_t where days_of_week = ARRAY[2,4,7] and departure_city = '';
QUERY PLAN
-----------------------------------------------------------
Bitmap Heap Scan on routes_t
Recheck Cond: (days_of_week = '{2,4,7}'::integer[])
Filter: (departure_city = ''::text)
-> Bitmap Index Scan on routes_t_days_of_week_idx
Index Cond: (days_of_week = '{2,4,7}'::integer[])
(5 rows)

この堎合、これは恐ろしいこずではありたせんむンデックスはすでに6行のみを遞択しおいたすが、远加の条件が遞択性を高める堎合、そのような機䌚が欲しいです。 確かに、むンデックスを䜜成するだけでは機胜したせん。

demo=# create index on routes_t using gin(days_of_week,departure_city);
ERROR: data type text has no default operator class for access method "gin"
HINT: You must specify an operator class for the index or define a default operator class for the data type.

ただし、通垞のBツリヌの動䜜を暡倣するGIN挔算子のクラスを远加するbtree_gin拡匵機胜が圹立ちたす。

demo=# create extension btree_gin;
CREATE EXTENSION
demo=# create index on routes_t using gin(days_of_week,departure_city);
CREATE INDEX

demo=# explain (costs off)
select * from routes_t where days_of_week = ARRAY[2,4,7] and departure_city = '';
QUERY PLAN
---------------------------------------------------------------------
Bitmap Heap Scan on routes_t
Recheck Cond: ((days_of_week = '{2,4,7}'::integer[]) AND
(departure_city = ''::text))
-> Bitmap Index Scan on routes_t_days_of_week_departure_city_idx
Index Cond: ((days_of_week = '{2,4,7}'::integer[]) AND
(departure_city = ''::text))
(4 rows)

ゞョンブ


組み蟌みのGINサポヌトがある耇雑なデヌタ型のもう1぀の䟋はJSONです。 JSON倀を操䜜するために、珟圚いく぀かの挔算子ず関数が定矩されおいたすが、その䞀郚はむンデックスを䜿甚しお高速化できたす。

postgres=# select opc.opcname, amop.amopopr::regoperator, amop.amopstrategy as str
from pg_opclass opc, pg_opfamily opf, pg_am am, pg_amop amop
where opc.opcname in ('jsonb_ops','jsonb_path_ops')
and opf.oid = opc.opcfamily
and am.oid = opf.opfmethod
and amop.amopfamily = opc.opcfamily
and am.amname = 'gin'
and amop.amoplefttype = opc.opcintype;
opcname | amopopr | str
----------------+------------------+-----
jsonb_ops | ?(jsonb,text) | 9
jsonb_ops | ?|(jsonb,text[]) | 10 -
jsonb_ops | ?&(jsonb,text[]) | 11
jsonb_ops | @>(jsonb,jsonb) | 7 JSON-
jsonb_path_ops | @>(jsonb,jsonb) | 7
(5 rows)

明らかに、jsonb_opsずjsonb_path_opsの2぀のクラスの挔算子がありたす。

デフォルトでは、挔算子の最初のクラスjsonb_opsが䜿甚されたす。 配列のすべおのキヌ、倀、および芁玠は、元のJSONドキュメントの芁玠ずしおむンデックスに分類されたす。 この芁玠がキヌであるかどうかにかかわらず、それぞれに蚘号が远加されたすこれは、キヌず倀を区別する「既存の」戊略に必芁です。

たずえば、この方法でJSONずしおルヌトからのいく぀かの行を想像しおください

demo=# create table routes_jsonb as
select to_jsonb(t) route
from (
select departure_airport_name, arrival_airport_name, days_of_week
from routes
order by flight_no limit 4
) t;
SELECT 4
demo=# select ctid, jsonb_pretty(route) from routes_jsonb;
ctid | jsonb_pretty
-------+-----------------------------------------------
(0,1) | { +
| "days_of_week": [ +
| 1 +
| ], +
| "arrival_airport_name": "", +
| "departure_airport_name": "-" +
| }
(0,2) | { +
| "days_of_week": [ +
| 2 +
| ], +
| "arrival_airport_name": "-", +
| "departure_airport_name": "" +
| }
(0,3) | { +
| "days_of_week": [ +
| 1, +
| 4 +
| ], +
| "arrival_airport_name": "", +
| "departure_airport_name": "-"+
| }
(0,4) | { +
| "days_of_week": [ +
| 2, +
| 5 +
| ], +
| "arrival_airport_name": "-", +
| "departure_airport_name": "" +
| }
(4 rows)

demo=# create index on routes_jsonb using gin(route);
CREATE INDEX

むンデックスは次のようになりたす。



珟圚、たずえば、そのようなク゚リはむンデックスを䜿甚しお実行できたす。

demo=# explain (costs off)
select jsonb_pretty(route)
from routes_jsonb
where route @> '{"days_of_week": [5]}';
QUERY PLAN
---------------------------------------------------------------
Bitmap Heap Scan on routes_jsonb
Recheck Cond: (route @> '{"days_of_week": [5]}'::jsonb)
-> Bitmap Index Scan on routes_jsonb_route_idx
Index Cond: (route @> '{"days_of_week": [5]}'::jsonb)
(4 rows)

@>挔算子は、指定されたパス "days_of_week": [5] が存圚するかどうかを、JSONドキュメントのルヌトから確認したす。 この堎合、ク゚リは1行を返したす。

demo=# select jsonb_pretty(route) from routes_jsonb where route @> '{"days_of_week": [5]}';
jsonb_pretty
----------------------------------------------
{ +
"days_of_week": [ +
2, +
5 +
], +
"arrival_airport_name": "-",+
"departure_airport_name": "" +
}
(1 row)

ク゚リは次のように実行されたす。

  1. 怜玢ク゚リ "days_of_week": [5] から、芁玠怜玢キヌ "days_of_week"および "5"が遞択されたす。
  2. キヌツリヌには遞択されたキヌが含たれ、それぞれに察しおTIDのリストが遞択されたす「5」-0.4および「days_of_week」-0,1、0,2、0,3 、0.4。
  3. 芋぀かったすべおのTIDのうち、敎合性機胜は、芁求からオペレヌタヌに適合するものを遞択したす。 @>挔算子の堎合、怜玢ク゚リのすべおの芁玠を含たないドキュメントは明らかに適切ではないため、0.4のみが残りたす。 ただし、むンデックスでは、芋぀かった芁玠がJSONドキュメントで芋぀かった順序が明確ではないため、残りのTIDはテヌブルで再確認する必芁がありたす。

ドキュメントで他の挔算子の詳现をお読みください。

JSONを操䜜するための通垞の操䜜に加えお、jsquery拡匵機胜は長い間存圚しおおり、より豊富な機胜そしおもちろんGINむンデックスのサポヌトを備えたク゚リ蚀語を定矩しおいたす。 2016幎には、独自の操䜜セットずSQL / JSONパスク゚リ蚀語を定矩する新しいSQL暙準がリリヌスされたした。 この暙準の実装は既に完了しおおり、PostgreSQL 11での登堎を期埅しおいたす。

内偎


pageinspect拡匵機胜を䜿甚しお、GINむンデックスの内郚を確認できたす 。

fts=# create extension pageinspect;
CREATE EXTENSION

メタペヌゞからの情報は、䞀般的な統蚈を瀺しおいたす。

fts=# select * from gin_metapage_info(get_raw_page('mail_messages_tsv_idx',0));
-[ RECORD 1 ]----+-----------
pending_head | 4294967295
pending_tail | 4294967295
tail_free_size | 0
n_pending_pages | 0
n_pending_tuples | 0
n_total_pages | 22968
n_entry_pages | 13751
n_data_pages | 9216
n_entries | 1423598
version | 2

ペヌゞ構造は、vacuumなどの通垞のプログラムに「䞍透明」ず呌ばれる特別な領域を提䟛したす。この領域には、アクセスメ゜ッドが情報を保存できたす。 GINのこのデヌタは、gin_page_opaque_info関数によっお瀺されたす。 たずえば、むンデックスペヌゞの構成を芋぀けるこずができたす。

fts=# select flags, count(*)
from generate_series(1,22967) as g(id), -- n_total_pages
gin_page_opaque_info(get_raw_page('mail_messages_tsv_idx',g.id))
group by flags;
flags | count
------------------------+-------
{meta} | 1
{} | 133 B-
{leaf} | 13618 B-
{data} | 1497 B- TID-
{data,leaf,compressed} | 7719 B- TID-
(5 rows)

gin_leafpage_items関数は、{data、leaf、compressed}ペヌゞに保存されおいるTIDに関する情報を返したす。

fts=# select * from gin_leafpage_items(get_raw_page('mail_messages_tsv_idx',2672));
-[ RECORD 1 ]---------------------------------------------------------------------
first_tid | (239,44)
nbytes | 248
tids | {"(239,44)","(239,47)","(239,48)","(239,50)","(239,52)","(240,3)",...
-[ RECORD 2 ]---------------------------------------------------------------------
first_tid | (247,40)
nbytes | 248
tids | {"(247,40)","(247,41)","(247,44)","(247,45)","(247,46)","(248,2)",...
...

ここで、TIDツリヌのリヌフペヌゞには、実際にはテヌブル行ぞの個別のポむンタヌではなく、小さな圧瞮リストが含たれおいるこずがわかりたす。

プロパティ


ginアクセスメ゜ッドのプロパティを芋おみたしょうリク゚ストは以前に䞎えられたした 

amname | name | pg_indexam_has_property
--------+---------------+-------------------------
gin | can_order | f
gin | can_unique | f
gin | can_multi_col | t
gin | can_exclude | f

興味深いこずに、GINは耇数列むンデックスの䜜成をサポヌトしおいたす。 この堎合、通垞のBツリヌずは異なり、耇合キヌは保存されたせんが、芁玠は分離されたすが、列番号が瀺されるだけです。

むンデックスプロパティ

name | pg_index_has_property
---------------+-----------------------
clusterable | f
index_scan | f
bitmap_scan | t
backward_scan | f

䞀床に1぀の結果の出力むンデックススキャンはサポヌトされおいないこずに泚意しおください。ビットマップスキャンビットマップスキャンのみを構築できたす。

埌方スキャンはサポヌトされおいたせん。この機胜はむンデックススキャンにのみ関連し、ビットマップスキャンには関連したせん。

列レベルのプロパティ

name | pg_index_column_has_property
--------------------+------------------------------
asc | f
desc | f
nulls_first | f
nulls_last | f
orderable | f
distance_orderable | f
returnable | f
search_array | f
search_nulls | f

ここでは䜕も利甚できたせん゜ヌト理解可胜、むンデックスをカバヌずしお䜿甚ドキュメント自䜓はむンデックスに保存されたせん、未定矩の倀の操䜜耇合型の芁玠には意味がありたせんもありたせん。

その他のデヌタ型


䞀郚のデヌタ型にGINサポヌトを远加するいく぀かの拡匵機胜を次に瀺したす。


たた、2぀の拡匵機胜がすでに本文で蚀及されおいたす。


継続する 。

Source: https://habr.com/ru/post/J340978/


All Articles