簡體   English   中英

優化postgresql查詢

[英]Optimize postgresql query

我在PostgreSQL 9.1中有2個表 - flight_2012_09_12包含大約500,000行,position_2012_09_12包含大約550萬行。 我正在運行一個簡單的連接查詢,它需要很長時間才能完成,盡管表格不小,但我確信在執行過程中會有一些重大收獲。

查詢是:

SELECT f.departure, f.arrival, 
       p.callsign, p.flightkey, p.time, p.lat, p.lon, p.altitude_ft, p.speed 
FROM position_2012_09_12 AS p 
JOIN flight_2012_09_12 AS f 
     ON p.flightkey = f.flightkey 
WHERE p.lon < 0 
      AND p.time BETWEEN '2012-9-12 0:0:0' AND '2012-9-12 23:0:0'

解釋分析的輸出是:

Hash Join  (cost=239891.03..470396.82 rows=4790498 width=51) (actual time=29203.830..45777.193 rows=4403717 loops=1)
Hash Cond: (f.flightkey = p.flightkey)
->  Seq Scan on flight_2012_09_12 f  (cost=0.00..1934.31 rows=70631 width=12) (actual time=0.014..220.494 rows=70631 loops=1)
->  Hash  (cost=158415.97..158415.97 rows=3916885 width=43) (actual time=29201.012..29201.012 rows=3950815 loops=1)
     Buckets: 2048  Batches: 512 (originally 256)  Memory Usage: 1025kB
     ->  Seq Scan on position_2012_09_12 p  (cost=0.00..158415.97 rows=3916885 width=43) (actual time=0.006..14630.058 rows=3950815 loops=1)
           Filter: ((lon < 0::double precision) AND ("time" >= '2012-09-12 00:00:00'::timestamp without time zone) AND ("time" <= '2012-09-12 23:00:00'::timestamp without time zone))
Total runtime: 58522.767 ms

我認為問題在於位置表上的順序掃描,但我無法弄清楚它為什么存在。 帶索引的表結構如下:

               Table "public.flight_2012_09_12"
   Column       |            Type             | Modifiers 
--------------------+-----------------------------+-----------
callsign           | character varying(8)        | 
flightkey          | integer                     | 
source             | character varying(16)       | 
departure          | character varying(4)        | 
arrival            | character varying(4)        | 
original_etd       | timestamp without time zone | 
original_eta       | timestamp without time zone | 
enroute            | boolean                     | 
etd                | timestamp without time zone | 
eta                | timestamp without time zone | 
equipment          | character varying(6)        | 
diverted           | timestamp without time zone | 
time               | timestamp without time zone | 
lat                | double precision            | 
lon                | double precision            | 
altitude           | character varying(7)        | 
altitude_ft        | integer                     | 
speed              | character varying(4)        | 
asdi_acid          | character varying(4)        | 
enroute_eta        | timestamp without time zone | 
enroute_eta_source | character varying(1)        | 
Indexes:
"flight_2012_09_12_flightkey_idx" btree (flightkey)
"idx_2012_09_12_altitude_ft" btree (altitude_ft)
"idx_2012_09_12_arrival" btree (arrival)
"idx_2012_09_12_callsign" btree (callsign)
"idx_2012_09_12_departure" btree (departure)
"idx_2012_09_12_diverted" btree (diverted)
"idx_2012_09_12_enroute_eta" btree (enroute_eta)
"idx_2012_09_12_equipment" btree (equipment)
"idx_2012_09_12_etd" btree (etd)
"idx_2012_09_12_lat" btree (lat)
"idx_2012_09_12_lon" btree (lon)
"idx_2012_09_12_original_eta" btree (original_eta)
"idx_2012_09_12_original_etd" btree (original_etd)
"idx_2012_09_12_speed" btree (speed)
"idx_2012_09_12_time" btree ("time")

          Table "public.position_2012_09_12"
