2013-11-04 31 views
7

Ben Aşağıdaki tablo boyutları ile buPostgreSQL: Korkunç yavaş SİPARİŞ anahtar sipariş olarak birincil anahtarla TARAFINDAN

enter image description here

gibi bir model: Şimdi

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

aşağıdaki sorgu

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 

yaklaşık yaklaşık 90 alır Sonlandırmak için. SİPARİŞ BY ve LIMIT maddelerini kaldırdığımda, 19.5 saniye alır. ANALYZE, sorguyu yürütmeden hemen önce tüm tablolarda çalıştırılmıştı.

bu özel terimi, bu kriterleri karşılayan kayıtların sayılardır:

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

sorgu planı:

enter image description here

İLE SİPARİŞ olmayan modifikasyon için sorgu planı ve LIMIT, here'dur.

Veritabanı parametreleri:

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 

herkes bu sorgu ile sorun nedir görebilir mi?

GÜNCELLEME: TARAFINDAN SİPARİŞ olmadan aynı sorgu için Query plan (ama yine de SINIR maddesi ile).

+0

nasıl Postgre'nin için optimiser çalışır? Örneğin, seçimlerinizden birini seçebilir ve iyimser olmaksızın bunu ikiye katlayabilir misiniz? – Paul

+0

Şanslı bir tahmin. Birleştirmedeki tümceyi taşımayı deneyebilir misin? Bu durumda, '' 'ile' '' kelimesini değiştirin. – foibs

+0

@foibs: Bu herhangi bir fark yaratmayacak. Postgres optimize edici, her iki versiyonun da aynı olduğunu tespit edecek kadar akıllıdır. –

cevap

1

Çeviri için yerinde birleşik bir dizin var mı (fk_id_translation_unit, id)? Bana öyle görünüyor ki, bu tablo aracılığıyla translation.id'e erişme ihtiyacından kaçınarak yardımcı olacaktır.

+0

"fk_id_translation_unit" ve "id" sütunlarını birleştiren bileşik dizini mi kastediyorsunuz? Yapmıyorum, ama denemelisin. – twoflower

+0

Gördüğüm yarar, gerekli olan verileri almak için çeviri tablosunun hiç erişilmesinin gerekmediği. –

+0

Peki, sonuç kümesinde 'TRANSLATION.id 'öğesine ihtiyacım var. Bir PostgreSQL performans forumundaki bir adam sadece veritabanını denormalize etmeyi ve 'fk_id_job'ı doğrudan 'TRANSLATION'a eklemeyi önerdi. – twoflower

2

Bu, yorum için biraz uzun. order by maddesini kaldırdığınızda elmaları ve portakalları karşılaştırıyorsunuz. order by olmadan, sorgunun işlem kısmının yalnızca 50 satırlık bir sayıya gelmesi gerekir.

order by ile, tüm satırlar sıralanmadan önce ve en az birkaç seçilecek şekilde oluşturulmalıdır. order byvelimit maddesini kaldırırsanız sorgu ne kadar sürer?

translation.id'un birincil anahtar olması, işlemin birkaç ekleme işleminden geçmesi gerektiğinden (sonuçlara filtre uyguladığı için) bir fark yaratmaz.

DÜZENLEME:

İlk tablo oluşturmak için bu bir CTE için nasıl kullanıldığını merak ve sonra başka sonuç sıralamak ve almak için:

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

Haklısınız Gordon, bu iki sorgu birbirinden eşsiz. Sorguyu hem "ORDER BY" ve "LIMIT" olmadan çalıştırdım ve 19.5 saniye sürüyor. Sorgu planı [burada] (http://explain.depesz.com/s/Qs0) – twoflower

+0

@twoflower şeklindedir. . . Çok miktarda veri var. Postgres için optimizasyon parametrelerine çok aşina değilim, ancak daha fazla bellek kullanmak için arabellek boyutlarınızı artırabilirseniz, bir performans artışı görebilirsiniz. –

+1

Evet, ses oldukça büyük, bu yüzden kayıtların __all__ getirilmesi için 19.5 saniye sürdüğünü şaşırmadım. Bununla birlikte, garip olan, garip olan şu ki, sadece bu veri setini (212.000 kayıt) sipariş etmek, 70 saniye daha ekliyor. – twoflower