提高 PostgreSQL 查询性能

Posted

技术标签:

【中文标题】提高 PostgreSQL 查询性能【英文标题】:Improve PostgreSQL query performance 【发布时间】:2013-02-17 20:05:11 【问题描述】:

在我的服务器上运行此查询时速度很慢,我不明白为什么。谁能帮我弄清楚? 查询:

SELECT
    "t_dat"."t_year" AS "c0",
    "t_dat"."t_month" AS "c1",
    "t_dat"."t_week" AS "c2",
    "t_dat"."t_day" AS "c3",
    "t_purs"."p_id" AS "c4",
    sum("t_purs"."days") AS "m0",
    sum("t_purs"."timecreated") AS "m1"
FROM "t_dat", "t_purs"
WHERE "t_purs"."created" = "t_dat"."t_key"
  AND "t_dat"."t_year" = 2013
  AND "t_dat"."t_month" = 3
  AND "t_dat"."t_week" = 9
  AND "t_dat"."t_day" IN (1,2)
  AND "t_purs"."p_id" IN (
      '4','15','18','19','20','29',
      '31','35','46','56','72','78')
GROUP BY
    "t_dat"."t_year",
    "t_dat"."t_month",
    "t_dat"."t_week",
    "t_dat"."t_day",
    "t_purs"."p_id"

解释分析:

HashAggregate(成本=12252.04..12252.04 行=1 宽度=28)(实际时间=10212.374..10212.384 行=10 循环=1) -> 嵌套循环(成本=0.00..12252.03 行=1 宽度=28)(实际时间=3016.006..10212.249 行=14 循环=1) 加入过滤器:(t_dat.t_key = t_purs.created) -> Seq Scan on t_dat (cost=0.00..129.90 rows=1 width=20) (实际时间=0.745..2.040 rows=48 loops=1) 过滤器: ((t_day = ANY ('1,2'::integer[])) AND (t_year = 2013) AND (t_month = 3) AND (t_week = 9)) -> Seq Scan on t_purs (cost=0.00..12087.49 rows=9900 width=16) (实际时间=0.018..201.630 rows=14014 loops=48) 过滤器:(p_id = ANY ('4,15,18,19,20,29,31,35,46,56,72,78'::integer[])) 总运行时间:10212.470 毫秒

【问题讨论】:

这些表中有多少条记录?索引完成了吗? t_purs 大约 600K 记录,t_dat 大约 9K。索引设置在 t_purs.id, t_dat.t_key 您需要向我们展示表和索引定义。 诊断慢查询需要完整的表和索引定义,而不仅仅是描述或解释。也许您的表格定义不佳。也许索引没有正确创建。也许您认为您在该列上没有索引。没有看到表和索引定义,我们无法分辨。 另外,完全没有必要将“列”和“表”名称放在引号中。 【参考方案1】:

很难说你到底错过了什么,但如果我是你,我会确保存在以下索引:

CREATE INDEX t_dat_id_date_idx
    ON t_dat (t_key, t_year, t_month, t_week, t_day);

对于t_purs,创建此索引:

CREATE INDEX t_purs_created_p_id_idx
    ON t_purs (created, p_id);

【讨论】:

嘿,我对索引进行了一些更改,现在它的速度就像光一样!谢谢!! 很高兴听到这个消息。不要低估复合索引的威力! :)【参考方案2】:

考虑在表格中使用单列

t_date date

而不是(t_year, t_month, t_week, t_day)。数据类型date 占用4 个字节。这会稍微缩小你的表,使索引更小更快,并且更容易分组。

可以使用@987654321轻松快速地从日期中提取@。您的查询可能看起来像这样并且会更快:

SELECT extract (year  FROM t_date) AS c0
      ,extract (month FROM t_date) AS c1
      ,extract (week  FROM t_date) AS c2
      ,extract (day   FROM t_date) AS c3
      ,p.p_id                      AS c4
      ,sum(p.days)                 AS m0
      ,sum(p.timecreated)          AS m1
FROM   t_dat  d
JOIN   t_purs p ON p.created = d.t_key
WHERE  d.t_date IN ('2013-03-01'::date, '2013-03-02'::date)
AND    p.p_id IN (4,15,18,19,20,29,31,35,46,56,72,78)
GROUP  BY d.t_date, p.p_id;

对于性能来说更重要的是索引,那么它就是:

CREATE INDEX t_dat_date_idx ON t_dat (t_key, t_date);

或者,取决于数据分布:

CREATE INDEX t_dat_date_idx ON t_dat (t_date, t_key);

The sequence of column matters.你甚至可以同时创建。

【讨论】:

以上是关于提高 PostgreSQL 查询性能的主要内容,如果未能解决你的问题,请参考以下文章

使用嵌套循环提高 SQL 查询的性能 - PostgreSQL

提高 Postgresql 查询性能

提高 PostgreSQL 查询性能

提高 Postgres 性能

如何提高 PostgreSQL 9.5 中的查询性能?

如何提高 PostgreSQL LIKE %text% 查询性能