2011-05-03 14 views
4

我正在與一家正在美國繪製出太陽能潛力的非營利機構合作。不用說,我們有一個可笑的大型PostgreSQL 9數據庫。運行如下所示的查詢會很快,直到order by行被取消註釋爲止,在這種情況下,相同的查詢需要永久運行(185毫秒沒有排序,相比之下25分鐘)。應該採取哪些步驟來確保這些查詢和其他查詢能夠在更可管理和合理的時間內運行?使用ORDER BY時數據庫性能不佳

select A.s_oid, A.s_id, A.area_acre, A.power_peak, A.nearby_city, A.solar_total 
from global_site A cross join na_utility_line B 
where (A.power_peak between 1.0 AND 100.0) 
and A.area_acre >= 500 
and A.solar_avg >= 5.0 
AND A.pc_num <= 1000 
and (A.fips_level1 = '06' AND A.fips_country = 'US' AND A.fips_level2 = '025') 
and B.volt_mn_kv >= 69 
and B.fips_code like '%US06%' 
and B.status = 'active' 
and ST_within(ST_Centroid(A.wkb_geometry), ST_Buffer((B.wkb_geometry), 1000)) 
--order by A.area_acre 
offset 0 limit 11; 
+2

查詢返回多少行?在每種情況下通過EXPLAIN ANALYZE運行查詢時,輸出是什麼? – 2011-05-03 17:53:45

+2

解決該問題的第一步是查看解釋分析*查詢的*兩個*版本的輸出。如果你不能閱讀它(它不是非常友好的),把它放在http://explain.depesz.com/並給我們兩個計劃的鏈接。 – 2011-05-03 18:10:17

+0

該特定查詢的結果只有11行。 Explain Analyze的輸出沒有'order by'與WITH WITH的基本相同,減去排序行所用的117117 ms。 – 2011-05-03 18:21:48

回答

5

的排序是沒有問題的 - 事實上排序的CPU和內存的成本接近於零,因爲Postgres有頂-N排序,其中,同時保持最新一小類緩衝區只有抱着結果集進行掃描前N行。

select count(*) from (1 million row table)    -- 0.17 s 
select * from (1 million row table) order by x limit 10; -- 0.18 s 
select * from (1 million row table) order by x;   -- 1.80 s 

所以你看到前10排序只增加了10毫秒到一個愚蠢的快速計數(*)與一個真正的排序更長的時間。這是一個非常整潔的功能,我使用它很多。現在沒有EXPLAIN

OK分析它不可能是肯定的,但我的感覺是,真正的問題是交叉連接。基本上你使用以下兩種表格過濾行:

where (A.power_peak between 1.0 AND 100.0) 
and A.area_acre >= 500 
and A.solar_avg >= 5.0 
AND A.pc_num <= 1000 
and (A.fips_level1 = '06' AND A.fips_country = 'US' AND A.fips_level2 = '025') 

and B.volt_mn_kv >= 69 
and B.fips_code like '%US06%' 
and B.status = 'active' 

好的。我不知道有多少行兩個表中選擇(只能解釋ANALYZE會告訴),但它可能顯著。知道這些數字會有所幫助。

然後,我們得到了最糟糕的情況下CROSS JOIN條件永遠

and ST_within(ST_Centroid(A.wkb_geometry), ST_Buffer((B.wkb_geometry), 1000)) 

這意味着A的所有行對B的所有行匹配(所以,這個表達式將被評估了大量的時間),使用了一堆非常複雜,慢速和cpu密集型功能。

當然,這是可怕的慢!

當您刪除ORDER BY,Postgres的只是上來(偶然?)帶着一幫在一開始的匹配行,它們輸出,並停止由於達到了極限。

這裏有一個小例子:

表A和B是相同的且包含1000行,和類型BOX的列。

select * from a cross join b where (a.b && b.b)  --- 0.28 s 

這裏百萬框重疊(操作者& &)測試在0.28s完成。測試數據集生成後,結果集只包含1000行。

create index a_b on a using gist(b); 
create index b_b on a using gist(b); 
select * from a cross join b where (a.b && b.b)  --- 0.01 s 

這裏索引用來優化交叉連接,速度很荒謬。

你需要以優化的幾何形狀匹配。

  • 添加這將緩存列:
  • ST_Buffer((B.wkb_geometry),1000)
    • ST_Centroid(A.wkb_geometry)

