为什么这个DISTINCT / INNER JOIN / ORDER BY postgresql查询这么慢?

时间:2012-03-29 00:01:53

标签: sql performance postgresql

此查询需要约4秒钟才能完成:

SELECT DISTINCT "resources_resource"."id",
                  "resources_resource"."heading",
                  "resources_resource"."name",
                  "resources_resource"."old_name",
                  "resources_resource"."clean_name",
                  "resources_resource"."sort_name",
                  "resources_resource"."see_also_id",
                  "resources_resource"."referenced_passages",
                  "resources_resource"."resource_type",
                  "resources_resource"."ord",
                  "resources_resource"."content",
                  "resources_resource"."thumb",
                  "resources_resource"."resource_origin"
  FROM "resources_resource"
  INNER JOIN "resources_passageresource" ON ("resources_resource"."id" = "resources_passageresource"."resource_id")
  WHERE "resources_passageresource"."start_ref" >= 66001001
  ORDER BY "resources_resource"."ord" ASC, "resources_resource"."sort_name" ASC LIMIT 5

按照受欢迎的要求,EXPLAIN ANALYZE:

Limit  (cost=1125.50..1125.68 rows=5 width=803) (actual time=4434.076..4434.557 rows=5 loops=1)
  ->  Unique  (cost=1125.50..1136.91 rows=326 width=803) (actual time=4434.076..4434.557 rows=5 loops=1)
        ->  Sort  (cost=1125.50..1126.32 rows=326 width=803) (actual time=4434.075..4434.075 rows=6 loops=1)
              Sort Key: resources_resource.ord, resources_resource.sort_name, resources_resource.id, resources_resource.heading, resources_resource.name, resources_resource.old_name, resources_resource.clean_name, resources_resource.see_also_id, resources_resource.referenced_passages, resources_resource.resource_type, resources_resource.content, resources_resource.thumb, resources_resource.resource_origin
              Sort Method:  quicksort  Memory: 424kB
              ->  Hash Join  (cost=697.00..1111.89 rows=326 width=803) (actual time=3.453..41.429 rows=424 loops=1)
                    Hash Cond: (resources_passageresource.resource_id = resources_resource.id)
                    ->  Bitmap Heap Scan on resources_passageresource  (cost=10.78..190.19 rows=326 width=4) (actual time=0.107..0.401 rows=424 loops=1)
                          Recheck Cond: (start_ref >= 66001001)
                          ->  Bitmap Index Scan on resources_passageresource_start_ref  (cost=0.00..10.70 rows=326 width=0) (actual time=0.086..0.086 rows=424 loops=1)
                                Index Cond: (start_ref >= 66001001)
                    ->  Hash  (cost=431.32..431.32 rows=2232 width=803) (actual time=3.228..3.228 rows=2232 loops=1)
                          Buckets: 1024  Batches: 2  Memory Usage: 947kB
                          ->  Seq Scan on resources_resource  (cost=0.00..431.32 rows=2232 width=803) (actual time=0.002..1.621 rows=2232 loops=1)
Total runtime: 4435.460 ms

这是ORM生成的SQL。我可以在SQL中工作,但我绝对不会精通,这里的EXPLAIN输出对我来说很神秘。这个查询怎么样把我拉下来?

更新: @Ybakos发现ORDER_BY造成了麻烦。完全删除ORDER_BY子句有点帮助,但查询仍需要800毫秒。这是EXPLAIN ANALYZE,没有ORDER_BY

