限制为 1 的 PostgreSQL 慢速查询并按不需要的 where 条件排序

标签 postgresql performance limit

我有一个表accounts 和索引

accounts {
    id  text
    num_id  bigint
    pid text
    fid text
    created_at  timestamp with time zone
    updated_at  timestamp with time zone
}

CREATE UNIQUE INDEX accounts_pkey ON public.accounts USING btree (id)
CREATE INDEX fid_idx ON public.accounts USING btree (fid)
CREATE INDEX idx_accounts_pid_fid ON public.accounts USING btree (pid, fid)

而且这个查询很慢

explain analyse SELECT * FROM accounts
WHERE pid = 'hd' AND fid = '123'
ORDER BY  id ASC
LIMIT 1;
Limit  (cost=0.56..3173.34 rows=1 width=123) (actual time=49389.351..49389.351 rows=0 loops=1)
  ->  Index Scan using accounts_pkey on accounts  (cost=0.56..5022497.13 rows=1583 width=123) (actual time=49389.350..49389.350 rows=0 loops=1)
        Filter: ((pid = 'hd'::text) AND (fid = '123'::text))
        Rows Removed by Filter: 56821193
Planning time: 0.094 ms
Execution time: 49389.368 ms

根据这个answer ,可以通过添加不需要的 where 条件 pidfid

来解决
explain analyse SELECT * FROM accounts
WHERE pid = 'hd' AND fid = '123'
ORDER BY  id ASC, pid, fid
LIMIT 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;

这表现良好的原因是 LIMITORDER BY步骤是 Postgres 在 SELECT 之前唯一需要做的事情, 和 accounts_pkey可以在此处轻松扫描唯一索引。实际上,Postgres 只需要找到最低的 id值,然后返回聚集索引以覆盖 SELECT * .

但是,您问题中的查询有点不同:

SELECT *
FROM accounts
WHERE pid = 'hd' AND fid = '123'
ORDER BY id ASC
LIMIT 1;

在这种情况下,Postgres 选择扫描整个 accounts_pkey索引,从与您的 WHERE 对应的过滤步骤开始条款。因为accounts_pkey仅涵盖 id列,Postgres 必须返回聚集索引以查找 pid 的值和 fid .理想情况下,Postgres 将从最低的 id 开始。 value 并向下遍历索引,直到在 pid 上找到第一个匹配项和 fid值。无论 Postgres 决定做什么,以下覆盖索引都可以提供帮助:

CREATE INDEX idx_accounts_cover ON public.accounts USING btree (pid, fid, id);

鉴于现在可以使用上述索引轻松删除近 600 万条记录,剩余的 LIMIT/ORDER BYid 上操作可能更能忍受。由于该索引还涵盖了 id , Postgres 只需要在查询的最后一次寻回聚簇索引。

关于限制为 1 的 PostgreSQL 慢速查询并按不需要的 where 条件排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58021771/

相关文章:

jquery - 在其父级中保留一个可拖动的 div

postgresql - Heroku 上的 Postgres 和转储单表转储文件

sql - 使用 UNION 或 OR 查询?

perl - 为什么这个 Perl 代码在不同的机器上运行时返回不同的数据类型?

python - Python 中大数据结构的性能

java - 在ConcurrentHashMap中,为什么不在段上使用ReentrantReadWriteLock来代替ReentrantLock

html - 每个网页的最大 <iFrame> 标记数

postgresql - 跨平台整理和 ctype

performance - 在 React 单页面应用程序中,测量页面在视觉上看起来完整的时间的有用方法是什么?

iphone - 限制对 iPhone 应用程序的 Web 服务访问