PostgreSQL慢速查询,限制为1,不需要where条件
我有一个表PostgreSQL慢速查询,限制为1,不需要where条件,postgresql,performance,limit,Postgresql,Performance,Limit,我有一个表帐户和索引 账户{ id文本 num_id bigint pid文本 fid文本 在带有时区的时间戳处创建\u 在带有时区的时间戳处更新了_ } 使用btree(id)在public.accounts上创建唯一索引帐户 使用btree(fid)在public.accounts上创建索引fid_idx 使用btree(pid,fid)在public.accounts上创建索引idx\u accounts\u pid\u fid 而且这个查询很慢 explain analysis SEL
帐户和索引
账户{
id文本
num_id bigint
pid文本
fid文本
在带有时区的时间戳处创建\u
在带有时区的时间戳处更新了_
}
使用btree(id)在public.accounts上创建唯一索引帐户
使用btree(fid)在public.accounts上创建索引fid_idx
使用btree(pid,fid)在public.accounts上创建索引idx\u accounts\u pid\u fid
而且这个查询很慢
explain analysis SELECT*FROM accounts
其中pid='hd'和fid='123'
按id订购ASC
限值1;
因此,可以通过添加不必要的where条件pid
和fid
explain analysis SELECT*FROM accounts
其中pid='hd'和fid='123'
按id订购ASC、pid、fid
限值1;
但是,它不起作用
Limit (cost=0.56..3173.37 rows=1 width=123) (actual time=49495.236..49495.236 rows=0 loops=1)
-> Index Scan using accounts_pkey on accounts (cost=0.56..5022556.07 rows=1583 width=123) (actual time=49495.234..49495.234 rows=0 loops=1)
Filter: ((pid = 'hd'::text) AND (fid = '123'::text))
Rows Removed by Filter: 56821555
Planning time: 0.096 ms
Execution time: 49495.253 ms
有我失踪的地方吗
PostgreSQL版本:9.6.8根据您的评论,以下查询实际上相当有效:
SELECT *
FROM accounts
ORDER BY id
LIMIT 1;
这种方法之所以表现良好,是因为在选择之前,Postgres只需执行限制和按顺序步骤,并且可以轻松扫描帐户的唯一索引。实际上,Postgres只需要找到最低的id
值,然后返回到聚集索引以覆盖SELECT*
但是,您问题中的查询有点不同:
SELECT *
FROM accounts
WHERE pid = 'hd' AND fid = '123'
ORDER BY id ASC
LIMIT 1;
在这种情况下,Postgres选择扫描整个accounts\u pkey
索引,从与您的WHERE
子句对应的筛选步骤开始。由于accounts\u pkey
只包含id
列,Postgres必须返回聚集索引以查找pid
和fid
的值。理想情况下,Postgres只需从最低的id
值开始,沿着索引向下走,直到在pid
和fid
值上找到第一个匹配项。无论Postgres决定做什么,下面的覆盖索引在这里都会有所帮助:
CREATE INDEX idx_accounts_cover ON public.accounts USING btree (pid, fid, id);
考虑到现在使用上述索引可以轻松删除近600万条记录,对id
执行剩余的限制
/顺序操作可能更容易接受。由于该索引也包含id,Postgres只需在查询结束时返回聚集索引一次。出于好奇,SELECT*FROM accounts ORDER BY id LIMIT 1的运行时间是多少?@TimBiegeleisen,SELECT*FROM accounts ORDER BY id LIMIT 1
的运行时间为LIMIT(成本=0.56..0.65行=1宽度=123)(实际时间=0.010..0.010行=1循环=1)->使用账户对账户进行索引扫描(成本=0.56..4738719.60行=56980788宽度=123)(实际时间=0.010..0.010行=1循环=1)计划时间:0.078毫秒执行时间:0.027毫秒
我试图在下面给出一个答案,希望能部分解释您看到的内容。我不知道为什么Postgres会选择这个执行计划,但是索引定义的一个小小的改变可能会解决所有的问题。pid,fid
索引的基数可能非常低?(仅仅因为这一个案例具有合理的基数,并不排除其他基数非常低的组合…@MatBailie是的,我也在考虑这个问题,想看看OP的数据样本。@TimBiegeleisen,我确实有机会在生产环境中添加这个索引(pid,fid,id)
。然而,我在beta-env中测试了它。添加此索引后,解释结果是)->排序(成本=8.39..8.39行=1宽度=119)(实际时间=0.028..0.028行=0循环=1)排序键:id排序方法:快速排序内存:25kB->使用fid_idx对帐户进行索引扫描(成本=0.29..8.38行=1宽度=119)(实际时间=0.020...020行=0循环=1)索引条件:(fid='123'::文本)过滤器:(pid='hd'::text)
<使用代码>fid\U idx
而不是idx\U账户_cover@zangw好的……这里的问题是,最快的Postgres可能是沿着id
上的索引走,并在找到第一条匹配记录时停止。但是,如果数百万条记录中只有很少几条与pid
和fid
匹配,那么无论使用何种策略,此过程都可能需要一些时间。
CREATE INDEX idx_accounts_cover ON public.accounts USING btree (pid, fid, id);