为什么我的查询很慢?

时间:2014-09-06 13:40:16

标签: postgresql

SELECT "Series".*
  ,"SeriesTranslations"."id" AS "SeriesTranslations.id"
  ,"SeriesTranslations"."title" AS "SeriesTranslations.title"
  ,"SeriesTranslations"."subtitle" AS "SeriesTranslations.subtitle"
  ,"SeriesTranslations"."slug" AS "SeriesTranslations.slug"
  ,"SeriesTranslations"."language" AS "SeriesTranslations.language"
  ,"SeriesTranslations"."seoTitle" AS "SeriesTranslations.seoTitle"
  ,"SeriesTranslations"."seoDescription" AS "SeriesTranslations.seoDescription"
  ,"Posts"."id" AS "Posts.id"
  ,"Posts"."type" AS "Posts.type"
  ,"Posts"."contentDuration" AS "Posts.contentDuration"
  ,"Posts"."publishDate" AS "Posts.publishDate"
  ,"Posts"."publishedAt" AS "Posts.publishedAt"
  ,"Posts"."thumbnailUrl" AS "Posts.thumbnailUrl"
  ,"Posts"."imageUrl" AS "Posts.imageUrl"
  ,"Posts"."media" AS "Posts.media"
  ,"Posts.PostTranslations"."id" AS "Posts.PostTranslations.id"
  ,"Posts.PostTranslations"."slug" AS "Posts.PostTranslations.slug"
  ,"Posts.PostTranslations"."title" AS "Posts.PostTranslations.title"
  ,"Posts.PostTranslations"."subtitle" AS "Posts.PostTranslations.subtitle"
  ,"Posts.PostTranslations"."language" AS "Posts.PostTranslations.language"
FROM (
  SELECT "Series"."id"
    ,"Series"."thumbnailUrl"
    ,"Series"."imageUrl"
    ,"Series"."coverUrl"
  FROM "Series" AS "Series"
  WHERE EXISTS (
      SELECT *
      FROM "SeriesTranslations" AS t
      WHERE t.LANGUAGE IN ('en-us')
        AND t.slug = 'in-residence-architecture-design-video-series'
        AND t."SeriesId" = "Series"."id" LIMIT 1
      ) LIMIT 1
  ) AS "Series"
INNER JOIN "SeriesTranslations" AS "SeriesTranslations" ON "Series"."id" = "SeriesTranslations"."SeriesId"
  AND "SeriesTranslations"."language" IN ('en-us')
LEFT JOIN "Posts" AS "Posts" ON "Series"."id" = "Posts"."SeriesId"
  AND EXISTS (
    SELECT *
    FROM "PostTranslations" AS pt
    WHERE pt.LANGUAGE IN ('en-us')
      AND pt."PostId" = "Posts"."id" LIMIT 1
    )
LEFT JOIN "PostTranslations" AS "Posts.PostTranslations" ON "Posts"."id" = "Posts.PostTranslations"."PostId"
  AND "Posts.PostTranslations"."language" IN ('en-us')
ORDER BY "Posts"."publishDate" DESC;

它从4个表“Series”,“SeriesTranslations”,“Posts”和“PostsTranslations”加载数据。我检索基于“SeriesTranslations”slug的单个“Series”以及属于这个系列的所有“Posts”及其翻译。

当返回包含14个帖子的系列时,此查询需要约1.5秒(从查询中返回总计14行)。在DB中只有少数系列(不超过5个),每个系列有2个翻译。但是在DB中有很多帖子 - 大概2000年,每个帖子有2个翻译所以大约4k PostTranslations ...


这是EXPLAIN结果

enter image description here

我有“slug”,“SeriesTranslations”和“PostTranslations”中的“语言”的独特索引,还有“帖子”上的忘记键。“SeriesId”,“SeriesTranslations”。“SeriesId”和“PostTranslations”。 “帖子ID”


在这里解析http://explain.depesz.com/s/fhm


我按照建议简化了查询:(删除了一个子查询并将条件移动到内连接) - 但查询仍然很慢......

SELECT "Series"."id"
    ,"Series"."thumbnailUrl"
    ,"Series"."imageUrl"
    ,"Series"."coverUrl"
    ,"SeriesTranslations"."id" AS "SeriesTranslations.id"
    ,"SeriesTranslations"."title" AS "SeriesTranslations.title"
    ,"SeriesTranslations"."subtitle" AS "SeriesTranslations.subtitle"
    ,"SeriesTranslations"."slug" AS "SeriesTranslations.slug"
    ,"SeriesTranslations"."language" AS "SeriesTranslations.language"
    ,"SeriesTranslations"."seoTitle" AS "SeriesTranslations.seoTitle"
    ,"SeriesTranslations"."seoDescription" AS "SeriesTranslations.seoDescription"
    ,"Posts"."id" AS "Posts.id"
    ,"Posts"."type" AS "Posts.type"
    ,"Posts"."contentDuration" AS "Posts.contentDuration"
    ,"Posts"."publishDate" AS "Posts.publishDate"
    ,"Posts"."publishedAt" AS "Posts.publishedAt"
    ,"Posts"."thumbnailUrl" AS "Posts.thumbnailUrl"
    ,"Posts"."imageUrl" AS "Posts.imageUrl"
    ,"Posts"."media" AS "Posts.media"
    ,"Posts.PostTranslations"."id" AS "Posts.PostTranslations.id"
    ,"Posts.PostTranslations"."slug" AS "Posts.PostTranslations.slug"
    ,"Posts.PostTranslations"."title" AS "Posts.PostTranslations.title"
    ,"Posts.PostTranslations"."subtitle" AS "Posts.PostTranslations.subtitle"
    ,"Posts.PostTranslations"."language" AS "Posts.PostTranslations.language"
