Faisons quelques hypothèses:
J'ai une table qui ressemble à ceci:
a | b
---+---
a | -1
a | 17
...
a | 21
c | 17
c | -3
...
c | 22
Faits sur mon set:
La taille de la table entière est ~ 10 10 lignes.
J'ai ~ 100k lignes avec une valeur
a
dans la colonnea
, similaire pour d'autres valeurs (par exemplec
).Cela signifie ~ 100k valeurs distinctes dans la colonne 'a'.
La plupart de mes requêtes liront toutes ou la plupart des valeurs pour une valeur donnée dans un, par exemple
select sum(b) from t where a = 'c'
.Le tableau est écrit de telle manière que les valeurs consécutives soient physiquement proches (soit il est écrit dans l'ordre, soit nous supposons qu'il a
CLUSTER
été utilisé sur ce tableau et cette colonnea
).Le tableau est rarement, voire jamais mis à jour, nous ne nous préoccupons que de la vitesse de lecture.
Le tableau est relativement étroit (disons ~ 25 octets par tuple, + 23 octets de surcharge).
Maintenant, la question est, quel type d'index dois-je utiliser? Ma compréhension est:
BTree Mon problème ici est que l'index BTree sera énorme car pour autant que je sache, il stockera des valeurs en double (il le doit, 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 vers lesquelles l'index pointe. (Nous pouvons utiliser
fillfactor = 100
pour diminuer un peu la taille de l'index.)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 grospages_per_range
signifie que je vais lire beaucoup de pages inutiles. Existe-t-il une formule magique pour trouver une bonne valeurpages_per_range
qui tient compte de ces compromis?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. Un
GIN
ou unGiST
index aiderait-il ici?
Une autre question est la suivante: Postgres utilisera-t-il le fait qu'un tableau est CLUSTER
édité (en supposant qu'il n'y ait pas de mises à jour) dans le planificateur de requêtes (par exemple en recherchant binaire les pages de début / fin pertinentes)? Quelque peu lié, puis-je simplement stocker toutes mes colonnes dans un BTree et supprimer complètement la table (ou obtenir quelque chose d'équivalent, je pense que ce sont des index clusterisés dans SQL Server)? Y a-t-il un indice hybride BTree / BRIN qui pourrait aider ici?
Je préfère éviter d'utiliser des tableaux pour stocker mes valeurs car ma requête sera moins lisible de cette façon (je comprends que cela réduirait le coût des 23 octets par surcharge de tuple en réduisant le nombre de tuples).
Réponses:
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.
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
.Pas de formule magique, mais commencez avec
pages_per_range
un peu moins que la taille moyenne (en pages) occupée par laa
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. RecherchezHeap Blocks: lossy=n
dans le plan d'exécutionpages_per_range=1
et comparez avec d'autres valeurs pourpages_per_range
- c.-à-d. Voyez combien de blocs de tas inutiles sont analysés.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:
tailles de relation:
couvrant btree:
btree ordinaire:
BRIN pages_per_range = 4:
BRIN pages_per_range = 2:
GIN:
dbfiddle ici
la source
Bitmap Index Scan
comme signifiant «lire l'index de brin entier» mais c'est peut-être la mauvaise lecture. OracleCOMPRESS
ressemble à quelque chose qui serait utile ici car cela réduirait la taille de l'arbre B, mais je suis coincé avec pg!Outre btree et brin qui semblent les options les plus sensées, quelques autres options exotiques qui méritent d'être étudiées - elles pourraient être utiles ou non dans votre cas:
INCLUDE
index . Ils seront - espérons-le - dans la prochaine version majeure (10) de Postgres, vers septembre 2017. Un index sur(a) INCLUDE (b)
a la même structure qu'un index sur(a)
mais inclut dans les pages feuilles, toutes les valeurs deb
(mais non ordonnées). Ce qui signifie que vous ne pouvez pas l'utiliser par exemple pourSELECT * FROM t WHERE a = 'a' AND b = 2 ;
. L'index peut être utilisé, mais alors qu'un(a,b)
index trouvera les lignes correspondantes avec une seule recherche, l'index include devra passer par les valeurs (peut-être 100K comme dans votre cas) qui correspondenta = 'a'
et vérifier leb
valeurs.En revanche, l'index est légèrement moins large que l'
(a,b)
index et vous n'avez pas besoin de l'ordreb
pour que votre requête soit calculéeSUM(b)
. Vous pourriez aussi avoir par exemple(a) INCLUDE (b,c,d)
qui peut être utilisé pour des requêtes similaires aux vôtres qui se regroupent sur les 3 colonnes.Index filtrés (partiels) . Une suggestion qui pourrait sonne un peu fou * dans un premier temps :
Un index pour chaque
a
valeur. Dans votre cas, environ 100 000 index. Bien que cela semble beaucoup, considérez que chaque index sera très petit, à la fois en taille (nombre de lignes) et en largeur (car il ne stockera que desb
valeurs). Dans tous les autres aspects cependant, il (les 100K index ensemble) agira comme un index b-tree(a,b)
tout en utilisant l'espace d'un(b)
index.L'inconvénient est que vous devrez les créer et les maintenir vous-même, chaque fois qu'une nouvelle valeur de
a
est ajoutée dans le tableau. Étant donné que votre table est plutôt stable, sans beaucoup (ou pas) d'insertions / mises à jour, cela ne semble pas être un problème.Tableaux récapitulatifs. Étant donné que le tableau est plutôt stable, vous pouvez toujours créer et remplir un tableau récapitulatif avec les agrégats les plus courants dont vous aurez besoin (
sum(b), sum(c), sum(d), avg(b), count(distinct b)
, etc.). Il sera petit (seulement 100 000 lignes) et ne devra être rempli qu'une seule fois et mis à jour uniquement lorsque des lignes seront insérées / mises à jour / supprimées sur la table principale.*: idée copiée de cette société qui gère 10 millions d'index dans leur système de production: The Heap: Running 10 Million Postgresql Indexes in Production (and counting) .
la source
SUM
comme exemple, mais dans la pratique mes requêtes ne peuvent pas être précalculées (elles ressemblent plus àselect ... from t where a = '?' and ??
wjere??
serait une autre condition définie par l'utilisateur.??
c'est;)DO
déclaration dans cette réponse connexe .