[英]PostgreSQL slow query with limit 1 and order by unneeded where condition
我有一個表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
根據這個答案,可以通過添加不需要的 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
值開始,沿着索引向下走,直到找到pid
和fid
值的第一個匹配項。 無論 Postgres 決定做什么,以下覆蓋索引都可以在這里提供幫助:
CREATE INDEX idx_accounts_cover ON public.accounts USING btree (pid, fid, id);
鑒於現在可以使用上述索引輕松刪除近 600 萬條記錄,對id
進行剩余的LIMIT
/ ORDER BY
操作可能更容易容忍。 而且由於這個索引也包含id
,所以 Postgres 只需要在查詢的最后一次回溯到聚集索引。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.