我们有一个特定 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。
在一个表中,我们有大约 50 万行。与查询相关的字段是:
- 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)
先谢谢你, - jack
最佳答案
对于这个查询:
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 行,因此最终排序被淘汰。
关于sql - Postgres 中的慢速查询优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56441909/