2010-09-17 7 views
3

J'essaie d'optimiser une requête lente générée par l'ORM de Django. C'est une requête plusieurs-à-plusieurs. Il faut plus d'une minute pour courir.Slow Postgres JOIN Query

Les tables ont une bonne quantité de données, mais ils ne sont pas énormes (400k lignes sp_article et 300k lignes sp_article_categories)

#categories.article_set.filter(post_count__lte=50) 

EXPLAIN ANALYZE SELECT * 
        FROM "sp_article" 
      INNER JOIN "sp_article_categories" ON ("sp_article"."id" = "sp_article_categories"."article_id") 
       WHERE ("sp_article_categories"."category_id" = 1081 
        AND "sp_article"."post_count" <= 50) 

Nested Loop (cost=0.00..6029.01 rows=656 width=741) (actual time=0.472..25.724 rows=1266 loops=1) 
    -> Index Scan using sp_article_categories_category_id on sp_article_categories (cost=0.00..848.82 rows=656 width=12) (actual time=0.015..1.305 rows=1408 loops=1) 
     Index Cond: (category_id = 1081) 
    -> Index Scan using sp_article_pkey on sp_article (cost=0.00..7.88 rows=1 width=729) (actual time=0.014..0.015 rows=1 loops=1408) 
     Index Cond: (sp_article.id = sp_article_categories.article_id) 
     Filter: (sp_article.post_count <= 50) 
Total runtime: 26.536 ms 

J'ai un index sur:

sp_article_categories.article_id (type: btree) 
sp_article_categories.category_id 
sp_article.post_count (type: btree) 

Des suggestions sur comment je peux régler ceci pour obtenir la requête rapidement?

Merci!

+0

exactement ce que vous avez réellement besoin de toutes les colonnes des deux tables dans l'ensemble de résultats? –

+0

Je tire juste toutes les colonnes dans cet exemple.J'ai testé avec seulement un sous-ensemble des colonnes et a eu le même problème ... – erikcw

+0

réduisant le nombre de colonnes avait peu d'effet? – philgo20

Répondre

1

Vous avez fourni les informations vitales ici - l'analyse d'expliquer. Cela ne montre pas une durée d'exécution de 1 seconde, cependant, il montre 20 millisecondes. Donc - soit ce n'est pas la requête en cours d'exécution, ou le problème est ailleurs.

La seule différence entre l'analyse d'explication et une application réelle est que les résultats ne sont pas réellement retournés. Vous auriez besoin de beaucoup de données pour ralentir les choses à 1 seconde cependant.

Les autres suggestions sont toutes hors de la portée car elles ignorent le fait que la requête n'est pas lente. Vous avez les index pertinents (les deux côtés de la jointure utilisent une analyse d'index) et le planificateur est parfaitement capable de filtrer sur la table de catégorie en premier (c'est le point entier d'avoir un planificateur de requête moitié décent).

Alors - vous devez d'abord comprendre ce qui est lent ...

0

Mettre un index sur sp_article_categories.category_id

+0

J'ai déjà celui-là. J'ai oublié de l'inclure dans mon post ... – erikcw

0

D'un pur point de vue SQL, la jointure est plus efficace si votre table de base a moins de lignes en elle, et les conditions WHERE sont effectuées sur la table avant de rejoindre à l'autre. Pour voir si Django peut sélectionner d'abord les catégories, filtrez l'attribut category_id avant de rejoindre la table d'articles.

suit Pseudo-code:

SELECT * FROM categories c 
INNER JOIN articles a 
    ON c.category_id = 1081 
    AND c.category_id = a.category_id 

Et mettre un index sur category_id comme l'indique Steven.

+0

ne semble pas faire une différence: SELECT * FROM sp_article_categories c INNER JOIN sp_article une SUR c.category_id = 1081 ET c.article_id = a.id OÙ une .post_count <= 50; – erikcw

+0

Vous devrez peut-être modifier l'ordre de tri de la table article, afin que category_id soit inclus dans l'index article_id btree. –

0

Vous pouvez également utiliser les noms de champs *.

sélectionnez [champs] de ....

+0

J'utilise des noms de champs dans le code actuel. Juste utilisé * pour garder les choses courtes dans le message. Ne semble pas faire de différence de performance quand je le compare. – erikcw

0

Je suppose que vous avez Analyze sur la base de données pour obtenir des statistiques fraîches.

Il semble que la jointure entre sp_article.id et sp_article_categories.article_id soit coûteuse. Quel type de données est l'ID de l'article, numérique? Si ce n'est pas le cas, vous devriez peut-être envisager de le rendre numérique - entier ou bigint, peu importe vos besoins. Cela peut faire une grande différence de performance selon mon expérience. J'espère que cela aide.

À la votre! // John