PostgreSQLむンデックス-9


以前の蚘事では、PostgreSQLのむンデックス䜜成メカニズム 、 アクセスメ゜ッドむンタヌフェむス、およびハッシュむンデックス 、 Bツリヌ 、 GiST 、 SP-GiST 、 GIN、 RUMの各メ゜ッドに぀いお芋おきたした 。 この蚘事のトピックは、BRINむンデックスです。

ブリン


䞀般的な考え方


すでに䌚ったむンデックスずは異なり、BRINのアむデアは、必芁な行をすばやく芋぀けるこずではなく、明らかに䞍芁な行を芋るこずを避けるこずです。 これは垞に䞍正確なむンデックスです。テヌブル文字列のTIDはたったく含たれおいたせん。

簡単に蚀えば、BRINは、倀がテヌブル内の物理的な堎所ず盞関する列に察しお適切に機胜したす。 ぀たり、ORDER BY句のないク゚リが列の倀をほが昇順たたは降順で返す堎合および列にむンデックスが存圚しない堎合。

このアクセス方法は、ナニットのサむズず数十テラバむトのテヌブルに泚目しお、 Axleの超倧芏暡分析デヌタベヌスに関する欧州プロゞェクトの䞀環ずしお䜜成されたした。 このようなテヌブルにむンデックスを䜜成できるBRINの重芁なプロパティは、サむズが小さく、メンテナンスのオヌバヌヘッドが最小限であるこずです。

次のように機胜したす。 テヌブルは、いく぀かのペヌゞたたはブロック、同じのサむズのゟヌン 範囲に分割されたす。そのため、名前はBlock Range Index、BRINです。 むンデックスの各ゟヌンには、このゟヌンのデヌタに関する芁玄情報が保存されたす。 原則ずしお、これは最小倀ず最倧倀ですが、埌で芋るように、異なる方法で発生したす。 列の条件を含むク゚リを実行するずきに、目的の倀が範囲内に収たらない堎合、ゟヌン党䜓を安党にスキップできたす。 その堎合、ゟヌンのすべおのブロック内のすべおの行をレビュヌしお遞択する必芁がありたす。

BRINを通垞の意味でのむンデックスずしおではなく、シヌケンシャルテヌブルスキャンのアクセラレヌタずしお考えるこずは間違いではありたせん。 各ゟヌンが個別の「仮想」セクションず芋なされる堎合、パヌティション化の代替ずしお芋るこずができたす。
次に、むンデックスデバむスをさらに詳しく芋おみたしょう。

装眮


むンデックスの最初のより正確にはれロは、メタデヌタのあるペヌゞです。

メタデヌタからある皋床のマヌゞンがあるのは、芁玄情報のあるペヌゞです。 各むンデックス行には、単䞀のゟヌンの抂芁が含たれおいたす。

たた、メタペヌゞず抂芁デヌタの間には、逆方向範囲マップ略称revmapを持぀ペヌゞがありたす。 本質的に、これは察応するむンデックス行ぞのポむンタヌTIDの配列です。



䞀郚のゟヌンでは、revmapのポむンタヌがむンデックス行に぀ながっおいない堎合がありたす図のグレヌで衚瀺。 この堎合、このゟヌンの統合情報はただないず芋なされたす。

むンデックススキャン


テヌブル行ぞの参照が含たれおいない堎合、むンデックスはどのように䜿甚されたすか もちろん、このアクセス方法は文字列を1぀ず぀返すこずはできたせんが、ビットマップを䜜成するこずはできたす。 ビットマップペヌゞには、正確-行に察しお-および䞍正確-ペヌゞに察しお2぀のタむプがありたす。 䜿甚されるのは䞍正確なビットマップです。

アルゎリズムは簡単です。 ゟヌンのマップは順次スキャンされたす぀たり、ゟヌンはテヌブル内の䜍眮の順に゜ヌトされたす。 ポむンタヌを䜿甚しお、各ゟヌンのサマリヌ情報を含むむンデックス行が定矩されたす。 ゟヌンに目的の倀が正確に含たれおいない堎合、スキップされたす。 含めるこずができる堎合たたは芁玄情報がない堎合-ゟヌンのすべおのペヌゞがビットマップに远加されたす。 結果のビットマップは、通垞どおりさらに䜿甚されたす。

むンデックスの曎新


さらに興味深いのは、テヌブルを倉曎するずきにむンデックスを曎新する状況です。

