[英]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.