3

我正在運行此Postgres問題,如果在查詢字符串中使用參數vs硬編碼其值,則相同查詢需要很長時間才能執行。列名是'media_type',它是一個VARCHAR(20)。我從PHP運行這些查詢,使用Symfony2和Doctrine2 ORM,並且有問題的表有大約1.000.000條記錄。當使用參數代替硬編碼字符串時,Postgres查詢速度很慢

我的查詢有問題嗎?它可以是Postgres配置問題嗎?

1 - 爲MEDIA_TYPE硬編碼值

duration: 5.365 ms parse pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4 
duration: 0.142 ms bind pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4 
parameters: $1 = '1', $2 = '1', $3 = '100', $4 = '0' 
duration: 8.667 ms execute pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4 
parameters: $1 = '1', $2 = '1', $3 = '100', $4 = '0' 

執行計劃:

duration: 8.640 ms plan: 
    Query Text: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4 
    Limit (cost=8.38..8.38 rows=1 width=12) (actual time=8.516..8.595 rows=24 loops=1) 
     Buffers: shared hit=10 read=15 
     -> Sort (cost=8.38..8.38 rows=1 width=12) (actual time=8.505..8.530 rows=24 loops=1) 
      Sort Key: id 
      Sort Method: quicksort Memory: 26kB 
      Buffers: shared hit=10 read=15 
      -> Index Scan using item_media_type_index on item (cost=0.00..8.37 rows=1 width=12) (actual time=7.955..8.397 rows=24 loops=1) 
        Index Cond: ((media_type)::text = 'Collection'::text) 
        Filter: (enabled AND (site_id = $1) AND (user_id = $2)) 
        Buffers: shared hit=8 read=15 

2 - 使用的參數爲媒體類型(更慢)

duration: 5.557 ms parse pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5 
duration: 1.322 ms bind pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5 
parameters: $1 = 'Collection', $2 = '1', $3 = '1', $4 = '100', $5 = '0' 
duration: 71564.998 ms execute pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5 
parameters: $1 = 'Collection', $2 = '1', $3 = '1', $4 = '100', $5 = '0' 

執行計劃:

duration: 71564.922 ms plan: 
    Query Text: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5 
    Limit (cost=90663.16..181326.31 rows=17184 width=12) (actual time=3.667..71564.864 rows=24 loops=1) 
     Buffers: shared hit=183786 read=96585 
     -> Index Scan Backward using item_pkey on item (cost=0.00..906610.46 rows=171836 width=12) (actual time=3.655..71564.798 rows=24 loops=1) 
       Filter: (enabled AND ((media_type)::text = $1) AND (site_id = $2) AND (user_id = $3)) 
       Buffers: shared hit=183786 read=96585 

在此先感謝。

回答

8

這是PostgreSQL中一個長期存在的疣,歷史上需要一些有趣的策劃者調整才能解決。它在PostgreSQL 9.2中得到修復(現在在測試版中),儘管像往常一樣,感謝Tom Lane。

E.1.3.1.3。優化

提高規劃者的選擇參數計劃(湯姆 巷)

已準備語句現在解析,分析和改寫,但不 一定計劃的能力。當準備好的計劃以 參數執行時,計劃人員可能會爲每個常量重新計劃該計劃,或者如果 的成本接近 特定於常規的計劃,則可能會執行一般計劃。

查看9.2 beta release notesquick note I wrote about this on lwn.net。關於在mailing lists上處理準備/參數化語句比正常運行速度慢的信息有很多。

+0

謝謝! PostgreSQL 9.2有預期的發佈日期嗎?我沒有幸運地找到它。另外,PostgreSQL測試版通常有多穩定? – luis

+0

感謝您的好消息。 (和往常一樣,感謝Tom Lane)。 –

+1

@luis我儘可能爲我的開發工作運行PostgreSQL測試版,並且從未遇到過問題。但是,我沒有在生產中使用它們。如果你仍然處於開發階段,使用測試版是不容易的。對於生產 - 呃,一般來說Pg是非常穩定的,雖然這裏偶爾會遇到一些危險的錯誤,但由於迴歸測試的緣故,它們通常處於奇怪的角落。我不能建議你在生產環境中運行測試版 - 它的測試版有一個原因,團隊認爲它沒有準備好 - 但如果我必須面對嚴重問題,我會這樣做。如果可能,請解決並等待發布。 –

1

我遇到了一個非常類似的問題,當我綁定到SMALLINT字段並傳遞一個Postgres隱式地從INTEGER轉換爲SMALLINT的值時。我通過明確演員來修正它。由於media_type是VARCHAR(20)類型,因此Postgres正在從TEXT類型進行隱式轉換。試試這個:

where media_type = $1::VARCHAR(20)

+0

這看起來很值得嘗試 - 儘管它也在進行更有效的索引查找計劃。 – Edmund

+0

給它一個嘗試,但沒有爲我工作。 – luis

1

在您的靜,更快的查詢,正在使用的item_media_type_index。 在綁定的較慢查詢中,未使用item_media_type_index。

「item」表中「media_type」列的選擇性是什麼?如果你做的:

SELECT media_type, COUNT(*) 
    FROM item 
    GROUP BY media_type 
    ORDER BY 2 desc 

是media_types均衡的,還是有相對較少的「合集」 media_types比別人?如果「收集」項目相對較少,那麼我就會冒這個猜測:對於靜態查詢,解析器知道您在查詢「集合」並可以確定「集合」的計數較低,並且索引可能值得使用。但在綁定變量的情況下,解析器不知道您正在使用哪個media_type。其他一些media_type值可能佔到表中記錄的很大比例(比如20%)。在這種情況下,進行掃描比直接使用索引要快。解析器需要做出決定,並且恰好決定不使用索引(對於您的情況錯誤,但可能對其他media_type正確)。這只是基於其他一些rdbms如何工作的猜測。

在這種情況下,如果您知道選擇性屬性非常偏斜,請使用動態sql強制延遲解析,或者強制索引使用(如果您認爲正確),則答案爲硬編碼。