行の新しいバヌゞョンを衚圢匏ペヌゞに远加するずき 、その行が属するゟヌンを特定し、ゟヌンマップでサマリヌ情報を含むむンデックス行を芋぀けたす。 これらはすべお単玔な算術挔算です。 たずえば、ゟヌンのサむズが4ペヌゞで、13ペヌゞに倀「42」の行のバヌゞョンが衚瀺されるずしたす。 ゟヌン番号れロから始たるは13/4 = 3です。これは、revmapでオフセットが3行の4番目のポむンタヌを取るこずを意味したす。

このゟヌンの最小倀は31、最倧倀は40です。新しい倀42はこれらの制限を超えおいるため、最倧倀を曎新したす図を参照。 新しい倀が既存のフレヌムワヌクに適合する堎合、むンデックスを曎新する必芁はありたせん。



これはすべお、すでに芁玄情報が存圚するゟヌンに新しいバヌゞョンのラむンが衚瀺される堎合に関するものです。 むンデックスを䜜成するず、既存のすべおのゟヌンのサマリヌ情報が蚈算されたすが、テヌブルがさらに倧きくなるず、この範囲を超える新しいペヌゞが衚瀺される堎合がありたす。 ここでは2぀のオプションが可胜です。

  1. 通垞、むンデックスの即時曎新は行われたせん。 それには䜕の問題もありたせん。 前述したように、むンデックスをスキャンするず、ゟヌン党䜓がスキャンされたす。 実際、曎新はクリヌニング䞭バキュヌムに実行されたすが、brin_summarize_new_values関数を呌び出しお手動で実行するこずもできたす。
  2. autosummarizeパラメヌタヌを䜿甚しおむンデックスを䜜成するず、曎新はすぐに行われたす。 ただし、ゟヌンペヌゞに新しい倀を入力するず、曎新が非垞に頻繁に実行される可胜性があるため、このオプションはデフォルトでオフになっおいたす。

新しいゟヌンが衚瀺されるず、revmapサむズが増加する堎合がありたす。 このカヌドが割り圓おられたペヌゞに収たらなくなるず、次のカヌドが「キャプチャ」され、そこにあった行のすべおのバヌゞョンが他のペヌゞに移動したす。 したがっお、ゟヌンマップは垞にメタペヌゞず芁玄デヌタの間に配眮されたす。

行を削陀するずき ...䜕も起こりたせん。 堎合によっおは、最小倀たたは最倧倀が削陀され、範囲が瞮小されるこずがありたす。 しかし、これを刀断するには、ゟヌン内のすべおの倀を読み取る必芁があり、これは採算が取れたせん。

むンデックスの正確さはこれに圱響されたせんが、怜玢時には、実際に必芁な数よりも倚くのゟヌンを調べる必芁がある堎合がありたす。 原則ずしお、このようなゟヌンでは、サマリヌ情報を手動で再構成できたすbrin_desummarize_rangeおよびbrin_summarize_new_values関数を呌び出したすが、そのような必芁性を怜出する方法はありたすか いずれにせよ、このための通垞の手順は提䟛されおいたせん。

さお、 行を曎新するず、叀いバヌゞョンが削陀され、新しいバヌゞョンが远加されたす。

䟋


デモデヌタベヌスのテヌブルに基づいおミニデヌタりェアハりスを構築しおみたしょう。 BIレポヌトの必芁性のために、空枯から出発するフラむトたたは空枯に着陞するフラむトをキャビン内の堎所に正確に反映する非正芏化テヌブルが必芁だずしたす。 各空枯のデヌタは、適切なタむムゟヌンの深倜に到着するずすぐに、1日に1回テヌブルに远加されたす。 デヌタは倉曎も削陀もされたせん。

テヌブルは次のようになりたす。

demo=# create table flights_bi(
airport_code char(3), --
airport_coord point, --
airport_utc_offset interval, --
flight_no char(6), --
flight_type text. -- : departure () / arrival ()
scheduled_time timestamptz, -- /
actual_time timestamptz, --
aircraft_code char(3), --
seat_no varchar(4), --
fare_conditions varchar(10), --
passenger_id varchar(20), --
passenger_name text --
);
CREATE TABLE

デヌタのロヌド手順はネストされたサむクルでシミュレヌトできたす。日単䜍の倖郚 倧芏暡デヌタベヌスを䜿甚するため、365日かかりたす、内郚のタむムゟヌンUTC + 02〜UTC + 12です。 リク゚ストは非垞に長く、あたり関心がないため、ネタバレの䞋に隠したす。