有在重新計算NO POINT在CROSS JOIN期間,這些函數會慢一百萬次,所以將結果存儲在一列中。使用觸發器使其保持最新狀態。

  • 添加這將緩存類型BOX的列:

      ST_Centroid的
    • 包圍盒(A.wkb_geometry)
    • ST_Buffer的
    • 包圍盒((B.wkb_geometry),1000)
  • 在盒子上添加要點索引

  • 添加盒子ove rlap測試(使用& &運營商)將使用索引

  • 保持你的ST_Within將作爲對通過行的最後過濾器

也許你可以只指數ST_Centroid和ST_Buffer列...並使用(索引)「包含」操作符,在這裏看到:

http://www.postgresql.org/docs/8.2/static/functions-geometry.html

+0

謝謝,這有很大的幫助。 – 2011-05-06 15:39:51

+0

謝謝;)你能解決你的性能問題嗎? – peufeu 2011-05-06 20:37:05

+0

表達式的索引,任何人? :-) http://stackoverflow.com/a/22690775/618649 – Craig 2014-03-29 15:50:23

0

首先我想看看創建索引,確保你的數據庫是被抽成真空,增加共享緩存爲你的數據庫安裝,work_mem設置。

2

我會建議在area_acre上創建一個索引。你可能想看看下面的內容:http://www.postgresql.org/docs/9.0/static/sql-createindex.html

我會推薦做高峯時間以外的事情,因爲這可能會對大量的數據有些密集。有一件事你必須看看索引是rebuilding他們在一個時間表,以確保性能隨着時間的推移。這個時間表再次應該在高峯時段之外。

你可能想看看這篇文章從一個老鄉SO'er,他與數據庫減速經驗隨着時間的推移指標:Why does PostgresQL query performance drop over time, but restored when rebuilding index

+0

正在討論的表已被編入索引CREATE INDEX global_site_area_acre_idx ON global_site USING btree(area_acre DESC);' - 有沒有更好的方法來做到這一點? – 2011-05-03 19:22:32

+0

在這樣的索引中使用DESC沒有意義。 – 2011-05-03 19:46:11

0

看的第一件事是你是否有在球場上的索引你正在點餐。如果不是,增加一個將顯着提高性能。我不知道那的PostgreSQL很好,但類似的東西:

CREATE INDEX area_acre ON global_site(area_acre) 

正如其他答覆指出,與大型數據集工作時,索引過程是密集的,所以非高峯期間做到這一點。

+0

該字段確實已編入索引。索引的完成類似於CREATE INDEX global_site_area_acre_idx ON global_site USING btree (area_acre DESC);'。這有什麼不對嗎,和/或可以調整嗎? – 2011-05-03 18:23:48

1

如果A.area_acre字段沒有編入索引可能會降低速度。您可以使用EXPLAIN運行查詢以查看執行過程中的操作。

0

我不熟悉PostgreSQL的優化,但它聽起來像是當查詢與ORDER BY子句運行所發生的事情是,整個結果集被創建,然後進行排序,然後將前11行取自該排序結果。如果沒有ORDER BY,查詢引擎可以按照喜歡的順序生成前11行,然後完成。

area_acre字段上有一個索引很可能可能無助於排序(ORDER BY),具體取決於結果集的構建方式。它理論上可以用於通過使用area_acre上的索引遍歷global_site表來生成結果集;在這種情況下,結果將按照所需的順序生成(並且在結果中生成11行後它可能會停止)。如果它不按照該順序生成結果(並且看起來可能不是這樣),那麼該索引將無助於排序結果。

您可能會嘗試的一件事是從查詢中刪除「CROSS JOIN」。我懷疑這會有所作爲,但值得一試。由於涉及連接兩個表的WHERE子句(通過ST_WITHIN),我相信結果與內部連接相同。使用CROSS JOIN語法可能會導致優化器做出不理想的選擇。除了確保被篩選字段的索引存在以外,您可以使用查詢來玩一些猜謎遊戲。突出的一個條件是area_acre >= 500。這意味着查詢引擎正在考慮符合該條件的所有行。但是隻有前11行被採用。您可以嘗試將其更改爲area_acre >= 500 and area_acre <= somevalue。該somevalue是需要調整,以確保你得到至少11行猜測的部分。然而,這似乎是一件很俗氣的事情,所以我用一些沉默來提起它。