簡體   English   中英

再次慢速Postgres 9.3查詢

[英]Slow Postgres 9.3 Queries, again

這是Slow Postgres 9.3 querys問題的后續操作。

新索引絕對有幫助。 但是我們看到的是,有時查詢在實踐中比運行EXPLAIN ANALYZE時要慢得多。 下面是一個在生產數據庫上運行的示例:

explain analyze SELECT * FROM messages WHERE groupid=957 ORDER BY id DESC LIMIT 20 OFFSET 31980;
                                                                       QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=127361.90..127441.55 rows=20 width=747) (actual time=152.036..152.143 rows=20 loops=1)
   ->  Index Scan Backward using idx_groupid_id on messages  (cost=0.43..158780.12 rows=39869 width=747) (actual time=0.080..150.484 rows=32000 loops=1)
         Index Cond: (groupid = 957)
 Total runtime: 152.186 ms
(4 rows)

啟用慢查詢日志記錄后,我們看到此查詢的實例耗時超過2秒。 我們還具有log_lock_waits=true ,並且log_lock_waits=true沒有在同一時間報告慢速鎖定。 有什么可以解釋執行時間的巨大差異?

LIMIT x OFFSET y通常不會比LIMIT x + y快很多。 較大的OFFSET 總是比較昂貴的。 鏈接的問題中建議的索引會有所幫助,但是雖然您無法獲得僅索引的掃描 ,但Postgres仍然必須檢查堆中的可見性(主要關系)至少x + y行,以確定正確的結果。

SELECT *
FROM   messages
WHERE  groupid = 957
ORDER  BY id DESC
LIMIT  20
OFFSET 31980;

索引(groupid,id)上的CLUSTER將有助於增加堆中數據的位置並減少每個查詢要讀取的數據頁數。 絕對是勝利。 但是,如果所有groupid均被查詢的可能性相同,那將不會消除RAM太少而無法緩存的瓶頸。 如果您具有並發訪問權限,請考慮使用pg_repack而不是CLUSTER

您實際上是否需要返回所有列? SELECT * )如果只需要返回一些小列,則啟用僅索引掃描的覆蓋索引可能會有所幫助。 (不過, autovacuum必須足夠強大以應付對表的寫入。只讀表將是理想的選擇。)

另外,根據您所鏈接的問題,您的表在磁盤上為32 GB。 (通常在RAM中多一點)。 (groupid,id)上的索引至少增加了308 MB(沒有膨脹):

SELECT pg_size_pretty(7337880.0 * 44);  -- row count * tuple size

您有8 GB RAM,您期望其中約4.5 GB用於緩存( effective_cache_size = 4608MB緩存大小effective_cache_size = 4608MB )。 這足以緩存索引以供重復使用,但不足以緩存整個表。

如果您的查詢碰巧在高速緩存中找到數據頁,則速度很快。 其他,不是很多。 即使使用SSD存儲,差異也很大(使用HDD則更多)。

與該查詢沒有直接關系,但是8 MB的work_ mem( work_mem = 7864kB )對於您的設置來說似乎很小。 根據各種其他因素,我將其設置為至少64MB(除非您有許多使用sort / hash操作的並發查詢)。 就像@Craig所評論的一樣, EXPLAIN (BUFFERS, ANALYZE)緩沖區EXPLAIN (BUFFERS, ANALYZE)可能會告訴我們更多信息。

最佳查詢計划還取決於價值頻率。 如果只有少數幾行通過過濾器,則對於某些groupid ,結果可能為空,並且查詢相對較快。 如果必須提取表的很大一部分,則將獲得普通的順序掃描。 您需要有效的表格統計信息(再次需要自動autovacuum )。 可能還有更大的groupid統計目標:

由於OFFSET速度較慢,因此,另一種方法是使用另一列和一些索引准備來模擬OFFSET 我們在表上需要一個UNIQUE列(如PRIMARY KEY)。 如果沒有,則可以添加:

CREATE SEQUENCE messages_pkey_seq ;
ALTER TABLE messages 
  ADD COLUMN message_id integer DEFAULT nextval('messages_pkey_seq');

接下來,我們為OFFSET模擬創建position列:

ALTER TABLE messages ADD COLUMN position INTEGER;
UPDATE messages SET position = q.position FROM (SELECT message_id,
  row_number() OVER (PARTITION BY group_id ORDER BY id DESC) AS position
  FROM messages ) AS q WHERE q.message_id=messages.message_id ;
CREATE INDEX ON messages ( group_id, position ) ;

現在我們可以在OP中使用新版本的查詢了:

SELECT * FROM messages WHERE group_id = 957 AND
  position BETWEEN 31980 AND (31980+20-1) ;

暫無
暫無

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

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