簡體   English   中英

Postgresql ORDER BY - 選擇正確的索引

[英]Postgresql ORDER BY - choosing right index

有表100 T(user, timestamp,...)有100毫升+記錄(PostgreSQL 9.1)。

表單的查詢

SELECT * 
FROM T 
WHERE user='abcd' 
ORDER BY timestamp 
LIMIT 1

當有大約100000個用戶記錄時,使用timestamp索引而不是用戶索引。

使用時間戳索引總是會得到較差的結果(20秒以上),因為它最終會掃描所有記錄。 通過更改查詢以使用ORDER BY DATE(timestamp)來繞過timestamp索引將導致查詢求助於用戶索引並給出小於100毫秒的結果。

  • 總RAM:64 GB
  • shared_buffers:16 GB
  • work_mem:32 MB

為什么postgresql忽略user索引並且正在使用timestamp索引(時間戳索引需要查看所有記錄)? 是否有任何postgresql配置參數可以更改,以使查詢使用用戶名索引本身?

好問題,我剛才解決了這個問題。

為什么會這樣?

你應該在你的統計數據中查看user='abcd'值的數量,如下所示:

SELECT attname, null_frac, ag_width, n_distinct,
       most_common_vals, most_common_freqs, histogram_bounds
  FROM pg_stats
 WHERE table_name='T';

我的猜測是 - 這個值經常發生,你會在most_common_vals輸出中找到它。 most_common_freqs挑選相同的元素,您將獲得該值的比率,將其乘以總行數(可以從pg_class獲得)以獲得估計具有'abcd'值的行

Planner假設所有值都具有線性分布。 實際上,事情當然是不同的。 此外,目前還沒有相關的統計數據盡管正在朝這個方向開展一些工作 )。

所以,讓我們取user='abcd'值,在相應的most_common_freqs條目中具有0.001比率(每個問題)。 這意味着每1000行會出現一次值(假設為線性分布)。 看來,如果我們以任何方式掃描表格我們將在大約1000行中擊中我們的user='abcd' 聽起來應該快! 計划程序“認為”相同並在timestamp列上選擇索引。

但事實並非如此。 如果我們假設您的表T包含用戶活動的日志,並且user='abcd'在過去3周休假,則這意味着我們必須從timestamp索引中讀取相當多的行(在我們真正達到我們想要的行之前,我們需要3周的數據。 嗯,你作為DBA知道這一點,但計划者假設線性分布。

那么,如何解決?

您必須欺騙規划人員使用您需要的東西,因為您對數據有更多了解。

  1. 在子查詢中使用OFFSET 0技巧

     SELECT * FROM ( SELECT * FROM T WHERE user='abcd' OFFSET 0 ) ORDER BY timestamp LIMIT 1; 

    這個技巧可以保護查詢不被內聯,因此內部部分可以自己執行。

  2. 使用CTE (命名子查詢):

     WITH s AS ( SELECT * FROM T WHERE user='abcd' ) SELECT * FROM s ORDER BY timestamp LIMIT 1; 

    每個文件:

    WITH查詢的一個有用屬性是,每次執行父查詢時,它們評估一次 ,即使父查詢或兄弟WITH查詢多次引用它們也是如此。

  3. 對countrgated查詢使用count(*)

     SELECT min(session_id), count(*) -- instead of simply `min(session_id)` FROM T WHERE user='abcd' ORDER BY timestamp LIMIT 1; 

    這不是真的適用,但我想提一下。

請考慮升級到9.3。

PS更多關於文檔中的行estiamtes 當然

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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