FROM "Series" AS "Series"
INNER JOIN "SeriesTranslations" AS "SeriesTranslations" ON "Series"."id" = "SeriesTranslations"."SeriesId"
    AND "SeriesTranslations"."language" IN ('en-us')
    AND "SeriesTranslations"."slug" = 'sdf'
LEFT JOIN "Posts" AS "Posts" ON "Series"."id" = "Posts"."SeriesId"
    AND EXISTS (
        SELECT *
        FROM "PostTranslations" AS pt
        WHERE pt.LANGUAGE IN ('en-us')
            AND pt."PostId" = "Posts"."id" LIMIT 1
        )
LEFT JOIN "PostTranslations" AS "Posts.PostTranslations" ON "Posts"."id" = "Posts.PostTranslations"."PostId"
    AND "Posts.PostTranslations"."language" IN ('en-us')
WHERE (1 = 1)
ORDER BY "Posts"."publishDate" DESC
    ,"Posts"."id" DESC;

这是新的查询计划:

                                                                                     QUERY PLAN                                                                                     
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=1014671.76..1014671.76 rows=1 width=695) (actual time=2140.906..2140.908 rows=14 loops=1)
   Sort Key: "Posts"."publishDate", "Posts".id
   Sort Method: quicksort  Memory: 45kB
   ->  Nested Loop Left Join  (cost=0.03..1014671.76 rows=1 width=695) (actual time=85.862..2140.745 rows=14 loops=1)
         Join Filter: ("Posts".id = "Posts.PostTranslations"."PostId")
         Rows Removed by Join Filter: 28266
         ->  Nested Loop  (cost=0.03..1014165.24 rows=1 width=564) (actual time=85.307..2042.304 rows=14 loops=1)
               Join Filter: ("Series".id = "SeriesTranslations"."SeriesId")
               Rows Removed by Join Filter: 35
               ->  Index Scan using "SeriesTranslations-slug-language-unique" on "SeriesTranslations"  (cost=0.03..4.03 rows=1 width=200) (actual time=0.044..0.046 rows=1 loops=1)
                     Index Cond: ((slug = 'in-residence-architecture-design-video-series'::text) AND (language = 'en-us'::text))
               ->  Nested Loop Left Join  (cost=0.00..1014159.63 rows=450 width=368) (actual time=85.243..2042.207 rows=49 loops=1)
                     Join Filter: ("Series".id = "Posts"."SeriesId")
                     Rows Removed by Join Filter: 18131
                     ->  Seq Scan on "Series"  (cost=0.00..11.35 rows=450 width=100) (actual time=0.006..0.046 rows=9 loops=1)
                     ->  Materialize  (cost=0.00..1012330.79 rows=1010 width=272) (actual time=4.422..226.499 rows=2020 loops=9)
                           ->  Seq Scan on "Posts"  (cost=0.00..1012329.78 rows=1010 width=272) (actual time=39.785..2020.448 rows=2020 loops=1)
                                 Filter: (SubPlan 1)
                                 SubPlan 1
                                   ->  Limit  (cost=0.00..500.94 rows=1 width=1267) (actual time=0.995..0.995 rows=1 loops=2020)
                                         ->  Seq Scan on "PostTranslations" pt  (cost=0.00..500.94 rows=1 width=1267) (actual time=0.992..0.992 rows=1 loops=2020)
                                               Filter: ((language = 'en-us'::text) AND ("PostId" = "Posts".id))
                                               Rows Removed by Filter: 1591
         ->  Seq Scan on "PostTranslations" "Posts.PostTranslations"  (cost=0.00..499.44 rows=2020 width=135) (actual time=0.003..3.188 rows=2020 loops=14)
               Filter: (language = 'en-us'::text)
               Rows Removed by Filter: 964
 Total runtime: 2141.432 ms
(27 rows)

1 个答案:

答案 0 :(得分:1)

FK上的索引可能有助于JOIN:

CREATE INDEX ON PostTranslations (PostId); -- For FK
VACUUM ANALYZE  PostTranslations ; -- refresh statistics

CREATE INDEX ON SeriesTranslations (SeriesId ); -- FK
VACUUM ANALYZE SeriesTranslations ;

CREATE INDEX ON Posts (SeriesId) ; -- FK
VACUUM ANALYZE Posts ;

删除 LIMIT 1子查询中的EXISTS(...)。它们只能造成伤害。