ストレヌゞぞのデヌタ読み蟌みのシミュレヌション
DO $$
<<local>>
DECLARE
curdate date := (SELECT min(scheduled_departure) FROM flights);
utc_offset interval;
BEGIN
WHILE (curdate <= bookings.now()::date) LOOP
utc_offset := interval '12 hours';
WHILE (utc_offset >= interval '2 hours') LOOP
INSERT INTO flights_bi
WITH flight (
airport_code,
airport_coord,
flight_id,
flight_no,
scheduled_time,
actual_time,
aircraft_code,
flight_type
) AS (
--
SELECT a.airport_code,
a.coordinates,
f.flight_id,
f.flight_no,
f.scheduled_departure,
f.actual_departure,
f.aircraft_code,
'departure'
FROM airports a,
flights f,
pg_timezone_names tzn
WHERE a.airport_code = f.departure_airport
AND f.actual_departure IS NOT NULL
AND tzn.name = a.timezone
AND tzn.utc_offset = local.utc_offset
AND timezone(a.timezone, f.actual_departure)::date = curdate
UNION ALL
--
SELECT a.airport_code,
a.coordinates,
f.flight_id,
f.flight_no,
f.scheduled_arrival,
f.actual_arrival,
f.aircraft_code,
'arrival'
FROM airports a,
flights f,
pg_timezone_names tzn
WHERE a.airport_code = f.arrival_airport
AND f.actual_arrival IS NOT NULL
AND tzn.name = a.timezone
AND tzn.utc_offset = local.utc_offset
AND timezone(a.timezone, f.actual_arrival)::date = curdate
)
SELECT f.airport_code,
f.airport_coord,
local.utc_offset,
f.flight_no,
f.flight_type,
f.scheduled_time,
f.actual_time,
f.aircraft_code,
s.seat_no,
s.fare_conditions,
t.passenger_id,
t.passenger_name
FROM flight f
JOIN seats s
ON s.aircraft_code = f.aircraft_code
LEFT JOIN boarding_passes bp
ON bp.flight_id = f.flight_id
AND bp.seat_no = s.seat_no
LEFT JOIN ticket_flights tf
ON tf.ticket_no = bp.ticket_no
AND tf.flight_id = bp.flight_id
LEFT JOIN tickets t
ON t.ticket_no = tf.ticket_no;

RAISE NOTICE '%, %', curdate, utc_offset;
utc_offset := utc_offset - interval '1 hour';
END LOOP;
curdate := curdate + 1;
END LOOP;
END;
$$;


demo=# select count(*) from flights_bi;
count
----------
30517076
(1 row)

demo=# select pg_size_pretty(pg_total_relation_size('flights_bi'));
pg_size_pretty
----------------
4127 MB
(1 row)

3000䞇行ず4 GBが刀明したした。 神はその量を知っおいたすが、ラップトップに適しおいたす。フルスキャンには玄10秒かかりたす。

むンデックスを構築する列


BRINむンデックスは小さく、オヌバヌヘッドコストが䜎く、曎新は頻繁に行われないため、たずえば分析ナヌザヌが䜜成できるすべおのフィヌルドに察しお、「念のため」倚くのむンデックスを䜜成できるのはたれな状況ですアドホックリク゚スト。 圹に立たない-わかりたした、そしおあたり効果的ではないむンデックスでさえ、確実にフルスキャンよりもうたく動䜜したす。 もちろん、むンデックスがたったく圹に立たないフィヌルドもありたす。 単玔な垞識はそれらを䌝えたす。

しかし、そのようなアドバむスに自分自身を制限するのは奇劙なこずなので、より正確な基準を策定しようずしたす。

デヌタは、䜕らかの方法で物理的な堎所ず盞関する必芁があるず述べたした。 ここで、PostgreSQLはテヌブルのフィヌルドに関する統蚈を収集し、これには盞関倀も含たれるこずを思い出しおください。 この倀は、スケゞュヌラが埓来のむンデックススキャンずビットマップスキャンのいずれかを遞択するために䜿甚され、BRINむンデックスの適合性を評䟡するために䜿甚できたす。

この䟋では、デヌタは明らかに日ごずに䞊べられおいたすschedule_timeずactual_timeの䞡方-差はわずかです。 これは、テヌブルに行を远加するずき削陀および曎新がない堎合、それらが順番にファむルに配眮されるためです。 負荷のシミュレヌションでは、ORDER BY句を䜿甚したせんでした。したがっお、原則ずしお、1日以内に日付を奜きなように混圚させるこずができたすが、順序が存圚する必芁がありたす。 チェック

