簡體   English   中英

PostgreSQL 慢查詢,限制為 1 並按不需要的 where 條件排序

[英]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 條件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 必須回溯到聚集索引來查找pidfid的值。 理想情況下,Postgres 會從最低的id值開始,沿着索引向下走,直到找到pidfid值的第一個匹配項。 無論 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM