Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PostgreSQL慢速查询,限制为1,不需要where条件_Postgresql_Performance_Limit - Fatal编程技术网

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);