2012-11-24 49 views
5

我需要runnerId的最小值。Postgres min函數性能

這個查詢:

SELECT "runnerId" FROM betlog WHERE "marketId" = '107416794' ; 

需要80毫秒(1968結果行)。

此:

SELECT min("runnerId") FROM betlog WHERE "marketId" = '107416794' ; 

需要1600毫秒。

有沒有更快的方法來找到最小值,或者我應該計算我的Java程序中的最小值?

"Result (cost=100.88..100.89 rows=1 width=0)" 
" InitPlan 1 (returns $0)" 
" -> Limit (cost=0.00..100.88 rows=1 width=9)" 
"   -> Index Scan using runneridindex on betlog (cost=0.00..410066.33 rows=4065 width=9)" 
"    Index Cond: ("runnerId" IS NOT NULL)" 
"    Filter: ("marketId" = 107416794::bigint)" 

CREATE INDEX marketidindex 
    ON betlog 
    USING btree 
    ("marketId" COLLATE pg_catalog."default"); 

另一個想法:

SELECT "runnerId" FROM betlog WHERE "marketId" = '107416794' ORDER BY "runnerId" LIMIT 1 >1600ms 
SELECT "runnerId" FROM betlog WHERE "marketId" = '107416794' ORDER BY "runnerId" >>100ms 

一個LIMIT如何減緩查詢下來?

+0

你有marketid的索引嗎? –

+1

[找出聲明執行這麼長時間的原因是用EXPLAIN命令完成的](http://wiki.postgresql.org/wiki/Using_EXPLAIN) – raina77ow

+0

是的,它有一個索引 – wutzebaer

回答

8

你需要的是一個multi-column index

CREATE INDEX betlog_mult_idx ON betlog ("marketId", "runnerId"); 

如果有興趣,你會發現關於PostgreSQL的多列索引深入的信息,this related question on dba.SE下鏈接和基準。

我是怎麼想的?
在多列索引中,行由索引的第一列(「marketId」)排序(並因此聚集),並且每個集羣依次由索引的第二列排序 - 因此第一行匹配條件min("runnerId")。這使得索引掃描速度非常快。

關於LIMIT放緩查詢的佯謬效應 - Postgres查詢規劃器在那裏存在一個弱點。常見的解決方法是使用CTE(在這種情況下,必須使用而不是)。最近這次,密切相關的問題,在查找更多信息:
PostgreSQL query taking too long

+0

這個問題解決了,你能給我一點背景嗎?你是如何認識到這一點的? – wutzebaer

+1

@wutzebaer:我添加了一個指向手冊的鏈接,指向一個問題的鏈接,其中包含有關多列索引的更多信息和一些解釋。 –

+0

這真的很奇怪 - 什麼是從「壞」查詢解釋?處理4065行不應占用1500毫秒。 –

1

min語句將由PostgreSQL使用整個表的順序掃描來執行。您可以使用以下方法優化查詢: SELECT col FROM sometable ORDER BY col ASC LIMIT 1;

+0

只是命令ist fast too >> SELECT 「runnerId」FROM betlog WHERE「marketId」='107416794'ORDER BY「runnerId」<<但是當我在1600 ms再加上「LIMIT 1」時 – wutzebaer

+0

所以基本上你可以使用沒有限制語句的order by方法。這應該優化您的查詢。 –

+0

好的,但限制如何減慢查詢速度?它的問題,因爲我想使用這個查詢作爲子查詢 – wutzebaer

1

當你對("runnerId")索引(或至少"runnerId"爲高階列),但並沒有對("marketId", "runnerId")索引它相比的成本使用該列上的索引傳遞具有匹配的"marketId"的所有行,並從該集合中挑選出最小的"runnerId"到使用"runnerId"上的索引進行掃描的成本,並在找到匹配的第一行"marketId"時停止。根據可用的統計數據和假設"marketId"值將隨機分佈在"runnerId"上的索引的索引條目內,它估計後一方法的成本較低。

它還估計掃描整個表的成本,並從匹配的行中挑選最小值以及可能的其他替代方法。它並不總是使用某種類型的計劃,但比較了所有替代方案的成本。

問題是假設值將隨機分佈在範圍內並不一定是真實的(如本例中),導致掃描範圍的高百分比以找到潛伏在最後的行。對於"marketId"的某些值,在"runnerId"索引開始附近選擇的值可用,此計劃應該非常快。

PostgreSQL開發人員社區已經討論過,如果數據分佈不是假定的,那麼我們如何偏向對長期運行而言是「風險」的計劃,並且已經開始跟蹤多列統計數據,以便相關值不會遇到這樣的問題。在接下來的幾個版本中,預計這方面會有所改進。在此之前,歐文的建議是針對如何解決這個問題的目標。

基本上,它歸結爲制定一個更具吸引力的計劃可用或引入優化障礙。在這種情況下,您可以通過在("marketId", "runnerId")上添加索引來提供更具吸引力的選項 - 這可以非常直接地直接找到答案。該計劃者爲該替代方案分配了一個非常低的成本,導致它被選中。

SELECT min("runnerId") 
    FROM (SELECT "runnerId" FROM betlog 
      WHERE "marketId" = '107416794' 
      OFFSET 0) x; 

當有OFFSET條款(即使對於零偏移)它迫使子查詢單獨計劃:如果你寧願不添加索引,你可以做這樣的事情迫使優化屏障並將其結果反饋給外部查詢。我預計這會在80毫秒內運行,而不是沒有優化障礙的1600毫秒。當然,如果您可以添加索引,那麼數據緩存時查詢的速度應該小於1毫秒。