BTree
Mon problème ici est que l'index BTree sera énorme, car il stockera des valeurs en double (il en a aussi, car il ne peut pas supposer que la table est triée physiquement). Si le BTree est énorme, je finis par devoir lire à la fois l'index et les parties du tableau que l'index pointe aussi ...
Pas nécessairement - Avoir un index btree qui «couvre» sera le temps de lecture le plus rapide, et si c'est tout ce que vous voulez (c'est-à-dire si vous pouvez vous permettre le stockage supplémentaire), alors c'est votre meilleur pari.
BRIN
Je crois comprendre que je peux avoir un petit index ici au détriment de la lecture de pages inutiles. Utiliser un petit pages_per_range
signifie que l'index est plus grand (ce qui est un problème avec BRIN car j'ai besoin de lire tout l'index), avoir un gros pages_per_range
signifie que je vais lire beaucoup de pages inutiles.
Si vous ne pouvez pas vous permettre les frais de stockage d'un index btree couvrant, BRIN est idéal pour vous, car vous avez déjà un clustering en place (cela est crucial pour que BRIN soit utile). Les index BRIN sont minuscules , donc toutes les pages sont susceptibles d'être en mémoire si vous choisissez une valeur appropriée pour pages_per_range
.
Existe-t-il une formule magique pour trouver une bonne valeur de pages_per_range qui prend en compte ces compromis?
Pas de formule magique, mais commencez avec pages_per_range
un peu moins que la taille moyenne (en pages) occupée par la a
valeur moyenne . Vous essayez probablement de minimiser: (nombre de pages BRIN analysées) + (nombre de pages de segment analysées) pour une requête standard. Recherchez Heap Blocks: lossy=n
dans le plan d'exécution pages_per_range=1
et comparez avec d'autres valeurs pour pages_per_range
- c.-à-d. Voyez combien de blocs de tas inutiles sont analysés.
GIN / GiST
Je ne suis pas sûr que ceux-ci soient pertinents ici car ils sont principalement utilisés pour la recherche en texte intégral, mais j'entends également qu'ils sont bons pour traiter les clés en double. Est-ce qu'un index GIN
/ GiST
aiderait ici?
GIN peut être utile, mais probablement pas GiST - cependant, si le clustering naturel est vraiment bon, alors BRIN sera probablement un meilleur pari.
Voici un exemple de comparaison entre les différents types d'index pour des données factices un peu comme les vôtres:
table et index:
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;
tailles de relation:
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;
nom | taille | pages | lignes / page
: ----------------- | : ------ | ----: | --------:
foo | 149 Mo | 19118 | 135
foo_btree_covering | 56 Mo | 7132 | 364
foo_btree | 56 Mo | 7132 | 364
foo_gin | 2928 kB | 366 | 7103
foo_brin_2 | 264 kB | 33 | 78787
foo_brin_4 | 136 kB | 17 | 152941
couvrant btree:
explain analyze select sum(b) from foo where a='a';
| PLAN DE DEMANDE |
| : ------------------------------------------------- -------------------------------------------------- ------------------------------------------- |
| Agrégat (coût = 3282,57..3282,58 lignes = 1 largeur = 8) (temps réel = 45,942..45,942 lignes = 1 boucles = 1) |
| -> Indexation uniquement en utilisant foo_btree_covering sur foo (coût = 0,43..3017,80 lignes = 105907 largeur = 4) (temps réel = 0,038..27,286 lignes = 100000 boucles = 1) |
| Index Cond: (a = 'a' :: texte) |
| Récupérations de tas: 0 |
| Temps de planification: 0,099 ms |
| Temps d'exécution: 45,968 ms |
btree ordinaire:
drop index foo_btree_covering;
explain analyze select sum(b) from foo where a='a';
| PLAN DE DEMANDE |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Agrégat (coût = 4064,57..4064,58 lignes = 1 largeur = 8) (temps réel = 54,242..54,242 lignes = 1 boucles = 1) |
| -> Index Scan en utilisant foo_btree sur foo (coût = 0,43..3799,80 lignes = 105907 largeur = 4) (temps réel = 0,037..33,084 lignes = 100000 boucles = 1) |
| Index Cond: (a = 'a' :: texte) |
| Temps de planification: 0,135 ms |
| Temps d'exécution: 54,280 ms |
BRIN pages_per_range = 4:
drop index foo_btree;
explain analyze select sum(b) from foo where a='a';
| PLAN DE DEMANDE |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Agrégat (coût = 21595,38..21595,39 lignes = 1 largeur = 8) (temps réel = 52,455..52,455 lignes = 1 boucles = 1) |
| -> Bitmap Heap Scan on foo (coût = 888,78..21330,61 lignes = 105907 largeur = 4) (temps réel = 2,738..31,967 lignes = 100000 boucles = 1) |
| Vérifiez à nouveau Cond: (a = 'a' :: text) |
| Lignes supprimées par la vérification de l'index: 96 |
| Blocs de tas: lossy = 736 |
| -> Bitmap Index Scan sur foo_brin_4 (coût = 0,00..862,30 lignes = 105907 largeur = 0) (temps réel = 2,720..2,720 lignes = 7360 boucles = 1) |
| Index Cond: (a = 'a' :: texte) |
| Temps de planification: 0,101 ms |
| Temps d'exécution: 52,501 ms |
BRIN pages_per_range = 2:
drop index foo_brin_4;
explain analyze select sum(b) from foo where a='a';
| PLAN DE DEMANDE |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| Agrégat (coût = 21659,38..21659,39 lignes = 1 largeur = 8) (temps réel = 53,971..53,971 lignes = 1 boucles = 1) |
| -> Bitmap Heap Scan on foo (coût = 952,78..21394,61 lignes = 105907 largeur = 4) (temps réel = 5,286..33,492 lignes = 100000 boucles = 1) |
| Vérifiez à nouveau Cond: (a = 'a' :: text) |
| Lignes supprimées par la vérification de l'index: 96 |
| Blocs de tas: lossy = 736 |
| -> Bitmap Index Scan sur foo_brin_2 (coût = 0,00..926,30 lignes = 105907 largeur = 0) (temps réel = 5,275..5,275 lignes = 7360 boucles = 1) |
| Index Cond: (a = 'a' :: texte) |
| Temps de planification: 0,095 ms |
| Temps d'exécution: 54,016 ms |
GIN:
drop index foo_brin_2;
explain analyze select sum(b) from foo where a='a';
| PLAN DE DEMANDE |
| : ------------------------------------------------- -------------------------------------------------- ------------------------------ |
| Agrégat (coût = 21687,38..21687,39 lignes = 1 largeur = 8) (temps réel = 55,331..55,331 lignes = 1 boucles = 1) |
| -> Bitmap Heap Scan sur foo (coût = 980,78..21422,61 lignes = 105907 largeur = 4) (temps réel = 12,377..33,956 lignes = 100000 boucles = 1) |
| Vérifiez à nouveau Cond: (a = 'a' :: text) |
| Blocs de tas: exact = 736 |
| -> Bitmap Index Scan sur foo_gin (coût = 0,00..954,30 lignes = 105907 largeur = 0) (temps réel = 12,271..12,271 lignes = 100000 boucles = 1) |
| Index Cond: (a = 'a' :: texte) |
| Temps de planification: 0,118 ms |
| Temps d'exécution: 55,366 ms |
dbfiddle ici