web-dev-qa-db-fra.com

Optimisation de requête Postgres (forçage d'une analyse d'index)

Ci-dessous ma requête. J'essaie de le faire utiliser un balayage d'index, mais il ne fera que seq scan.

À propos, la table metric_data a 130 millions de lignes. La table metrics a environ 2000 lignes.

metric_data colonnes de la table:

  metric_id integer
, t timestamp
, d double precision
, PRIMARY KEY (metric_id, t)

Comment puis-je obtenir cette requête pour utiliser mon index PRIMARY KEY?

SELECT
    S.metric,
    D.t,
    D.d
FROM metric_data D
INNER JOIN metrics S
    ON S.id = D.metric_id
WHERE S.NAME = ANY (ARRAY ['cpu', 'mem'])
  AND D.t BETWEEN '2012-02-05 00:00:00'::TIMESTAMP
              AND '2012-05-05 00:00:00'::TIMESTAMP;

EXPLIQUE:

Hash Join  (cost=271.30..3866384.25 rows=294973 width=25)
  Hash Cond: (d.metric_id = s.id)
  ->  Seq Scan on metric_data d  (cost=0.00..3753150.28 rows=29336784 width=20)
        Filter: ((t >= '2012-02-05 00:00:00'::timestamp without time zone)
             AND (t <= '2012-05-05 00:00:00'::timestamp without time zone))
  ->  Hash  (cost=270.44..270.44 rows=68 width=13)
        ->  Seq Scan on metrics s  (cost=0.00..270.44 rows=68 width=13)
              Filter: ((sym)::text = ANY ('{cpu,mem}'::text[]))
25
Jeff

À des fins de test, vous pouvez forcer l'utilisation de l'index en "désactivant" les analyses séquentielles, ce qui est préférable pour votre session actuelle uniquement:

SET enable_seqscan = OFF;

Détails dans le manuel ici. J'ai cité "désactiver", car vous ne pouvez pas réellement désactiver les analyses de table séquentielles. Mais toute autre option disponible est maintenant préférable pour Postgres. Cela prouvera que l'index multicolonne sur (metric_id, t)peut être utilisé - mais pas aussi efficace qu'un index sur la colonne de tête.

Vous obtiendrez probablement de meilleurs résultats en changeant l'ordre des colonnes dans votre PRIMARY KEY (et l'index utilisé pour l'implémenter derrière les rideaux) en (t, metric_id). Ou créez un index additional avec des colonnes inversées comme celle-là.

Vous n'avez normalement pas à forcer de meilleurs plans de requête par une intervention manuelle. Si définir enable_seqscan = OFF conduit à un meilleur plan much, quelque chose ne va probablement pas dans votre base de données. Considérez cette réponse connexe:

40
Erwin Brandstetter

Dans ce cas, vous ne pouvez pas forcer le balayage d'index car cela ne le rendra pas plus rapide.

Vous avez actuellement l'index sur metric_data (metric_id, t), mais le serveur ne peut pas tirer parti de cet index pour votre requête, car il doit pouvoir distinguer uniquement metric_data.t (sans metric_id), mais aucun index n'existe. Le serveur peut utiliser des sous-champs dans les index composés, mais seulement à partir du début. Par exemple, la recherche par metric_id pourra utiliser cet index. 

Si vous créez un autre index sur metric_data (t), votre requête utilisera cet index et fonctionnera beaucoup plus rapidement.

En outre, vous devez vous assurer que vous avez un index sur metrics (id).

1
mvp

Il semble que vous manquiez de contraintes FK appropriées:

CREATE TABLE metric_data
( metric_id integer
, t timestamp
, d double precision
, PRIMARY KEY (metric_id, t)
, FOREIGN KEY metrics_xxx_fk (metric_id) REFERENCES metrics (id)
)

et dans les métriques de tableau:

CREATE TABLE metrics
( id INTEGER PRIMARY KEY
...
);

Vérifiez également si vos statistiques sont suffisantes (et suffisamment fines, car vous avez l'intention de sélectionner 0,2% de la table metrics_data).

0
joop

Avez-vous essayé d'utiliser:

WHERE S.NAME = ANY (VALUES ('cpu'), ('mem'))).

comme ici

0
Gabriel Bastos