Column    |            Type             | Modifiers 
-------------+-----------------------------+-----------
 callsign    | character varying(8)        | 
 flightkey   | integer                     | 
 time        | timestamp without time zone | 
 lat         | double precision            | 
 lon         | double precision            | 
 altitude    | character varying(7)        | 
 altitude_ft | integer                     | 
 course      | integer                     | 
 speed       | character varying(4)        | 
 trackerkey  | integer                     | 
 the_geom    | geometry                    | 
Indexes:
"index_2012_09_12_altitude_ft" btree (altitude_ft)
"index_2012_09_12_callsign" btree (callsign)
"index_2012_09_12_course" btree (course)
"index_2012_09_12_flightkey" btree (flightkey)
"index_2012_09_12_speed" btree (speed)
"index_2012_09_12_time" btree ("time")
"position_2012_09_12_flightkey_idx" btree (flightkey)
"test_index" btree (lon)
"test_index_lat" btree (lat)

我想不出任何其他方式來重寫查詢,所以我很難過。 如果當前的設置盡可能好,那么在我看來它應該比現在快得多。 任何幫助將非常感激。

行數估計非常合理,所以我懷疑這是一個統計問題。

我試試:

  • 如果您經常搜索lon < 0 position_2012_09_12(lon,"time")position_2012_09_12(lon,"time")或可能是position_2012_09_12("time") WHERE (lon < 0)的部分索引上創建索引position_2012_09_12("time") WHERE (lon < 0)

  • random_page_cost設置random_page_cost較低,可能為1.1。 看(a)這是否改變了計划;(b)新計划是否實際更快。 為了測試目的,看看是否可以更快地避免seqscan,你可以SET enable_seqscan = off ; 如果是,請更改成本參數。

  • 增加此查詢的work_mem 在運行之前SET work_mem = 10M或者其他東西。

  • 如果你還沒有運行最新的PostgreSQL。 始終在問題中指定PostgreSQL版本。 (編輯后更新):你在9.1; 沒關系。 9.2中最大的性能改進是僅索引掃描,並且您似乎不太可能從此查詢的僅索引掃描中大量受益。

如果你可以擺脫列來縮小行,你也會在某種程度上提高性能。 它不會產生太大的影響,但它會產生一些影響。

您獲得順序掃描的原因是Postgres認為它會比使用索引讀取更少的磁盤頁面。 這可能是對的。 考慮一下,如果使用非覆蓋索引,則需要讀取所有匹配的索引頁面。 它本質上輸出行標識符列表。 然后,數據庫引擎需要讀取每個匹配的數據頁面。

你的位置表每行使用71個字節,加上一個geom類型(我假設16個字節用於說明),產生87個字節。 Postgres頁面是8192個字節。 所以每頁大約有90行。

您的查詢與5563070行中的3950815匹配,或約占總數的70%。 假設數據是隨機分布的,關於你的where過濾器,找到沒有匹配行的數據頁面幾乎有30%^ 90的可能性。 這基本上沒什么。 因此,無論索引有多好,您仍然需要閱讀所有數據頁。 如果您還要閱讀所有頁面,表格掃描通常是一種很好的方法。

一個人離開這里,是我說非覆蓋指數。 如果您准備創建可以自己回答查詢的索引,則可以避免查找數據頁面,這樣您就可以重新進入游戲。 我建議以下值得關注:

flight_2012_09_12 (flightkey, departure, arrival)
position_2012_09_12 (filghtkey, time, lon, ...)
position_2012_09_12 (lon, time, flightkey, ...)
position_2012_09_12 (time, long, flightkey, ...)

這里的點代表您選擇的其余列。 你只需要一個位置上的指數,但很難說哪個指數最好。 第一種方法可以允許在預先排序的數據上進行合並連接,其中讀取整個第二索引的成本進行過濾。 第二個和第三個將允許預過濾數據,但需要散列連接。 給出在散列連接中看起來有多少成本,合並連接可能是一個不錯的選擇。

由於您的查詢需要每行87個字節中的52個,並且索引有開銷,因此您可能不會最終獲得索引占用的空間(如果有的話),而不是表本身。

另一種方法是通過查看聚類來攻擊它的“隨機分布”一側。

暫無
暫無

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

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