HashAggregate  (cost=1122.49..1125.75 rows=326 width=803) (actual time=787.519..787.559 rows=104 loops=1)
  ->  Hash Join  (cost=697.00..1111.89 rows=326 width=803) (actual time=3.381..7.312 rows=424 loops=1)
        Hash Cond: (resources_passageresource.resource_id = resources_resource.id)
        ->  Bitmap Heap Scan on resources_passageresource  (cost=10.78..190.19 rows=326 width=4) (actual time=0.095..0.686 rows=424 loops=1)
              Recheck Cond: (start_ref >= 66001001)
              ->  Bitmap Index Scan on resources_passageresource_start_ref  (cost=0.00..10.70 rows=326 width=0) (actual time=0.079..0.079 rows=424 loops=1)
                    Index Cond: (start_ref >= 66001001)
        ->  Hash  (cost=431.32..431.32 rows=2232 width=803) (actual time=3.173..3.173 rows=2232 loops=1)
              Buckets: 1024  Batches: 2  Memory Usage: 947kB
              ->  Seq Scan on resources_resource  (cost=0.00..431.32 rows=2232 width=803) (actual time=0.002..1.568 rows=2232 loops=1)
Total runtime: 787.678 ms

3 个答案:

答案 0 :(得分:2)

在我看来,DISTINCT必须用于删除连接产生的重复项。所以我的问题是,为什么要首先制作副本?我不完全确定这个查询是ORM生成的必须暗示的,但是如果重写它是一个选项,你当然可以用防止重复出现的方式重写它。例如,使用IN

SELECT "resources_resource"."id",
       "resources_resource"."heading",
       "resources_resource"."name",
       "resources_resource"."old_name",
       "resources_resource"."clean_name",
       "resources_resource"."sort_name",
       "resources_resource"."see_also_id",
       "resources_resource"."referenced_passages",
       "resources_resource"."resource_type",
       "resources_resource"."ord",
       "resources_resource"."content",
       "resources_resource"."thumb",
       "resources_resource"."resource_origin"
  FROM "resources_resource"
  WHERE "resources_resource"."id" IN (
        SELECT "resources_passageresource"."resource_id"
          FROM "resources_passageresource"
          WHERE "resources_passageresource"."start_ref" >= 66001001
        )
  ORDER BY "resources_resource"."ord" ASC, "resources_resource"."sort_name" ASC LIMIT 5

或使用EXISTS

SELECT "resources_resource"."id",
       "resources_resource"."heading",
       "resources_resource"."name",
       "resources_resource"."old_name",
       "resources_resource"."clean_name",
       "resources_resource"."sort_name",
       "resources_resource"."see_also_id",
       "resources_resource"."referenced_passages",
       "resources_resource"."resource_type",
       "resources_resource"."ord",
       "resources_resource"."content",
       "resources_resource"."thumb",
       "resources_resource"."resource_origin"
  FROM "resources_resource"
  WHERE EXISTS (
        SELECT *
          FROM "resources_passageresource"
          WHERE "resources_passageresource"."resource_id" = "resources_resource"."id"
            AND "resources_passageresource"."start_ref" >= 66001001
        )
  ORDER BY "resources_resource"."ord" ASC, "resources_resource"."sort_name" ASC LIMIT 5

当然,如果完全重写查询是可以接受的,我还会删除列名前面的长表名。例如,请考虑以下内容(重写IN查询):

SELECT "id",
       "heading",
       "name",
       "old_name",
       "clean_name",
       "sort_name",
       "see_also_id",
       "referenced_passages",
       "resource_type",
       "ord",
       "content",
       "thumb",
       "resource_origin"
  FROM "resources_resource"
  WHERE "resources_resource"."id" IN (
        SELECT "resource_id"
          FROM "resources_passageresource"
          WHERE "start_ref" >= 66001001
        )
  ORDER BY "ord" ASC, "sort_name" ASC LIMIT 5

答案 1 :(得分:1)

这是ORDER BY与LIMIT的组合。

如果你没有(ord,sort_name)索引,那么我敢打赌这是导致性能下降的原因。或者,此特定查询可能需要(start_ref,ord,sort_name)上的索引。最后,由于该连接,可能左/第一个表是您的ORDER BY标准适用的表。

答案 2 :(得分:1)

在JOIN中似乎很长一段时间。 postgresql.conf中的默认内存设置对于任何现代计算机来说都太低了。你有没有记得碰它们?