2017-01-10 35 views
0

我運行簡單的查詢:的PostgreSQL和ActiveRecord的 - 慢查詢

History.where(channel_id: 1).order('histories.id DESC').first 

結果:

History Load (808.8ms) SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 ORDER BY histories.id DESC LIMIT 1 [["channel_id", 1]] 

808.8ms 7 1的記錄與CHANNEL_ID = 1。總的歷史數爲2110443。

如果我選擇CHANNEL_ID所有歷史= 1:

History.where(channel_id: 1) 

History Load (0.5ms) SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 [["channel_id", 1]] 

只花了0.5ms的

如果我們試圖採取一個紀錄紅寶石陣列的幫助:

History.where(channel_id: 1).order('histories.id DESC').to_a.first 

History Load (0.5ms) SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 ORDER BY id DESC [["channel_id", 1]] 

我應該在哪裏找到問題?

PS:我已經有了channel_id字段的索引。

UPD:

History.where(channel_id: 1).order('histories.id DESC').limit(1).explain 
    History Load (848.9ms) SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 ORDER BY histories.id DESC LIMIT 1 [["channel_id", 1]] 
=> EXPLAIN for: SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 ORDER BY histories.id DESC LIMIT 1 [["channel_id", 1]] 
               QUERY PLAN 
------------------------------------------------------------------------------------------------------- 
Limit (cost=0.43..13.52 rows=1 width=42) 
    -> Index Scan Backward using histories_pkey on histories (cost=0.43..76590.07 rows=5849 width=42) 
     Filter: (channel_id = 1) 
(3 rows) 
+1

通常,'channel_id,id'複合索引應該有所幫助。如果你的用例是特殊的,即你只需要通道'1',你可以設置一個[部分索引](https://www.postgresql.org/docs/current/static/indexes-partial.html) 。 – pozs

+0

@pozs謝謝你,通過複合索引'channel_id,id'解決 – zolter

回答

0

有兩種方法的PostgreSQL可以處理你的查詢(與ORDER BY和LIMIT子句):

  • 它可以掃描表,並責令發現的元組,然後限制你的結果。如果PostgreSQL認爲你的表的元組數量非常少,或者他認爲索引不起作用,PostgreSQL將選擇這個計劃;
  • 它可以使用索引。

看來,PostgreSQL的做選擇第一個選項,它可以發生在我的愚見只有兩個原因:

  • 你的表的統計信息是不準確的。我建議你真空你的表,然後再試一次這個查詢
  • channel_id值是不均勻分佈的(例如幾乎所有的元組都有channel_id = 2),這就是爲什麼PostgreSQL認爲索引是沒用的原因。這裏我建議使用部分索引。