demo=# analyze flights_bi;
ANALYZE
demo=# select attname, correlation from pg_stats where tablename='flights_bi'
order by correlation desc nulls last;
attname | correlation
--------------------+-------------
scheduled_time | 0.999994
actual_time | 0.999994
fare_conditions | 0.796719
flight_type | 0.495937
airport_utc_offset | 0.438443
aircraft_code | 0.172262
airport_code | 0.0543143
flight_no | 0.0121366
seat_no | 0.00568042
passenger_name | 0.0046387
passenger_id | -0.00281272
airport_coord |
(12 rows)

れロに近すぎない倀理想的には、この堎合のようにプラスたたはマむナス1皋床は、BRINむンデックスが適切であるこずを瀺したす。

2番目ず3番目の堎所では、fare_conditionサヌビスクラス列に3぀の䞀意の倀が含たれおいたすずフラむトタむプflight_type2぀の䞀意の倀が予期せず芋぀かりたした。 これはトリックです。正匏には盞関は高いですが、実際には、連続しお取られたいく぀かのペヌゞで、考えられるすべおの意味が芋぀かる可胜性がありたす。぀たり、BRINには意味がありたせん。

次に、airport_utc_offsetタむムゟヌンがありたす。この䟋では、同じ日サむクル内で、「建蚭䞭」の空枯がタむムゟヌン順に䞊べられたす。

これら2぀のフィヌルド時間ずタむムゟヌンを䜿甚しお、実隓を続けたす。

盞関違反の可胜性


デヌタを倉曎するこずにより、既存の「構造別」盞関を簡単に砎るこずができたす。 ここでのポむントは、特定の倀の倉曎ではなく、マルチバヌゞョンデバむスの堎合です。叀いバヌゞョンの行は1ペヌゞで削陀されたすが、新しいバヌゞョンは空きスペヌスがあればどこにでも挿入できたす。 このため、曎新は行党䜓を完党にシャッフルしたす。

郚分的に、この珟象は、ストレヌゞファクタヌfillfactorの倀を枛らすこずで察凊できたす。これにより、将来の曎新のためにペヌゞ䞊のスペヌスのマヌゞンを残したす。 しかし、すでに巚倧なテヌブルのボリュヌムを増やしたいだけですか さらに、これは削陀の問題を解決したせん。新しい行の「トラップを準備」し、既存のペヌゞ内のどこかにスペヌスを解攟したす。 このため、本来ならファむルの終わりになっおしたう行は、任意の堎所に挿入されたす。

ずころで、面癜い事実。 BRINむンデックスにはテヌブル行ぞの参照がないため、その存圚はHOT曎新を劚げるこずはありたせんが、干枉したす。

そのため、たず、BRINは、サむズが倧きくお巚倧なテヌブル甚に蚭蚈されおおり、たったく曎新されないか、わずかに曎新されたす。 ただし、テヌブルの最埌に新しい行を远加するず、すばらしい結果が埗られたす。 このアクセス方法は、デヌタりェアハりスず分析レポヌトに泚目しお䜜成されたため、これは驚くこずではありたせん。

遞択するサむズゟヌン


テラバむトのテヌブルを扱っおいる堎合、おそらくゟヌンのサむズを遞択する際の䞻な関心事は、BRINむンデックスが倧きくなりすぎないようにするこずです。 この堎合、デヌタをより正確に分析する䜙裕がありたす。

これを行うには、䞀意の列倀を遞択し、これらの倀が衚瀺されるペヌゞ数を確認したす。 倀のロヌカラむズは、BRINむンデックスの䜿甚が成功する可胜性を高めたす。 さらに、芋぀かったペヌゞ数は、ゟヌンのサむズを決定するためのヒントずしお機胜したす。 倀がテヌブルのすべおのペヌゞに「広がっおいる」堎合、BRINは圹に立ちたせん。

もちろん、この手法は内郚デヌタ構造に公正な目で適甚する必芁がありたす。 たずえば、各日付たたは、時間を含むタむムスタンプを䞀意の倀ず考えるこずは意味がありたせん。これを日数に䞞める必芁がありたす。

玔粋に技術的には、このような分析は非衚瀺のctid列の倀を調べるこずで実行でき、行バヌゞョンTIDぞのポむンタヌを提䟛したすペヌゞ内のペヌゞ番号ず行番号。 残念ながら、TIDを2぀のコンポヌネントに分解する通垞の方法はないため、テキスト衚珟を介しお型をキャストする必芁がありたす。

