BTree
Mój problem polega na tym, że indeks BTree będzie ogromny, ponieważ będzie przechowywał zduplikowane wartości (ma też, ponieważ nie może zakładać, że tabela jest fizycznie posortowana). Jeśli BTree jest ogromny, muszę przeczytać zarówno indeks, jak i te części tabeli, które indeks wskazuje również ...
Niekoniecznie - posiadanie indeksu btree „zakrywającego” będzie najszybszym czasem odczytu, a jeśli to wszystko, czego chcesz (tj. Jeśli możesz sobie pozwolić na dodatkowe miejsce), to jest to najlepszy wybór.
BRIN
Rozumiem, że mogę mieć tutaj mały indeks kosztem czytania bezużytecznych stron. Użycie małej pages_per_range
oznacza, że indeks jest większy (co jest problemem w BRIN, ponieważ muszę przeczytać cały indeks), posiadanie dużego pages_per_range
oznacza, że przeczytam wiele bezużytecznych stron.
Jeśli nie możesz sobie pozwolić na obciążenie magazynu indeksem pokrywającym btree, BRIN jest dla Ciebie idealny, ponieważ masz już klastrowanie ( jest to bardzo ważne, aby BRIN był przydatny). Indeksy BRIN są małe , więc wszystkie strony prawdopodobnie będą w pamięci, jeśli wybierzesz odpowiednią wartość pages_per_range
.
Czy istnieje magiczna formuła, aby znaleźć dobrą wartość strony_per_range, która uwzględnia te kompromisy?
Brak magicznej formuły, ale zacznij od pages_per_range
nieco mniejszego niż średni rozmiar (w stronach) zajmowany przez średnią a
wartość. Prawdopodobnie próbujesz zminimalizować: (liczbę zeskanowanych stron BRIN) + (liczbę zeskanowanych stron sterty) dla typowego zapytania. Poszukaj Heap Blocks: lossy=n
w planie wykonania pages_per_range=1
i porównaj z innymi wartościami pages_per_range
- tzn. Sprawdź, ile skanowanych jest niepotrzebnych bloków sterty.
GIN / GiST
Nie jestem pewien, czy są one istotne tutaj, ponieważ są one najczęściej używane do wyszukiwania pełnotekstowego, ale słyszę również, że dobrze radzą sobie z duplikatami kluczy. Czy pomoże tu albo indeks GIN
/ GiST
?
Warto rozważyć GIN, ale prawdopodobnie nie GiST - jednak jeśli naturalne grupowanie jest naprawdę dobre, to BRIN będzie prawdopodobnie lepszym wyborem.
Oto przykładowe porównanie różnych typów indeksów dla danych fikcyjnych, trochę podobnych do twojego:
tabela i indeksy:
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;
rozmiary relacji:
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;
nazwa | rozmiar | strony | wiersze / strona
: ----------------- | : ------ | ----: | --------:
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
obejmujące btree:
explain analyze select sum(b) from foo where a='a';
| PLAN ZAPYTANIA |
| : ------------------------------------------------- -------------------------------------------------- ------------------------------------------- |
| Agregat (koszt = 3282,57..3282,58 wierszy = 1 szerokość = 8) (rzeczywisty czas = 45,942..45,942 wierszy = 1 pętla = 1) |
| -> Skanuj tylko indeks używając foo_btree_covering na foo (koszt = 0,43..3017.80 wierszy = 105907 szerokość = 4) (czas rzeczywisty = 0,038..27.286 wierszy = 100000 pętli = 1) |
| Index Cond: (a = „a” :: text) |
| Sterty pobrania: 0 |
| Czas planowania: 0,099 ms |
| Czas wykonania: 45,968 ms |
zwykły btree:
drop index foo_btree_covering;
explain analyze select sum(b) from foo where a='a';
| PLAN ZAPYTANIA |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Agregat (koszt = 4064,57..4064,58 wierszy = 1 szerokość = 8) (rzeczywisty czas = 54.242..54.242 wierszy = 1 pętli = 1) |
| -> Skanowanie indeksu za pomocą foo_btree na foo (koszt = 0,43..3799.80 wierszy = 105907 szerokość = 4) (rzeczywisty czas = 0,037..33,084 wierszy = 100000 pętli = 1) |
| Index Cond: (a = „a” :: text) |
| Czas planowania: 0,135 ms |
| Czas wykonania: 54,280 ms |
BRIN pages_per_range = 4:
drop index foo_btree;
explain analyze select sum(b) from foo where a='a';
| PLAN ZAPYTANIA |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Agregat (koszt = 21595.38..21595.39 wierszy = 1 szerokość = 8) (czas rzeczywisty = 52.455..52.455 wierszy = 1 pętla = 1) |
| -> Skanowanie sterty bitmap na foo (koszt = 888,78..21330,61 wierszy = 105907 szerokość = 4) (rzeczywisty czas = 2,738..31,967 wierszy = 100000 pętli = 1) |
| Ponownie sprawdź Cond: (a = 'a' :: text) |
| Wiersze usunięte przez sprawdzenie indeksu: 96 |
| Bloki sterty: straty = 736 |
| -> Skanowanie indeksu bitmap na foo_brin_4 (koszt = 0,00..862,30 wierszy = 105907 szerokość = 0) (rzeczywisty czas = 2,720..2,720 wierszy = 7360 pętli = 1) |
| Index Cond: (a = „a” :: text) |
| Czas planowania: 0,101 ms |
| Czas realizacji: 52,501 ms |
BRIN pages_per_range = 2:
drop index foo_brin_4;
explain analyze select sum(b) from foo where a='a';
| PLAN ZAPYTANIA |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Agregat (koszt = 21659,38..21659.39 wierszy = 1 szerokość = 8) (czas rzeczywisty = 53.971..53.971 wierszy = 1 pętla = 1) |
| -> Skanowanie sterty bitmap na foo (koszt = 952,78..21394.61 wierszy = 105907 szerokość = 4) (rzeczywisty czas = 5.286..33.492 wierszy = 100000 pętli = 1) |
| Ponownie sprawdź Cond: (a = 'a' :: text) |
| Wiersze usunięte przez sprawdzenie indeksu: 96 |
| Bloki sterty: straty = 736 |
| -> Skanowanie indeksu bitmap na foo_brin_2 (koszt = 0,00..926,30 wierszy = 105907 szerokość = 0) (rzeczywisty czas = 5,275..5,275 wierszy = 7360 pętli = 1) |
| Index Cond: (a = „a” :: text) |
| Czas planowania: 0,095 ms |
| Czas wykonania: 54,016 ms |
GIN:
drop index foo_brin_2;
explain analyze select sum(b) from foo where a='a';
| PLAN ZAPYTANIA |
| : ------------------------------------------------- -------------------------------------------------- ------------------------------ |
| Agregat (koszt = 21687,38..21687.39 wierszy = 1 szerokość = 8) (czas rzeczywisty = 55.331..55.331 wierszy = 1 pętla = 1) |
| -> Skanowanie sterty bitmap na foo (koszt = 980,78..21422,61 wierszy = 105907 szerokość = 4) (rzeczywisty czas = 12,377..33.956 wierszy = 100000 pętli = 1) |
| Ponownie sprawdź Cond: (a = 'a' :: text) |
| Bloki stert: dokładne = 736 |
| -> Skanowanie indeksu bitmap na foo_gin (koszt = 0,00..954,30 wierszy = 105907 szerokość = 0) (rzeczywisty czas = 12.271..12.271 wierszy = 100000 pętli = 1) |
| Index Cond: (a = „a” :: text) |
| Czas planowania: 0,118 ms |
| Czas wykonania: 55,366 ms |
dbfiddle tutaj