为什么postgresql查询的这种独特/内部连接/顺序如此缓慢?

为什么postgresql查询的这种独特/内部连接/顺序如此缓慢?,sql,performance,postgresql,Sql,Performance,Postgresql,完成此查询大约需要4秒钟: SELECT DISTINCT "resources_resource"."id", "resources_resource"."heading", "resources_resource"."name", "resources_resource"."old_name", "resources_resource"."clean

完成此查询大约需要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
根据大众要求,解释和分析:

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,但我绝对不精通,这里的解释输出让我感到困惑。这个问题会把我拖下水吗

更新:@Ybakos发现的
订单造成了麻烦。完全删除
ORDER\u BY
子句会有所帮助,但查询仍然需要800毫秒。下面是解释分析,sans
ORDER\u 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

它是顺序与限制的结合


如果没有索引(ord,sort_name),那么我打赌这就是性能低下的原因。或者,对于这个特定的查询,可能需要一个索引(start\u ref、ord、sort\u name)。最后,由于这种连接,可能左/第一个表就是您的ORDER BY标准所适用的表。

它是ORDER BY和LIMIT的组合


如果没有索引(ord,sort_name),那么我打赌这就是性能低下的原因。或者,对于这个特定的查询,可能需要一个索引(start\u ref、ord、sort\u name)。最后,由于该联接,可能左/第一个表就是应用ORDER BY条件的表。

在联接中似乎花费了很长时间。postgresql.conf中的默认内存设置对于任何现代计算机来说都太低。你记得要把它们往上推吗?

那好像是很久以前的事了。postgresql.conf中的默认内存设置对于任何现代计算机来说都太低。您是否记得将它们放大?

在我看来,必须使用
DISTINCT
删除联接产生的重复项。所以我的问题是,为什么首先要生产复制品?我不完全确定ORM生成的这个查询意味着什么,但是如果重写它是一个选项,您当然可以重写它,以防止出现重复。例如,在
中使用

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
或使用
存在

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
当然,如果完全重写查询是可以接受的,我还将删除列名前面的长表名。考虑下面的例子,例如< <代码> >重写查询>:


在我看来,
DISTINCT
必须用于删除联接产生的重复项。所以我的问题是,为什么首先要生产复制品?我不完全确定ORM生成的这个查询意味着什么,但是如果重写它是一个选项,您当然可以重写它,以防止出现重复。例如,在
中使用

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
或使用
存在

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
当然,如果完全重写查询是可以接受的,我还将删除列名前面的长表名。考虑下面的例子,例如< <代码> >重写查询>:



您是否有此字段的
索引
resources\u resource.id
resources\u passageresource.resource\u id
johntotetwoo有一个好问题。您的WHERE子句正在命中resources\u passageresource.start\u ref上的索引。。。那些成本值呢?分析的输出是什么?在
resources\u resource.ord
.id
上的单个索引,以及在
resources\u passageresource.resource\u id
上的单个索引。在这个字段上有
索引吗
resources\u resource.id
resources\u passageresource.resource\u id
johntotetwoo有一个好问题。您的WHERE子句正在命中resources\u passageresource.start\u ref上的索引。。。那么这些成本值呢?分析的输出是什么?在
resources\u resource.ord
.id
上的单个索引,以及在
resources\u passageresource.resource\u id
上的单个索引。计划者估计有326行要按以下顺序排序。。。应该是毫秒而不是秒。OP需要对解释进行后期分析,以获得更好的图片。它还需要对唯一的过程进行排序,以实现DISTINCT。有序索引扫描并不总是比seq scans+sort好,事实上,由于搜索时间的原因,它们通常会差得多。我在
ord
上有索引,但在
sort\u name
上没有索引,我想我真的不需要在
sort\u name
上排序。我将尝试删除它。仅从ORDER BY中删除
sort\u name
没有帮助,但删除ORDER BY会将查询降低到800ms。这是一个显著的改进,但速度仍然慢得令人无法接受。计划者估计有326行要按。。。应该是毫秒而不是秒。OP需要对解释进行后期分析,以获得更好的图片。它还需要对唯一的过程进行排序,以实现DISTINCT。有序索引扫描并不总是比seq scans+sort好,事实上,由于搜索时间的原因,它们通常会差得多。我在
ord
上有索引,但在
sort\u name
上没有索引,我想我真的不需要在
sort\u name
上排序。我将尝试删除它。仅从ORDER BY中删除
sort\u name
没有帮助,但删除ORDER BY会将查询降低到800ms。有明显的改善,但仍然慢得令人无法接受。对不起,应该怎么做?如果存在
DISTINCT
子句,并且您只从两个表中的一个表中提取行,并且(可能)包括PK列,则在
中内部将联接替换为
?将这两种查询相互匹配涉及多个因素,因此我想,教查询规划者这么做可能不是一件容易的事。不过,作为一个软件类,我对查询计划器知之甚少。但你至少可以说PostgreSQL的一个还不够聪明(现在?)。对不起,你应该怎么做?当出现
DISTINCT
子句并且正在拉取时,在内部将联接替换为