BTree
Il mio problema qui è che l'indice BTree sarà enorme poiché afaict memorizzerà valori duplicati (ha anche, dal momento che non può presumere che la tabella sia ordinata fisicamente). Se il BTree è enorme finisco per dover leggere sia l'indice che le parti della tabella che anche l'indice indica ...
Non necessariamente - Avere un indice btree che 'copre' sarà il tempo di lettura più veloce, e se questo è tutto ciò che vuoi (cioè se puoi permetterti lo spazio extra), allora è la soluzione migliore.
BRIN
La mia comprensione è che posso avere un piccolo indice qui a spese della lettura di pagine inutili. Usare un piccolo pages_per_range
significa che l'indice è più grande (il che è un problema con BRIN poiché ho bisogno di leggere l'intero indice), avendo un grande pages_per_range
mezzo che leggerò molte pagine inutili.
Se non puoi permetterti l'overhead di archiviazione di un indice btree di copertura, BRIN è l'ideale per te, perché hai già un clustering (questo è fondamentale per BRIN per essere utile). Gli indici BRIN sono minuscoli , quindi è probabile che tutte le pagine siano in memoria se si sceglie un valore adatto di pages_per_range
.
Esiste una formula magica per trovare un buon valore di pages_per_range che tenga conto di questi compromessi?
Nessuna formula magica, ma inizia con pages_per_range
un po 'meno della dimensione media (in pagine) occupata dal a
valore medio . Probabilmente stai tentando di ridurre a icona: (numero di pagine BRIN scansionate) + (numero di pagine heap scansionate) per una query tipica. Cercare Heap Blocks: lossy=n
nel piano di esecuzione pages_per_range=1
e confrontare con altri valori per pages_per_range
, ad esempio vedere quanti blocchi heap non necessari vengono sottoposti a scansione.
GIN / GiST
Non sono sicuro che siano rilevanti qui poiché sono utilizzati principalmente per la ricerca full-text, ma ho anche sentito che sono bravi a gestire chiavi duplicate. Uno GIN
/ GiST
indice potrebbe aiutare qui?
Vale la pena prendere in considerazione GIN, ma probabilmente non GiST, tuttavia se il clustering naturale è davvero buono, allora BRIN sarà probabilmente una scommessa migliore.
Ecco un confronto di esempio tra i diversi tipi di indice per dati fittizi un po 'come il tuo:
tabella e indici:
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;
dimensioni della relazione:
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;
nome | dimensione | pagine | righe / pagina
: ----------------- | : ------ | ----: | --------:
pippo | 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 | 78.787
foo_brin_4 | 136 kB | 17 | 152941
che copre btree:
explain analyze select sum(b) from foo where a='a';
| PIANO DI QUERY |
| : ------------------------------------------------- -------------------------------------------------- ------------------------------------------- |
| Aggregato (costo = 3282.57..3282.58 righe = 1 larghezza = 8) (tempo effettivo = 45.942..45.942 righe = 1 anelli = 1) |
| -> Solo indice Scansione utilizzando foo_btree_covering su foo (costo = 0.43..3017.80 righe = 105907 larghezza = 4) (tempo effettivo = 0.038..27.286 righe = 100000 loop = 1) |
| Indice cond: (a = 'a' :: testo) |
| Heap Fetches: 0 |
| Tempo di pianificazione: 0,099 ms |
| Tempo di esecuzione: 45.968 ms |
semplice btree:
drop index foo_btree_covering;
explain analyze select sum(b) from foo where a='a';
| PIANO DI QUERY |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Aggregato (costo = 4064.57..4064.58 righe = 1 larghezza = 8) (tempo effettivo = 54.242..54.242 righe = 1 anelli = 1) |
| -> Scansione indice usando foo_btree su foo (costo = 0.43..3799.80 righe = 105907 larghezza = 4) (tempo effettivo = 0.037..33.084 righe = 100000 loop = 1) |
| Indice cond: (a = 'a' :: testo) |
| Tempo di pianificazione: 0,135 ms |
| Tempo di esecuzione: 54.280 ms |
BRIN pages_per_range = 4:
drop index foo_btree;
explain analyze select sum(b) from foo where a='a';
| PIANO DI QUERY |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Aggregato (costo = 21595.38..21595.39 righe = 1 larghezza = 8) (tempo effettivo = 52.455..52.455 righe = 1 anelli = 1) |
| -> Scansione heap bitmap su foo (costo = 888.78..21330.61 righe = 105907 larghezza = 4) (tempo effettivo = 2.738..31.967 righe = 100000 loop = 1) |
| Ricontrolla Cond: (a = 'a' :: testo) |
| Righe rimosse dall'indice Ricontrolla: 96 |
| Heap Blocks: lossy = 736 |
| -> Scansione indice bitmap su foo_brin_4 (costo = 0,00..862,30 righe = 105907 larghezza = 0) (tempo effettivo = 2.720..2.720 righe = 7360 loop = 1) |
| Indice cond: (a = 'a' :: testo) |
| Tempo di pianificazione: 0.101 ms |
| Tempo di esecuzione: 52.501 ms |
BRIN pages_per_range = 2:
drop index foo_brin_4;
explain analyze select sum(b) from foo where a='a';
| PIANO DI QUERY |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Aggregato (costo = 21659.38..21659.39 righe = 1 larghezza = 8) (tempo effettivo = 53.971..53.971 righe = 1 anelli = 1) |
| -> Scansione heap bitmap su foo (costo = 952.78..21394.61 righe = 105907 larghezza = 4) (tempo effettivo = 5.286..33.492 righe = 100000 loop = 1) |
| Ricontrolla Cond: (a = 'a' :: testo) |
| Righe rimosse dall'indice Ricontrolla: 96 |
| Heap Blocks: lossy = 736 |
| -> Scansione indice bitmap su foo_brin_2 (costo = 0,00..926,30 righe = 105907 larghezza = 0) (tempo effettivo = 5,275..5,275 righe = 7360 loop = 1) |
| Indice cond: (a = 'a' :: testo) |
| Tempo di pianificazione: 0,095 ms |
| Tempo di esecuzione: 54.016 ms |
GIN:
drop index foo_brin_2;
explain analyze select sum(b) from foo where a='a';
| PIANO DI QUERY |
| : ------------------------------------------------- -------------------------------------------------- ------------------------------ |
| Aggregato (costo = 21687.38..21687.39 righe = 1 larghezza = 8) (tempo effettivo = 55.331..55.331 righe = 1 anelli = 1) |
| -> Scansione heap bitmap su foo (costo = 980.78..21422.61 righe = 105907 larghezza = 4) (tempo effettivo = 12.377..33.956 righe = 100000 loop = 1) |
| Ricontrolla Cond: (a = 'a' :: testo) |
| Blocchi heap: esatto = 736 |
| -> Scansione indice bitmap su foo_gin (costo = 0,00..954,30 righe = 105907 larghezza = 0) (tempo effettivo = 12.271..12.271 righe = 100000 loop = 1) |
| Indice cond: (a = 'a' :: testo) |
| Tempo di pianificazione: 0,118 ms |
| Tempo di esecuzione: 55.366 ms |
dbfiddle qui