2017-04-15 66 views
1

explain analyze表明Postgres將使用索引掃描我的查詢,其獲取的行和由日期執行濾波(即,2017-04-14 05:27:51.039):postgres如何決定是使用索引掃描還是seq掃描?

explain analyze select * from tbl t where updated > '2017-04-14 05:27:51.039'; 
                  QUERY PLAN               
----------------------------------------------------------------------------------------------------------------------------- 
    Index Scan using updated on tbl t (cost=0.43..7317.12 rows=10418 width=93) (actual time=0.011..0.515 rows=1179 loops=1) 
    Index Cond: (updated > '2017-04-14 05:27:51.039'::timestamp without time zone) 
    Planning time: 0.102 ms 
    Execution time: 0.720 ms 

然而運行相同的查詢,但具有不同的日期過濾器「2016年4月14日05:27:51.039' 顯示,Postgres將使用了序列掃描,而不是運行查詢:

explain analyze select * from tbl t where updated > '2016-04-14 05:27:51.039'; 
                 QUERY PLAN              
----------------------------------------------------------------------------------------------------------------------- 
Seq Scan on tbl t (cost=0.00..176103.94 rows=5936959 width=93) (actual time=0.008..2005.455 rows=5871963 loops=1) 
    Filter: (updated > '2016-04-14 05:27:51.039'::timestamp without time zone) 
    Rows Removed by Filter: 947 
Planning time: 0.100 ms 
Execution time: 2910.086 ms 

按日期進行過濾時如何Postgres的決定使用什麼,特別是?

回答

2

Postgres查詢規劃師根據成本估算和表格統計信息做出決策。表統計信息由ANALYZE收集,並通過一些其他實用程序命令進行機會性收集。這一切都會在autovacuum開啓時自動發生(默認情況下)。

The manual:

大部分查詢只檢索行的一小部分在一個表中,由於 WHERE條款,限制要檢查的行。因此,規劃者 需要估計WHERE條款的選擇性,即 是與條款中的每個條件匹配的行的分數。用於此任務的信息存儲在系統目錄中的 pg_statistic中。 pg_statistic中的條目通過 更新爲ANALYZEVACUUM ANALYZE命令,並且即使在新鮮更新時也總是近似爲 。

有一個行數(在pg_class),最常見的值的列表等

的多個行的Postgres期望找到,就越有可能會切換到一個順序掃描,這是更便宜地檢索大部分表格。

通常,它是索引掃描 - >位圖索引掃描 - >順序掃描,預計檢索的行越多。

對於您的特定示例,重要的統計信息是histogram_bounds,它給Postgres一個大概的想法,有多少行比給定的行有更大的值。有對人眼更方便查看pg_stats

SELECT histogram_bounds FROM pg_stats 
WHERE tablename='tbl' AND attname='updated'; 

有一個dedicated chapter explaining row estimation in the manual.

1

顯然,查詢優化是棘手的。這個答案不打算深入Postgres優化器的細節。相反,它的目的是給你一些關於如何使用索引的決定的背景知識。

您的第一個查詢估計會返回10,418行。使用索引時,會發生以下操作:

  • 引擎使用索引查找滿足條件的第一個值。
  • 然後引擎循環遍歷值,當條件不再爲真時結束。
  • 對於索引中的每個值,引擎然後在數據頁面上查找數據。

換句話說,使用索引時有一點點的開銷 - 初始化索引然後單獨查找每個數據頁。

當發動機執行全表掃描一下:

  • 開始以第一名的成績第一個頁面
  • 是否比較和接受或通過所有數據頁拒絕該記錄
  • 順序繼續進行

沒有額外的開銷。此外,引擎可以在處理當前頁面的同時「預加載」下一個要掃描的頁面。 I/O和處理的重疊是一個巨大的勝利。

我試圖做的一點是獲得這兩者之間的平衡可能會非常棘手。在10,418到5,936,959之間,Postgres決定索引開銷(隨機讀取頁面)花費的不僅僅是掃描整個表格。