Analyses d'index lentes dans une grande table

12

En utilisant PostgreSQL 9.2, j'ai des problèmes avec les requêtes lentes sur une table relativement grande (200+ millions de lignes). Je n'essaye rien de fou, j'ajoute juste des valeurs historiques. Vous trouverez ci-dessous la requête et la sortie du plan de requête.

Ma disposition de table:

                                   Table "public.energy_energyentry"
  Column   |           Type           |                            Modifiers
-----------+--------------------------+-----------------------------------------------------------------
 id        | integer                  | not null default nextval('energy_energyentry_id_seq'::regclass)
 prop_id   | integer                  | not null
 timestamp | timestamp with time zone | not null
 value     | double precision         | not null
Indexes:
    "energy_energyentry_pkey" PRIMARY KEY, btree (id)
    "energy_energyentry_prop_id" btree (prop_id)
    "energy_energyentry_prop_id_timestamp_idx" btree (prop_id, "timestamp")
Foreign-key constraints:
    "energy_energyentry_prop_id_fkey" FOREIGN KEY (prop_id) REFERENCES gateway_peripheralproperty(id) DEFERRABLE INITIALLY DEFERRED

Les données vont du 01/01/2012 jusqu'à maintenant, avec de nouvelles données constamment ajoutées. Il existe environ 2,2 k valeurs distinctes dans la prop_idclé étrangère, réparties uniformément.

Je remarque que les estimations de ligne ne sont pas loin, mais les estimations de coût semblent plus grandes d'un facteur 4x. Ce n'est probablement pas un problème, mais est-ce que je pourrais y faire quelque chose?

Je m'attends à ce que l'accès au disque soit le problème, car la table n'est pas en mémoire tout le temps.

EXPLAIN ANALYZE 
SELECT SUM("value") 
FROM "energy_energyentry" 
WHERE 
  "prop_id"=82411 
  AND "timestamp">'2014-06-11' 
  AND "timestamp"<'2014-11-11'
;
 Aggregate  (cost=214481.45..214481.46 rows=1 width=8) (actual time=51504.814..51504.814 rows=1 loops=1)
   ->  Index Scan using energy_energyentry_prop_id_timestamp_idx on  energy_energyentry (cost=0.00..214434.08 rows=18947 width=8) (actual time=136.030..51488.321 rows=13578 loops=1)
         Index Cond: ((prop_id = 82411) AND ("timestamp" > '2014-06-11 00:00:00+00'::timestamp with time zone) AND ("timestamp" < '2014-11-11 00:00:00+00'::timestamp with time zone))
 Total runtime: 51504.841 ms

Des suggestions sur la façon d'accélérer cela?
Je suis également d'accord pour entendre que je n'ai rien fait de bizarre.

Exelian
la source
1
Veuillez nous dire à quoi ressemble votre table, quels index elle contient et la répartition des données.
Colin 't Hart du
J'ai ajouté les informations supplémentaires que vous avez demandées. Je ne sais pas si j'ai raté quelque chose.
Exelian
2
Étrange: Votre explication analyse les émissions prop_time_idx, mais la définition de la table s'affiche entry_prop_id_timestamp_idx. Est-ce le même indice? S'il-vous-plaît, réparez.
Colin 't Hart du
Si vous faites référence par `` les estimations de coûts semblent être un facteur 4x plus grand '' au fait que les chiffres des coûts sont environ 4 fois ceux du temps réel , alors veuillez noter que les deux n'ont rien à voir l'un avec l'autre. Le coût n'est qu'une estimation, aidant l'optimiseur de requêtes à choisir le meilleur plan. En dehors de ce contexte, il s'agit généralement d'une valeur dénuée de sens.
dezso
1
Combien de pourcentages du tableau représente votre plage de dates (sans tenir compte des valeurs pour prop)? Si seulement un petit pourcentage, un indice ("timestamp", prop)serait peut-être mieux. Plusieurs index avec les mêmes colonnes de tête ( propdans votre cas) sont également souvent redondants.
Colin 't Hart du

Réponses:

10

Votre table est grande , tout comme tout index couvrant toute la table. En admettant que:

  • seules les nouvelles données (avec timestamp = now()) sont entrées
  • les lignes existantes ne sont ni modifiées ni supprimées.
  • vous disposez de données depuis le 01/01/2012 mais les requêtes concernent principalement l'année en cours (?)

Je suggérerais un index partiel, multi-colonnes (couvrant!) :

CREATE INDEX ON energy_energyentry (prop_id, "timestamp", value)
WHERE "timestamp" >= '2014-01-01 0:0';  -- adapt to your needs

N'incluez que la plage horaire qui est interrogée régulièrement. L'efficacité se détériore avec le temps avec de nouvelles entrées. Recréez l'index de temps en temps. (Vous devrez peut-être adapter vos requêtes.) Voir la réponse associée ci-dessous.

La dernière valeur de colonne n'est incluse que pour obtenir des analyses d'index uniquement . Le réglage agressif du vide automatique peut aider en gardant la carte de visibilité à jour, comme @jjanes déjà mentionné .

L'index partiel devrait s'insérer plus facilement dans la RAM et y rester plus longtemps.

Vous devrez peut-être inclure cette WHEREcondition dans les requêtes pour que le planificateur comprenne que l'index est applicable à la requête, comme:

SELECT sum(value) AS sum_value
FROM   energy_energyentry
WHERE  prop_id = 82411 
AND   "timestamp" > '2014-06-11 0:0' 
AND   "timestamp" < '2014-11-11 0:0'
AND   "timestamp" >= '2014-01-01 0:0'; -- seems redundant, but may be needed

Étant donné que votre requête résume un grand nombre de lignes ( rows=13578), cela va prendre un certain temps, même avec une analyse d'index uniquement. Cependant, cela ne devrait pas être proche de 50 secondes. Moins d'une seconde sur n'importe quel matériel à moitié décent.

Associé (mais ignorez CLUSTERet FILLFACTOR, les deux ne sont pas pertinents si vous pouvez obtenir des analyses d'index uniquement) :

En plus: étant
donné que vous disposez actuellement d'un indice (prop_id, "timestamp"), l'index supplémentaire (prop_id)peut coûter plus cher qu'il n'en vaut la peine:

Erwin Brandstetter
la source
Maintenant que Postgres prend en charge les index BRIN, cela serait-il utile ici? Je prévois de stocker environ 140 millions de lignes de données sur PostgreSQL, BRIN est-il le bon index à utiliser pour une table aussi grande?
Arya
2

Si vous activez l'index (prop_id, "timestamp", "value"), il peut alors utiliser une analyse d'index uniquement pour calculer la valeur sans jamais visiter la table. Cela pourrait économiser beaucoup d'accès aléatoire au disque.

Pour tirer le meilleur parti, vous devez être agressif à propos de l'aspiration de la table. Les paramètres autovac par défaut ne sont pas suffisamment agressifs pour les tables d'insertion uniquement sur lesquelles vous souhaitez prendre en charge efficacement les analyses d'index uniquement.

jjanes
la source
Ajouter de la valeur pourrait être intéressant, je vais voir si cela va accélérer les choses. Avez-vous des suggestions de paramètres d'aspiration ou de documentation à consulter?
Exelian