2013-11-04 151 views
7

我有這樣PostgreSQL的:。真慢ORDER BY與主鍵排序鍵

enter image description here

與下表大小的模型:

+------------------+-------------+ 
| Table   | Records | 
+------------------+-------------+ 
| JOB    |   8k | 
| DOCUMENT   |  150k | 
| TRANSLATION_UNIT |  14,5m | 
| TRANSLATION  |  18,3m | 
+------------------+-------------+ 

現在下面的查詢

select translation.id 
from "TRANSLATION" translation 
    inner join "TRANSLATION_UNIT" unit 
    on translation.fk_id_translation_unit = unit.id 
    inner join "DOCUMENT" document 
    on unit.fk_id_document = document.id  
where document.fk_id_job = 11698 
order by translation.id asc 
limit 50 offset 0 

大約需要90 seco nd完成。當我刪除ORDER BYLIMIT條款,它需要19.5秒ANALYZE在執行查詢之前已在所有表上運行。

對於這個特定的查詢,這些記錄滿足條件的數字:

+------------------+-------------+ 
| Table   |  Records | 
+------------------+-------------+ 
| JOB    |   1 | 
| DOCUMENT   |  1200 | 
| TRANSLATION_UNIT | 210,000 | 
| TRANSLATION  | 210,000 | 
+------------------+-------------+ 

查詢計劃:

enter image description here

改性用的查詢計劃,而不ORDER BYLIMIThere

數據庫參數:

PostgreSQL 9.2 

shared_buffers = 2048MB 
effective_cache_size = 4096MB 
work_mem = 32MB 

Total memory: 32GB 
CPU: Intel Xeon X3470 @ 2.93 GHz, 8MB cache 

任何人都可以看到什麼是錯此查詢?

UPDATEQuery plan爲無順序相同的查詢BY(但仍與LIMIT條款)。

+0

如何爲Postgre優化器的工作?例如,您可以從您的選擇中進行選擇,並且在沒有優化程序的情況下訂購它,這是兩步選擇? – Paul

+0

只是一個幸運的猜測。你可以嘗試移動連接中的where子句嗎?在這種情況下,只需用'和'替換'where'。 – foibs

+0

@fibibs:那不會有什麼區別。 Postgres優化器足夠聰明,可以檢測到兩個版本都是一樣的。 –

回答

1

你有翻譯的組合索引(fk_id_translation_unit,id)嗎?在我看來,這將有助於避免需要通過表訪問translation.id。

+0

你的意思是組合索引結合'fk_id_translation_unit'和'id'列?我不這樣做,但可以試試看。 – twoflower

+0

我看到的好處是,翻譯表本身並不需要被訪問以檢索所需的數據。 –

+0

嗯,它確實,因爲我需要結果集中的TRANSLATION.id。 PostgreSQL性能論壇上的一個人只是建議對數據庫進行非規範化處理,並將'fk_id_job'直接添加到'TRANSLATION'。 – twoflower

2

這是有點太長的評論。當您刪除order by條款時,您正在比較蘋果和橙子。如果沒有order by,查詢的處理部分只需要提供50行。

隨着order by,所有的行都需要生成之前,他們排序和選擇前幾名。如果刪除order bylimit子句,查詢需要多長時間?

translation.id是主鍵的事實沒有什麼區別,因爲處理過程需要經過多個聯接(篩選結果)。

編輯:

我不知道如何做到這一點的CTE合作,首先創建表,然後又進行排序,並獲取結果:

with CTE as (
    select translation.id 
    from "TRANSLATION" translation 
      inner join "TRANSLATION_UNIT" unit 
      on translation.fk_id_translation_unit = unit.id 
      inner join "DOCUMENT" document 
      on unit.fk_id_document = document.id  
    where document.fk_id_job = 11698 
    ) 
select * 
from CTE 
order by translation.id asc 
limit 50 offset 0; 
+0

你是對的戈登,這兩個查詢是無法比擬的。我只是在沒有'ORDER BY'和'LIMIT'的情況下運行查詢,它需要19.5秒。查詢計劃是[here](http://explain.depesz.com/s/Qs0) – twoflower

+0

@twoflower。 。 。你有大量的數據。我對Postgres的優化參數並不熟悉,但如果您可以增加緩衝區大小以使用更多內存,則可能會看到性能提高。 –

+1

是的,音量非常大,這就是爲什麼我不驚訝它需要19.5秒來獲取__all__記錄。然而,讓我覺得奇怪的是,僅僅訂購這個數據集(cca 212,000條記錄)就又增加了70秒。 – twoflower