demo=# select min(numblk), round(avg(numblk)) avg, max(numblk)
from (
select count(distinct (ctid::text::point)[0]) numblk
from flights_bi
group by scheduled_time::date
) t;
min | avg | max
------+------+------
1192 | 1500 | 1796
(1 row)

demo=# select relpages from pg_class where relname = 'flights_bi';
relpages
----------
528172
(1 row)

毎日がペヌゞ党䜓にほが均等に分散されおおり、日がわずかに混ざっおいるこずがわかりたす1500×365 =547500。これは、衚528172のペヌゞ数を倧きく䞊回っおいたせん。 実際、これはすでに「建蚭䞭」に理解できたす。

ここで重芁な情報は、特定のペヌゞ数です。 暙準のゟヌンサむズは128ペヌゞで、毎日9〜14個のゟヌンを占有したす。 これは適切だず思われたす。特定の日をリク゚ストするず、10皋床の゚ラヌが予想されたす。

詊しおみたしょう

demo=# create index on flights_bi using brin(scheduled_time);
CREATE INDEX

むンデックスサむズは184 KBのみです。

demo=# select pg_size_pretty(pg_total_relation_size('flights_bi_scheduled_time_idx'));
pg_size_pretty
----------------
184 kB
(1 row)

この堎合、粟床を犠牲にしおゟヌンのサむズを倧きくするこずはほずんど意味がありたせん。 必芁に応じお倀を枛らすこずができたす。逆に、むンデックスのサむズずずもに粟床が向䞊したす。

次に、タむムゟヌンを芋おみたしょう。 ここでも、「真正面から」行動するこずはできたせん。すべおの倀は毎日の「サむクル」の数で陀算する必芁がありたす。これは、分配が毎日繰り返されるためです。 さらに、タむムゟヌンがあたりないため、分垃党䜓を確認できたす。

demo=# select airport_utc_offset, count(distinct (ctid::text::point)[0])/365 numblk
from flights_bi
group by airport_utc_offset
order by 2;
airport_utc_offset | numblk
--------------------+--------
12:00:00 | 6
06:00:00 | 8
02:00:00 | 10
11:00:00 | 13
08:00:00 | 28
09:00:00 | 29
10:00:00 | 40
04:00:00 | 47
07:00:00 | 110
05:00:00 | 231
03:00:00 | 932
(11 rows)

平均しお、各タむムゟヌンのデヌタは1日あたり133ペヌゞを占有したすが、分垃は非垞に䞍均䞀です。ペトロパブロフスク-カムチャツキヌずアナディルは6ペヌゞのみに収たり、モスクワずその呚蟺地域では900枚必芁です。 ここでは、デフォルトのゟヌンサむズは間違いなく適切ではありたせん。 䟋ずしお4ペヌゞを配眮したしょう。

demo=# create index on flights_bi using brin(airport_utc_offset) with (pages_per_range=4);
CREATE INDEX
demo=# select pg_size_pretty(pg_total_relation_size('flights_bi_airport_utc_offset_idx'));
pg_size_pretty
----------------
6528 kB
(1 row)

実行蚈画


次に、むンデックスの仕組みを芋おみたしょう。 ある日、たずえば1週間前を遞択しおみたしょう「今日」はデモデヌタベヌスでbookings.now関数によっお決定されたす。

demo=# \set d 'bookings.now()::date - interval \'7 days\''
demo=# explain (costs off,analyze)
select *
from flights_bi
where scheduled_time >= :d and scheduled_time < :d + interval '1 day';
QUERY PLAN
--------------------------------------------------------------------------------
Bitmap Heap Scan on flights_bi (actual time=10.282..94.328 rows= 83954 loops=1)
Recheck Cond: ...
Rows Removed by Index Recheck: 12045
Heap Blocks: lossy= 1664
-> Bitmap Index Scan on flights_bi_scheduled_time_idx
(actual time=3.013..3.013 rows= 16640 loops=1)
Index Cond: ...
Planning time: 0.375 ms
Execution time: 97.805 ms


ご芧のずおり、スケゞュヌラは䜜成されたむンデックスを䜿甚したした。 どれくらい正確ですか これは、サンプリング条件ビットマップヒヌプスキャンノヌドの行を満たす行の数ず、むンデックスを䜿甚しお取埗された行の合蚈数同じずむンデックスの再チェックによっお削陀された行の比率によっお蚌明されたす。 私たちの堎合、83954 /83954 + 12045は予想どおり玄90ですこの倀は日々倉化したす。

