【问题标题】:Slow query optimisation in PostgresPostgres 中的慢查询优化
【发布时间】:2019-06-04 10:19:02
【问题描述】:

我们遇到了特定 SQL 查询的性能问题,我们正在尝试找出如何改进这里。它的执行时间大约是 20 - 100 秒!

这是查询和解释:

SELECT  "jobs".* FROM "jobs"
  WHERE "jobs"."status" IN (1, 2, 3, 4)
  ORDER BY "jobs"."due_date" ASC
  LIMIT 5;


Limit  (cost=0.42..1844.98 rows=5 width=2642) (actual time=16927.150..18151.643 rows=1 loops=1)
   ->  Index Scan using index_jobs_on_due_date on jobs  (cost=0.42..1278647.41 rows=3466 width=2642) (actual time=16927.148..18151.641 rows=1 loops=1)
         Filter: (status = ANY ('{1,2,3,4}'::integer[]))
         Rows Removed by Filter: 595627
 Planning time: 0.205 ms
 Execution time: 18151.684 ms

我们在 AWS RDS 上使用 PostgreSQL 9.6.11。

在一个表中,我们有大约 500K 行。与查询相关的字段是:

  • due_date(时间戳不带时区,可以为空)
  • 状态(整数,非空)

我们有以下索引:

CREATE INDEX index_jobs_on_due_date ON public.jobs USING btree (due_date)
CREATE INDEX index_jobs_on_due_date_and_status ON public.jobs USING btree (due_date, status)
CREATE INDEX index_jobs_on_status ON public.jobs USING btree (status)
CREATE UNIQUE INDEX jobs_pkey ON public.jobs USING btree (id)

提前谢谢你, - 杰克

【问题讨论】:

    标签: sql postgresql query-optimization sql-optimization


    【解决方案1】:

    对于这个查询:

    SELECT  j.*
    FROM "jobs" j
    WHERE j."status" IN (1, 2, 3, 4)
    ORDER BY "jobs"."due_date" ASC
    LIMIT 5;
    

    “明显”索引位于(status)。但这可能无济于事。目标是摆脱排序。因此,您可以重写查询并使用索引jobs(status, due_date)

    select j.*
    from ((select j.*
           from jobs j
           where j.status = 1
           order by j.due_date asc
           limit 5
          ) union all
          (select j.*
           from jobs j
           where j.status = 2
           order by j.due_date asc
           limit 5
          ) union all
          (select j.*
           from jobs j
           where j.status = 3
           order by j.due_date asc
           limit 5
          ) union all
          (select j.*
           from jobs j
           where j.status = 4
           order by j.due_date asc
           limit 5
          )
         ) j
    order by due_date
    limit 5;
    

    每个子查询都应该使用复合索引。最终的排序将(最多)20 行,这应该很快)。

    编辑:

    这是一个相关的想法,索引相同:

    SELECT j.*
    FROM (SELECT  j.*,
                  ROW_NUMBER() OVER (PARTITION BY j.status ORDER BY j.due_date ASC) as seqnum
          FROM "jobs" j
         ) j
    WHERE j.status in (1, 2, 3, 4) AND seqnum <= 5
    ORDER BY j.due_date ASC
    LIMIT 5;
    

    这可以使用索引进行ROW_NUMBER() 计算。这可能需要对表进行全表扫描。但是,最终排序将被限制为 20 行,因此最终排序被淘汰。

    【讨论】:

    • 您提供的解决方案有效,但在我们的案例中,我们有很多此类查询(从多个状态中选择)。此外,这些查询通常有很多额外的过滤器和条件。所以建立这样的工会是相当棘手的。有没有办法强制使用那里的状态索引 - 看起来很慢,因为它没有被使用。或者其他可能的解决方案?谢谢!
    • @JackJuiceson 。 . .这不是强制使用status 索引;这是关于之后的排序。
    猜你喜欢
    • 2022-01-24
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 2015-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多