web-dev-qa-db-fra.com

Requête PostgreSQL très lente avec limite 1

Mes requêtes deviennent très lentes lorsque j'ajoute un limit 1.

J'ai une table object_values avec des valeurs horodatées pour les objets:

 timestamp |  objectID |  value
--------------------------------
 2014-01-27|       234 | ksghdf

Par objet, je veux obtenir la dernière valeur:

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC LIMIT 1;

(J'ai annulé la requête après plus de 10 minutes)

Cette requête est très lente lorsqu'il n'y a pas de valeurs pour un objectID donné (elle est rapide s'il y a des résultats). Si je supprime la limite, cela me dit presque instantanément qu'il n'y a pas de résultats:

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC;  
...  
Time: 0.463 ms

Une explication me montre que la requête sans limite utilise l'index, alors que la requête avec limit 1 n'utilise pas l'index:

requête lente:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC limit 1;  
QUERY PLAN`
----------------------------------------------------------------------------------------------------------------------------
Limit  (cost=0.00..2350.44 rows=1 width=126)
->  Index Scan Backward using object_values_timestamp on object_values  (cost=0.00..3995743.59 rows=1700 width=126)
     Filter: (objectID = 53708)`

Requête rapide:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC;
                                                  QUERY PLAN
--------------------------------------------------------------------------------------------------------------
 Sort  (cost=6540.86..6545.11 rows=1700 width=126)
   Sort Key: timestamp
   ->  Index Scan using object_values_objectID on working_hours_t  (cost=0.00..6449.65 rows=1700 width=126)
         Index Cond: (objectID = 53708)

Le tableau contient 44 884 559 lignes et 66 762 ID d'objet distincts.
J'ai des index distincts sur les deux champs: timestamp et objectID.
J'ai fait un vacuum analyze sur la table et j'ai réindexé la table.

De plus, la requête lente devient rapide lorsque je fixe la limite à 3 ou plus:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC limit 3;
                                                     QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Limit  (cost=6471.62..6471.63 rows=3 width=126)
   ->  Sort  (cost=6471.62..6475.87 rows=1700 width=126)
         Sort Key: timestamp
         ->  Index Scan using object_values_objectID on object_values  (cost=0.00..6449.65 rows=1700 width=126)
               Index Cond: (objectID = 53708)

En général, je suppose que cela a à voir avec le planificateur faisant des hypothèses erronées sur les coûts d'exécution et choisit donc un plan d'exécution plus lent.

Est-ce la vraie raison? Y a-t-il une solution à cela?

36
pat

Vous rencontrez un problème lié, je pense, au manque de statistiques sur les corrélations entre les lignes. Pensez à le signaler à pg-bugs pour référence s'il utilise la dernière version de Postgres.

L'interprétation que je suggérerais pour vos plans est la suivante:

  • limit 1 oblige Postgres à rechercher une seule ligne et, ce faisant, il suppose que votre object_id est suffisamment commun pour qu'il apparaisse assez rapidement dans un scan d'index.

    Sur la base des statistiques que vous avez réfléchies, il est probable qu'il faudra lire ~ 70 lignes en moyenne pour trouver une ligne qui convient; il ne se rend tout simplement pas compte que object_id et timestamp sont corrélés au point où il va réellement lire une grande partie de la table.

  • limit 3, en revanche, lui fait comprendre que c'est assez rare, donc il considère sérieusement (et finit par…) trier les n premiers 1700 lignes attendues avec le object_id vous voulez, car cela revient probablement moins cher.

    Par exemple, il peut savoir que la distribution de ces lignes est telle qu'elles sont toutes regroupées dans la même zone sur le disque.

  • aucune clause limit signifie qu'elle récupérera le 1700 de toute façon, donc elle va directement pour l'index sur object_id.

Solution, btw: ajoutez un index sur (object_id, timestamp) ou (object_id, timestamp desc).

31
Denis de Bernardy

Vous pouvez éviter ce problème en ajoutant une clause ORDER BY Inutile à la requête.

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp, objectID DESC limit 1;
39
Brendan Nee

J'ai commencé à avoir des symptômes similaires sur une table lourde de mises à jour, et ce qui était nécessaire dans mon cas était

analyze $table_name;

Dans ce cas, les statistiques devaient être actualisées, ce qui a ensuite corrigé les plans de requête lents qui se produisaient.
Documents de support: https://www.postgresql.org/docs/current/sql-analyze.html

1
Dan Tanner