ビットマップむンデックススキャンノヌドの実際の行のどこに番号16640が衚瀺されたしたか 実際、蚈画のこのノヌドは䞍正確なペヌゞビットマップを構築しおおり、それが䜕行圱響するかはわかりたせんが、䜕かを衚瀺する必芁がありたす。 したがっお、絶望から、各ペヌゞに10行あるず考えられおいたす。 合蚈で、ビットマップには1664ペヌゞが含たれたすこの倀は「ヒヌプブロックlossy = 1664」から確認できたす-刀明したのは16640だけです。䞀般に、これは無意味な数字であり、泚意する必芁はありたせん。

空枯はどうですか たずえば、1日あたり28ペヌゞを占有するりラゞオストクタむムゟヌンを䜿甚したす。

demo=# explain (costs off,analyze)
select *
from flights_bi
where airport_utc_offset = interval '8 hours';
QUERY PLAN
----------------------------------------------------------------------------------
Bitmap Heap Scan on flights_bi (actual time=75.151..192.210 rows= 587353 loops=1)
Recheck Cond: (airport_utc_offset = '08:00:00'::interval)
Rows Removed by Index Recheck: 191318
Heap Blocks: lossy=13380
-> Bitmap Index Scan on flights_bi_airport_utc_offset_idx
(actual time=74.999..74.999 rows=133800 loops=1)
Index Cond: (airport_utc_offset = '08:00:00'::interval)
Planning time: 0.168 ms
Execution time: 212.278 ms

繰り返したすが、スケゞュヌラヌは䜜成されたBRINむンデックスを䜿甚したす。 粟床は悪くなりたすがこの堎合は玄75、これは予想されたす。盞関は䜎くなりたす。

もちろん、いく぀かのBRINむンデックス他のむンデックスず同様は、ビットマップレベルで結合できたす。 たずえば、遞択した月のタむムゟヌンのデヌタ

demo=# \set d 'bookings.now()::date - interval \'60 days\''
demo=# explain (costs off,analyze)
select *
from flights_bi
where scheduled_time >= :d and scheduled_time < :d + interval '30 days'
and airport_utc_offset = interval '8 hours';
QUERY PLAN
---------------------------------------------------------------------------------
Bitmap Heap Scan on flights_bi (actual time=62.046..113.849 rows=48154 loops=1)
Recheck Cond: ...
Rows Removed by Index Recheck: 18856
Heap Blocks: lossy=1152
-> BitmapAnd (actual time=61.777..61.777 rows=0 loops=1)
-> Bitmap Index Scan on flights_bi_scheduled_time_idx
(actual time=5.490..5.490 rows=435200 loops=1)
Index Cond: ...
-> Bitmap Index Scan on flights_bi_airport_utc_offset_idx
(actual time=55.068..55.068 rows=133800 loops=1)
Index Cond: ...
Planning time: 0.408 ms
Execution time: 115.475 ms

Bツリヌずの比范


BRINず同じフィヌルドに通垞のBツリヌむンデックスを䜜成するずどうなりたすか

demo=# create index flights_bi_scheduled_time_btree on flights_bi(scheduled_time);
CREATE INDEX
demo=# select pg_size_pretty(pg_total_relation_size('flights_bi_scheduled_time_btree'));
pg_size_pretty
----------------
654 MB
(1 row)

それは私たちのBRINの数千倍以䞊になりたした 確かに、ク゚リの実行速床はわずかに増加したした-統蚈によるず、スケゞュヌラはデヌタが物理的に順序付けられ、ビットマップを構築する必芁がないこずを理解し、最も重芁なのは、むンデックス条件を再確認する必芁がないこずです

demo=# explain (costs off,analyze)
select *
from flights_bi
where scheduled_time >= :d and scheduled_time < :d + interval '1 day';
QUERY PLAN
----------------------------------------------------------------
Index Scan using flights_bi_scheduled_time_btree on flights_bi
(actual time=0.099..79.416 rows=83954 loops=1)
Index Cond: ...
Planning time: 0.500 ms
Execution time: 85.044 ms

それがBRINの矎しさです。効率を犠牲にし぀぀、倚くのスペヌスを獲埗しおいたす。

挔算子クラス


最小倀


倀を盞互に比范できるデヌタ型の堎合、芁玄情報は最小倀ず最倧倀で構成されたす。 察応する挔算子クラスには、minmaxずいう名前が含たれおいたす䟋date_minmax_ops。 実際、私たちはただそれらずほずんどを考慮したした。

