【问题标题】:Slow Postgres JOIN Query慢 Postgres JOIN 查询
【发布时间】:2010-09-17 19:31:47
【问题描述】:

我正在尝试优化由 Django ORM 生成的慢查询。这是一个多对多查询。运行时间超过 1 分钟。

这些表包含大量数据,但它们并不庞大(sp_article 中的 400k 行和 sp_article_categories 中的 300k 行)

#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

我有一个索引:

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

关于如何调整它以加快查询速度的任何建议?

谢谢!

【问题讨论】:

  • 您真的需要结果集中两个表中的所有列吗?
  • 我只是拉出这个例子中的所有列。我只测试了列的一个子集并且遇到了同样的问题......
  • 减少列的nb效果不大?

标签: django postgresql


【解决方案1】:

您在此处提供了重要信息 - 解释分析。不过,这并没有显示 1 秒的运行时间,而是显示了 20 毫秒。所以 - 要么不是正在运行的查询,要么问题出在其他地方。

解释分析和实际应用程序之间的唯一区别是结果实际上并没有返回。不过,您需要大量数据才能将速度减慢到 1 秒。

其他建议都是不正确的,因为它们忽略了查询并不慢的事实。你有相关的索引(连接的两边都在使用索引扫描),并且规划器完全能够首先过滤类别表(这就是拥有一个半体面的查询规划器的全部意义)。

所以 - 你首先需要弄清楚到底什么是慢...

【讨论】:

    【解决方案2】:

    sp_article_categories.category_id上建立索引

    【讨论】:

      【解决方案3】:

      从纯 SQL 的角度来看,如果您的基表中的行数较少,并且在该表连接到另一个表之前执行 WHERE 条件,则连接效率更高。

      所以看看能不能让Django先从分类中进行选择,然后在加入文章表之前过滤category_id。

      伪代码如下:

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

      并像 Steven 建议的那样在 category_id 上建立索引。

      【讨论】:

      • 似乎没有区别:SELECT * FROM sp_article_categories c INNER JOIN sp_article a ON c.category_id = 1081 AND c.article_id = a.id WHERE a.post_count
      • 您可能需要更改文章表的排序顺序,使 category_id 包含在 article_id btree 索引中。
      【解决方案4】:

      您也可以使用字段名称来代替 *。

      从...中选择 [字段]

      【讨论】:

      • 我在实际代码中使用了字段名称。只是用 * 在帖子中保持简短。当我对它进行基准测试时,似乎在性能方面没有什么不同。
      【解决方案5】:

      我假设您已对数据库运行分析以获取新的统计信息。

      sp_article.id 和 sp_article_categories.article_id 之间的连接似乎代价高昂。文章 ID 是什么数据类型,数字?如果不是,您也许应该考虑将其设为数字​​ - 整数或 bigint,无论您需要什么。根据我的经验,它可以对性能产生很大影响。希望对您有所帮助。

      干杯! // 约翰

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-04-14
        • 1970-01-01
        • 2014-04-04
        • 2014-03-15
        • 2016-04-02
        • 1970-01-01
        • 2015-06-04
        相关资源
        最近更新 更多