2012-07-26 12 views
8

I mają następujące zapytaniePostgreSQL: Dolny limit, wolniej zapytanie

SELECT translation.id 
FROM "TRANSLATION" translation 
    INNER JOIN "UNIT" unit 
    ON translation.fk_id_unit = unit.id 
    INNER JOIN "DOCUMENT" document 
    ON unit.fk_id_document = document.id 
WHERE document.fk_id_job = 3665 
ORDER BY translation.id asc 
LIMIT 50 

Prowadzi do straszliwych 110 sekund.

Wielkości tabeli:

+----------------+-------------+ 
| Table   | Records  | 
+----------------+-------------+ 
| TRANSLATION | 6,906,679 | 
| UNIT   | 6,906,679 | 
| DOCUMENT  |  42,321 | 
+----------------+-------------+ 

Jednak, kiedy należy zmienić parametr LIMIT od 50 do 1000, zapytanie kończy się w 2 sekund.

Oto plan kwerend dla powolnego jednego

Limit (cost=0.00..146071.52 rows=50 width=8) (actual time=111916.180..111917.626 rows=50 loops=1) 
    -> Nested Loop (cost=0.00..50748166.14 rows=17371 width=8) (actual time=111916.179..111917.624 rows=50 loops=1) 
     Join Filter: (unit.fk_id_document = document.id) 
    -> Nested Loop (cost=0.00..39720545.91 rows=5655119 width=16) (actual time=0.051..15292.943 rows=5624514 loops=1) 
      -> Index Scan using "TRANSLATION_pkey" on "TRANSLATION" translation (cost=0.00..7052806.78 rows=5655119 width=16) (actual time=0.039..1887.757 rows=5624514 loops=1) 
      -> Index Scan using "UNIT_pkey" on "UNIT" unit (cost=0.00..5.76 rows=1 width=16) (actual time=0.002..0.002 rows=1 loops=5624514) 
       Index Cond: (unit.id = translation.fk_id_translation_unit) 
    -> Materialize (cost=0.00..138.51 rows=130 width=8) (actual time=0.000..0.006 rows=119 loops=5624514) 
      -> Index Scan using "DOCUMENT_idx_job" on "DOCUMENT" document (cost=0.00..137.86 rows=130 width=8) (actual time=0.025..0.184 rows=119 loops=1) 
       Index Cond: (fk_id_job = 3665) 

i do szybkiego jednym

Limit (cost=523198.17..523200.67 rows=1000 width=8) (actual time=2274.830..2274.988 rows=1000 loops=1) 
    -> Sort (cost=523198.17..523241.60 rows=17371 width=8) (actual time=2274.829..2274.895 rows=1000 loops=1) 
     Sort Key: translation.id 
     Sort Method: top-N heapsort Memory: 95kB 
     -> Nested Loop (cost=139.48..522245.74 rows=17371 width=8) (actual time=0.095..2252.710 rows=97915 loops=1) 
      -> Hash Join (cost=139.48..420861.93 rows=17551 width=8) (actual time=0.079..2005.238 rows=97915 loops=1) 
       Hash Cond: (unit.fk_id_document = document.id) 
       -> Seq Scan on "UNIT" unit (cost=0.00..399120.41 rows=5713741 width=16) (actual time=0.008..1200.547 rows=6908070 loops=1) 
       -> Hash (cost=137.86..137.86 rows=130 width=8) (actual time=0.065..0.065 rows=119 loops=1) 
        Buckets: 1024 Batches: 1 Memory Usage: 5kB 
        -> Index Scan using "DOCUMENT_idx_job" on "DOCUMENT" document (cost=0.00..137.86 rows=130 width=8) (actual time=0.009..0.041 rows=119 loops=1) 
         Index Cond: (fk_id_job = 3665) 
      -> Index Scan using "TRANSLATION_idx_unit" on "TRANSLATION" translation (cost=0.00..5.76 rows=1 width=16) (actual time=0.002..0.002 rows=1 loops=97915) 
       Index Cond: (translation.fk_id_translation_unit = unit.id) 

widocznie plany wykonawcze są bardzo różne, a drugi wyników w zapytaniu 50 razy szybciej.

Mam indeksy we wszystkich polach związanych z zapytaniem i uruchomiłem ANALYZE na wszystkich tabelach tuż przed uruchomieniem zapytań.

Czy ktoś może zobaczyć, co jest nie tak z pierwszym zapytaniem?