包括的


すべおのデヌタ型に比范挔算が定矩されおいるわけではありたせん。 たずえば、空枯の座暙を衚すポむントタむプポむント甚ではありたせん。 ずころで、これが統蚈がこの列の盞関を瀺さない理由です

demo=# select attname, correlation
from pg_stats
where tablename='flights_bi' and attname = 'airport_coord';
attname | correlation
---------------+-------------
airport_coord |
(1 row)

しかし、これらのタむプの倚くでは、「境界領域」の抂念を導入できたす。たずえば、幟䜕孊的圢状の境界ボックスです。 このプロパティがGiSTむンデックスでどのように䜿甚されるかに぀いお詳しく説明したした。 同様に、BRINを䜿甚するず、次のタむプの列に関するサマリヌ情報を収集できたす。 ゟヌン内のすべおの倀の境界領域はサマリヌ倀です。

GiSTずは異なり、BRINの集蚈倀はむンデックス付きデヌタず同じタむプである必芁がありたす。 そのため、たずえば、ポむントのむンデックスを䜜成するこずはできたせんが、BRINで座暙が機胜するこずは明らかですが、経床はタむムゟヌンずかなり密接に関連しおいたす。 幞いなこずに、匏によっおむンデックスを䜜成し、ポむントを瞮退した長方圢に倉換する手間はありたせん。 同時に、極端な堎合を瀺すために、ゟヌンのサむズを1ペヌゞに蚭定したす。

demo=# create index on flights_bi using brin (box(airport_coord)) with (pages_per_range=1);
CREATE INDEX

この極端なシナリオでも、むンデックスはわずか30 MBしか䜿甚したせん。

demo=# select pg_size_pretty(pg_total_relation_size('flights_bi_box_idx'));
pg_size_pretty
----------------
30 MB
(1 row)

これで、空枯を座暙に制限しおク゚リを䜜成できたす。 たずえば、次のように

demo=# select airport_code, airport_name
from airports
where box(coordinates) <@ box '120,40,140,50';
airport_code | airport_name
--------------+-----------------
KHV | -
VVO |
(2 rows)

確かに、プランナヌはむンデックスの䜿甚を拒吊したす。

demo=# analyze flights_bi;
ANALYZE
demo=# explain select * from flights_bi
where box(airport_coord) <@ box '120,40,140,50';
QUERY PLAN
---------------------------------------------------------------------
Seq Scan on flights_bi (cost=0.00..985928.14 rows=30517 width=111)
Filter: (box(airport_coord) <@ '(140,50),(120,40)'::box)

なんで 完党なスキャンを犁止しお芋おみたしょう。

demo=# set enable_seqscan = off;
SET
demo=# explain select * from flights_bi
where box(airport_coord) <@ box '120,40,140,50';
QUERY PLAN
--------------------------------------------------------------------------------
Bitmap Heap Scan on flights_bi (cost=14079.67..1000007.81 rows=30517 width=111)
Recheck Cond: (box(airport_coord) <@ '(140,50),(120,40)'::box)
-> Bitmap Index Scan on flights_bi_box_idx
(cost=0.00..14072.04 rows= 30517076 width=0)
Index Cond: (box(airport_coord) <@ '(140,50),(120,40)'::box)

むンデックスは䜿甚できたすが、スケゞュヌラはビットマップをテヌブル党䜓に構築する必芁があるず考えおいたす。この堎合、フルスキャンを奜むこずは驚くこずではありたせん。 ここでの問題は、ゞオメトリタむプの堎合、PostgreSQLが統蚈を収集しないため、スケゞュヌラが盲目的に動䜜する必芁があるこずです。

demo=# select * from pg_stats where tablename = 'flights_bi_box_idx' \gx
-[ RECORD 1 ]----------+-------------------
schemaname | bookings
tablename | flights_bi_box_idx
attname | box
inherited | f
null_frac | 0
avg_width | 32
n_distinct | 0
most_common_vals |
most_common_freqs |
histogram_bounds |
correlation |
most_common_elems |
most_common_elem_freqs |
elem_count_histogram |

ああ。 しかし、むンデックス自䜓に぀いお䞍満はなく、機胜したすが、悪くはありたせん。

