2012-07-26 8 views
8

ho la seguente queryPostgreSQL: Più basso è il limite, il più lento la query

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 

Funziona per terribili 110 secondi.

Le dimensioni di tabella:

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

Tuttavia, quando cambio il parametro LIMIT da 50 a 1000, la query termina in 2 secondi.

Ecco il piano di query per il lento

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) 

e per il digiuno uno

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) 

A quanto pare i piani di esecuzione sono molto diverse e la seconda si traduce in una query 50 volte più veloce.

Ho indici su tutti i campi coinvolti nella query e ho eseguito ANALYZE su tutte le tabelle appena prima di eseguire le query.

Qualcuno può vedere cosa c'è di sbagliato nella prima query?

UPDATE: definizioni di tabella

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: parametri del database

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

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

puoi pubblicare la definizione della tabella? –

+0

@JohnTotetWoo aggiornato – twoflower

+0

L'installazione è stata ottimizzata? Quali sono le impostazioni per shared_buffers, effective_cache_size, work_mem e le specifiche del tuo sistema? – eevar

risposta

1

Questa è una parte interessante della documentazione ufficiale ANALYZE.

Per le tabelle di grandi dimensioni, ANALYZE prende un campione casuale del contenuto della tabella, anziché esaminare ogni riga. [...] L'estensione dell'analisi può essere controllata regolando la variabile di configurazione default_statistics_target o su base colonna per colonna impostando l'obiettivo di statistiche per colonna con ALTER TABLE ... ALTER COLUMN ... SET STATISTICHE.

Apparentemente è un modo comune per migliorare il piano di query non valido. Analizzare sarà un po 'più lento, ma il piano di query può essere migliore.

ALTER TABLE

0

In prima interrogazione, ottimizzatore prende stretegies che ignorano sorta tramite scansione chiave primaria. Il problema è che i risultati soddisfano le condizioni su document.fk_id sono state fondate troppo raramente. Quindi index scan e nl join dovrebbero andare molto lontano per riempire il bucket dei risultati.

Problemi correlati