UPDATE: Definicje Tabela

CREATE TABLE "public"."TRANSLATION" (
    "id" BIGINT NOT NULL, 
    "fk_id_translation_unit" BIGINT NOT NULL, 
    "translation" TEXT NOT NULL, 
    "fk_id_language" INTEGER NOT NULL, 
    "relevance" INTEGER, 
    CONSTRAINT "TRANSLATION_pkey" PRIMARY KEY("id"), 
    CONSTRAINT "TRANSLATION_fk" FOREIGN KEY ("fk_id_translation_unit") 
    REFERENCES "public"."UNIT"("id") 
    ON DELETE CASCADE 
    ON UPDATE NO ACTION 
    DEFERRABLE 
    INITIALLY DEFERRED, 
    CONSTRAINT "TRANSLATION_fk1" FOREIGN KEY ("fk_id_language") 
    REFERENCES "public"."LANGUAGE"("id") 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION 
    NOT DEFERRABLE 
) WITHOUT OIDS; 

CREATE INDEX "TRANSLATION_idx_unit" ON "public"."TRANSLATION" 
    USING btree ("fk_id_translation_unit"); 

CREATE INDEX "TRANSLATION_language_idx" ON "public"."TRANSLATION" 
    USING hash ("translation"); 

CREATE TABLE "public"."UNIT" (
    "id" BIGINT NOT NULL, 
    "text" TEXT NOT NULL, 
    "fk_id_language" INTEGER NOT NULL, 
    "fk_id_document" BIGINT NOT NULL, 
    "word_count" INTEGER DEFAULT 0, 
    CONSTRAINT "UNIT_pkey" PRIMARY KEY("id"), 
    CONSTRAINT "UNIT_fk" FOREIGN KEY ("fk_id_document") 
    REFERENCES "public"."DOCUMENT"("id") 
    ON DELETE CASCADE 
    ON UPDATE NO ACTION 
    NOT DEFERRABLE, 
    CONSTRAINT "UNIT_fk1" FOREIGN KEY ("fk_id_language") 
    REFERENCES "public"."LANGUAGE"("id") 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION 
    NOT DEFERRABLE 
) WITHOUT OIDS; 

CREATE INDEX "UNIT_idx_document" ON "public"."UNIT" 
    USING btree ("fk_id_document"); 

CREATE INDEX "UNIT_text_idx" ON "public"."UNIT" 
    USING hash ("text"); 

CREATE TABLE "public"."DOCUMENT" (
    "id" BIGINT NOT NULL, 
    "fk_id_job" BIGINT, 
    CONSTRAINT "DOCUMENT_pkey" PRIMARY KEY("id"), 
    CONSTRAINT "DOCUMENT_fk" FOREIGN KEY ("fk_id_job") 
    REFERENCES "public"."JOB"("id") 
    ON DELETE SET NULL 
    ON UPDATE NO ACTION 
    NOT DEFERRABLE 
) WITHOUT OIDS; 

UPDATE: Parametry bazy

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

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

Czy możesz umieścić definicję stołu? –

+0

@JohnTotetWoo Updated – twoflower

+0

Czy Twoja instalacja została dostrojona? Jakie są ustawienia dla shared_buffers, effective_cache_size, work_mem i specyfikacji systemu? – eevar

Odpowiedz

1

Oto interesująca część oficjalnej dokumentacji ANALYZE.

W przypadku dużych tabel ANALYZE pobiera losową próbkę zawartości tabeli, a nie analizuje każdy wiersz. [...] Zakres analizy można kontrolować, dostosowując zmienną konfiguracyjną default_statistics_target lub kolumnę za kolumną, ustawiając docelową statystykę dla kolumny za pomocą ALTER TABLE ... ALTER COLUMN ... SET STATYSTYKA.

Wygląda na to, że jest to popularny sposób na poprawienie złego planu kwerend. Analiza będzie nieco wolniejsza, ale plan zapytania może być lepszy.

ALTER TABLE

0

W pierwszej kwerendy, optymalizator bierze stretegies pomijające sortowania poprzez skan klucza podstawowego. Problem polega na tym, że wyniki spełniają warunek na document.fk_id są tworzone zbyt rzadko. Dlatego skanowanie indeksu i dołączenie nl powinny zająć dużo miejsca, aby wypełnić pole wynikowe.

Powiązane problemy