我有一个表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 条件 pid
和 fid
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;
这表现良好的原因是 LIMIT
和 ORDER 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 BY
在 id
上操作可能更能忍受。由于该索引还涵盖了 id
, Postgres 只需要在查询的最后一次寻回聚簇索引。
关于限制为 1 的 PostgreSQL 慢速查询并按不需要的 where 条件排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58021771/