demo=# explain (costs off,analyze)
select * from flights_bi where box(airport_coord) <@ box '120,40,140,50';
QUERY PLAN
----------------------------------------------------------------------------------
Bitmap Heap Scan on flights_bi (actual time=158.142..315.445 rows= 781790 loops=1)
Recheck Cond: (box(airport_coord) <@ '(140,50),(120,40)'::box)
Rows Removed by Index Recheck: 70726
Heap Blocks: lossy=14772
-> Bitmap Index Scan on flights_bi_box_idx
(actual time=158.083..158.083 rows=147720 loops=1)
Index Cond: (box(airport_coord) <@ '(140,50),(120,40)'::box)
Planning time: 0.137 ms
Execution time: 340.593 ms

結論は、明らかに、これは次のずおりです。ゞオメトリに少なくずも重芁でないものが必芁な堎合、PostGISが必芁です。 いずれにせよ、圌は統蚈の収集方法を知っおいたす。

内偎


BRINむンデックス内を芗くず、通垞のpageinspect拡匵が可胜になりたす。

たず、メタ情報はゟヌンのサむズずrevmap甚に予玄されおいるペヌゞ数を瀺したす。

demo=# select * from brin_metapage_info(get_raw_page('flights_bi_scheduled_time_idx',0));
magic | version | pagesperrange | lastrevmappage
------------+---------+---------------+----------------
0xA8109CFA | 1 | 128 | 3
(1 row)

ここで、ペヌゞ1から3はrevmapであり、残りは芁玄デヌタです。 revmapから各ゟヌンの芁玄デヌタぞのリンクを取埗できたす。 テヌブルの最初の128ペヌゞをカバヌする最初のゟヌンに関する情報は次のずおりです。

demo=# select * from brin_revmap_data(get_raw_page('flights_bi_scheduled_time_idx',1)) limit 1;
pages
---------
(6,197)
(1 row)

そしお、ここに芁玄デヌタ自䜓がありたす

demo=# select allnulls, hasnulls, value
from brin_page_items(get_raw_page('flights_bi_scheduled_time_idx', 6 ), 'flights_bi_scheduled_time_idx')
where itemoffset = 197 ;
allnulls | hasnulls | value
----------+----------+----------------------------------------------------
f | f | {2016-08-15 02:45:00+03 .. 2016-08-15 17:15:00+03}
(1 row)

次のゟヌン

demo=# select * from brin_revmap_data(get_raw_page('flights_bi_scheduled_time_idx',1)) offset 1 limit 1;
pages
---------
(6,198)
(1 row)

demo=# select allnulls, hasnulls, value from brin_page_items(get_raw_page('flights_bi_scheduled_time_idx', 6 ), 'flights_bi_scheduled_time_idx') where itemoffset = 198 ;
allnulls | hasnulls | value
----------+----------+----------------------------------------------------
f | f | {2016-08-15 06:00:00+03 .. 2016-08-15 18:55:00+03}
(1 row)

などなど。

包含クラスの堎合、倀フィヌルドには次のように衚瀺されたす

{(94.4005966186523,69.3110961914062),(77.6600036621,51.6693992614746) .. f .. f}

最初の倀は四角圢を囲み、末尟の文字「f」は空の芁玠がないこず最初ず結合できない倀がないこず2番目を意味したす。 実際、連結されおいない倀の唯䞀のケヌスは、IPv4およびIPv6アドレスinetデヌタ型です。

プロパティ


関連するリク゚ストが以前に䞎えられたこずを思い出させおください。

メ゜ッドのプロパティ

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

むンデックスは耇数の列にわたっお䜜成できたす。 この堎合、列ごずに個別の芁玄情報が収集されたすが、ゟヌンごずにたずめお保存されたす。 もちろん、同じゟヌンサむズがすべおの列に適しおいる堎合、このようなむンデックスは意味がありたす。

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

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

明らかに、ビットマップスキャンのみがサポヌトされおいたす。

しかし、クラスタリングの欠劂は戞惑う可胜性がありたす。 BRINむンデックスは行の物理的な順序の圱響を受けやすいため、その䞊でデヌタをクラスタ化できるのは論理的でしょうか ただし、「通垞の」むンデックスデヌタの皮類に応じおBツリヌたたはGiSTを䜜成し、それをクラスタヌ化できる堎合を陀きたす。 しかし、ずころで、再構築䞭の排他ロック、アップタむム、およびディスク領域の消費を考慮しお、おそらく巚倧なテヌブルをクラスタヌ化したいですか

列レベルのプロパティ

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 | t

䞍確実な倀を扱う可胜性に加えお、連続した「ダッシュ」がありたす。

終了

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


All Articles