web-dev-qa-db-fra.com

La couverture des index dans PostgreSQL aide-t-elle à joindre des colonnes?

J'ai beaucoup de tableaux qui ressemblent vaguement à ceci:

CREATE TABLE table1(id INTEGER PRIMARY KEY, t1c1 INTEGER, t1c2 INTEGER);
CREATE TABLE table2(id INTEGER PRIMARY KEY, t1 INTEGER REFERENCES table1(id), t2c1 INTEGER);

Et je fais beaucoup de jointures où j'essaie de filtrer sur la table jointe pour obtenir des éléments de la première table, comme ceci:

SELECT t1c1
FROM table1
JOIN table2 ON table2.t1 = table1.id
WHERE t2c1 = 42;

Quand je vais écrire des index pour une table, je regarde les colonnes qui sont utilisées dans la clause WHERE et je construis des index pour les satisfaire. Donc, pour cette requête, je finirais par écrire un index comme celui-ci:

CREATE INDEX ON table2 (t2c1);

Et cet index est au moins éligible pour une utilisation dans cette requête.

Ma question est que si j'écris un index comme celui-ci:

CREATE INDEX ON table2 (t2c1, t1);

L'index sera-t-il utilisé comme index de couverture pour aider le JOIN dans la requête ci-dessus? Dois-je changer ma stratégie d'écriture d'index pour couvrir les colonnes de clés étrangères?

8
ldrg

L'index sera-t-il utilisé comme index de couverture pour aider le JOIN dans la requête ci-dessus?

Ça dépend. Postgres a scans "index uniquement" as ( méthode d'accès à l'index , il n'y a pas d '"indices de couverture" en soi - jusqu'à Postgres 10.

Commençant par Postgres 11 true couvrant les index avec des colonnes INCLUDE sont disponibles. Entrée de blog de Michael Paquier présentant la fonctionnalité:

Réponse associée avec un exemple de code:

Cela dit, l'index CREATE INDEX ON table2 (t2c1, t1); est parfaitement logique pour la requête que vous démontrez. Il peut être utilisé pour une analyse d'index uniquement si des conditions préalables supplémentaires sont remplies, ou il peut être utilisé dans une analyse d'index bitmap ou une analyse d'index simple. En relation:

JOIN conditions et WHERE conditions sont presque complètement équivalentes dans Postgres. Ils peuvent certainement utiliser les index de la même manière. Vous pouvez réécrire votre requête

SELECT t1c1
FROM   table1
JOIN   table2 ON table2.t1 = table1.id
WHERE  t2c1 = 42;

Avec cet équivalent:

SELECT t1c1
FROM   table1 CROSS JOIN table2
WHERE  table2.t1 = table1.id
AND    table2.t2c1 = 42;

La première forme est évidemment préférable, cependant. Plus facile à lire.

Pourquoi "presque" équivalent?

15

L'index sera-t-il utilisé comme index de couverture pour aider le JOIN dans la requête ci-dessus? Dois-je changer ma stratégie d'écriture d'index pour couvrir les colonnes de clés étrangères?

Peu probable dans la requête ci-dessus. Il s'agit d'un problème complexe trompeur avec les résultats basés sur les estimations et la sélectivité des deux conditions,

  • table2.t1 = table1.id
  • t2c1 = 42

Essentiellement, vous voulez lancer les deux environnements (nombre de lignes) pour que les deux conditions aient plus ou moins de sélectivité. Et si vous obtenez une boucle imbriquée, vous souhaitez augmenter le montant brut jusqu'à ce que ce ne soit plus la méthode de jointure la plus viable.

CREATE TABLE table1(
   id INTEGER PRIMARY KEY,
   t1c1 INTEGER,
   t1c2 INTEGER
);
INSERT INTO table1(id, t1c1, t1c2)
  SELECT x,x,x FROM generate_series(1,1000)
  AS gs(x);

CREATE TABLE table2(
  id INTEGER PRIMARY KEY,
  t1 INTEGER REFERENCES table1(id),
  t2c1 INTEGER
);
INSERT INTO table2(id, t1, t2c1)
SELECT x,1+x%1000,x%50 FROM generate_series(1,1e6)
  AS gs(x);

EXPLAIN ANALYZE
  SELECT t1c1
  FROM table1
  JOIN table2 ON table2.t1 = table1.id
  WHERE t2c1 = 42;

Maintenant, vérifiez le plan.

Créez maintenant l'index composé,

CREATE INDEX ON table2 (t2c1, t1);
VACUUM FULL ANALYZE table1;
VACUUM FULL ANALYZE table2;

Et vérifiez à nouveau le plan,

EXPLAIN ANALYZE
  SELECT t1c1
  FROM table1
  JOIN table2 ON table2.t1 = table1.id
  WHERE t2c1 = 42;

Vous pouvez déposer les clés et autres pour trouver la forme qu'il préfère

CREATE INDEX ON table2 (t1, t2c1);

ou

CREATE INDEX ON table2 (t2c1, t1);

En fin de compte, même si cela représente beaucoup de travail, je suggère de commencer par

CREATE INDEX ON table2 (t1);
CREATE INDEX ON table2 (t2c1);

Et l'optimisation uniquement si cela ne suffit pas.

Vous pouvez également désactiver des options de planificateur spécifiques pour voir si un autre plan vraiment est plus rapide ou plus lent, puis chercher à résoudre ce problème, mais cela peut également demander beaucoup de travail.

3
Evan Carroll