2012-03-29 3 views
1

Diese Abfrage dauert ca. 4 Sekunden abgeschlossen:Warum ist diese DISTINCT/INNER JOIN/ORDER BY Postgresql-Abfrage so langsam?

SELECT DISTINCT "resources_resource"."id", 
        "resources_resource"."heading", 
        "resources_resource"."name", 
        "resources_resource"."old_name", 
        "resources_resource"."clean_name", 
        "resources_resource"."sort_name", 
        "resources_resource"."see_also_id", 
        "resources_resource"."referenced_passages", 
        "resources_resource"."resource_type", 
        "resources_resource"."ord", 
        "resources_resource"."content", 
        "resources_resource"."thumb", 
        "resources_resource"."resource_origin" 
    FROM "resources_resource" 
    INNER JOIN "resources_passageresource" ON ("resources_resource"."id" = "resources_passageresource"."resource_id") 
    WHERE "resources_passageresource"."start_ref" >= 66001001 
    ORDER BY "resources_resource"."ord" ASC, "resources_resource"."sort_name" ASC LIMIT 5 

Auf vielfachen Wunsch EXPLAIN ANALYZE:

Limit (cost=1125.50..1125.68 rows=5 width=803) (actual time=4434.076..4434.557 rows=5 loops=1) 
    -> Unique (cost=1125.50..1136.91 rows=326 width=803) (actual time=4434.076..4434.557 rows=5 loops=1) 
     -> Sort (cost=1125.50..1126.32 rows=326 width=803) (actual time=4434.075..4434.075 rows=6 loops=1) 
       Sort Key: resources_resource.ord, resources_resource.sort_name, resources_resource.id, resources_resource.heading, resources_resource.name, resources_resource.old_name, resources_resource.clean_name, resources_resource.see_also_id, resources_resource.referenced_passages, resources_resource.resource_type, resources_resource.content, resources_resource.thumb, resources_resource.resource_origin 
       Sort Method: quicksort Memory: 424kB 
       -> Hash Join (cost=697.00..1111.89 rows=326 width=803) (actual time=3.453..41.429 rows=424 loops=1) 
        Hash Cond: (resources_passageresource.resource_id = resources_resource.id) 
        -> Bitmap Heap Scan on resources_passageresource (cost=10.78..190.19 rows=326 width=4) (actual time=0.107..0.401 rows=424 loops=1) 
          Recheck Cond: (start_ref >= 66001001) 
          -> Bitmap Index Scan on resources_passageresource_start_ref (cost=0.00..10.70 rows=326 width=0) (actual time=0.086..0.086 rows=424 loops=1) 
           Index Cond: (start_ref >= 66001001) 
        -> Hash (cost=431.32..431.32 rows=2232 width=803) (actual time=3.228..3.228 rows=2232 loops=1) 
          Buckets: 1024 Batches: 2 Memory Usage: 947kB 
          -> Seq Scan on resources_resource (cost=0.00..431.32 rows=2232 width=803) (actual time=0.002..1.621 rows=2232 loops=1) 
Total runtime: 4435.460 ms 

Dies ist ORM-generierte SQL. Ich kann in SQL arbeiten, aber ich bin definitiv nicht kompetent, und die EXPLAIN-Ausgabe hier ist für mich mystifizierend. Was ist mit dieser Abfrage zieht mich runter?

UPDATE: @ Ybakos identifiziert, dass die ORDER_BY Probleme verursacht. Das Entfernen der ORDER_BY-Klausel insgesamt hilft ein wenig, aber die Abfrage dauert immer noch 800ms. Hier ist die EXPLAIN ANALYZE, sans ORDER_BY:

HashAggregate (cost=1122.49..1125.75 rows=326 width=803) (actual time=787.519..787.559 rows=104 loops=1) 
    -> Hash Join (cost=697.00..1111.89 rows=326 width=803) (actual time=3.381..7.312 rows=424 loops=1) 
     Hash Cond: (resources_passageresource.resource_id = resources_resource.id) 
     -> Bitmap Heap Scan on resources_passageresource (cost=10.78..190.19 rows=326 width=4) (actual time=0.095..0.686 rows=424 loops=1) 
       Recheck Cond: (start_ref >= 66001001) 
       -> Bitmap Index Scan on resources_passageresource_start_ref (cost=0.00..10.70 rows=326 width=0) (actual time=0.079..0.079 rows=424 loops=1) 
        Index Cond: (start_ref >= 66001001) 
     -> Hash (cost=431.32..431.32 rows=2232 width=803) (actual time=3.173..3.173 rows=2232 loops=1) 
       Buckets: 1024 Batches: 2 Memory Usage: 947kB 
       -> Seq Scan on resources_resource (cost=0.00..431.32 rows=2232 width=803) (actual time=0.002..1.568 rows=2232 loops=1) 
Total runtime: 787.678 ms 
+0

Sie haben eine 'Index' auf diesen Feldern? 'resources_resource.id' und' resources_passageresource.resource_id' –

+0

johntotetwoo hat eine gute Frage. Ihre WHERE-Klausel trifft den Index auf resources_passageresource.start_ref ... was ist mit diesen Kostenwerten? – ybakos

+1

Was ist die Ausgabe von ANALYZE? – ybakos

Antwort

2

Es scheint mir, DISTINCT verwendet werden, um die durch den Join erzeugten Duplikate zu entfernen. Meine Frage ist also, warum die Duplikate überhaupt produziert werden? Ich bin mir nicht ganz sicher, was diese Abfrage ORM-generierte Abfrage bedeutet, aber wenn das Überschreiben eine Option ist, könnten Sie es sicherlich so umschreiben, dass Duplikate nicht angezeigt werden. Zum Beispiel mit IN:

SELECT "resources_resource"."id", 
     "resources_resource"."heading", 
     "resources_resource"."name", 
     "resources_resource"."old_name", 
     "resources_resource"."clean_name", 
     "resources_resource"."sort_name", 
     "resources_resource"."see_also_id", 
     "resources_resource"."referenced_passages", 
     "resources_resource"."resource_type", 
     "resources_resource"."ord", 
     "resources_resource"."content", 
     "resources_resource"."thumb", 
     "resources_resource"."resource_origin" 
    FROM "resources_resource" 
    WHERE "resources_resource"."id" IN (
     SELECT "resources_passageresource"."resource_id" 
      FROM "resources_passageresource" 
      WHERE "resources_passageresource"."start_ref" >= 66001001 
     ) 
    ORDER BY "resources_resource"."ord" ASC, "resources_resource"."sort_name" ASC LIMIT 5 

oder mit EXISTS:

SELECT "resources_resource"."id", 
     "resources_resource"."heading", 
     "resources_resource"."name", 
     "resources_resource"."old_name", 
     "resources_resource"."clean_name", 
     "resources_resource"."sort_name", 
     "resources_resource"."see_also_id", 
     "resources_resource"."referenced_passages", 
     "resources_resource"."resource_type", 
     "resources_resource"."ord", 
     "resources_resource"."content", 
     "resources_resource"."thumb", 
     "resources_resource"."resource_origin" 
    FROM "resources_resource" 
    WHERE EXISTS (
     SELECT * 
      FROM "resources_passageresource" 
      WHERE "resources_passageresource"."resource_id" = "resources_resource"."id" 
      AND "resources_passageresource"."start_ref" >= 66001001 
     ) 
    ORDER BY "resources_resource"."ord" ASC, "resources_resource"."sort_name" ASC LIMIT 5 

Und natürlich, wenn es um die Abfrage neu zu schreiben, völlig akzeptabel ist, würde ich auch die langen Tabellennamen vor Spalte entfernen Namen. Betrachten Sie die folgende, zum Beispiel (die IN Abfrage neu geschrieben):

SELECT "id", 
     "heading", 
     "name", 
     "old_name", 
     "clean_name", 
     "sort_name", 
     "see_also_id", 
     "referenced_passages", 
     "resource_type", 
     "ord", 
     "content", 
     "thumb", 
     "resource_origin" 
    FROM "resources_resource" 
    WHERE "resources_resource"."id" IN (
     SELECT "resource_id" 
      FROM "resources_passageresource" 
      WHERE "start_ref" >= 66001001 
     ) 
    ORDER BY "ord" ASC, "sort_name" ASC LIMIT 5 
+0

+1 aber ist es nicht, was der Planer sowieso tun sollte? –

+0

Entschuldigung, sollte was tun? Ersetzen Sie den Join intern durch "IN", wenn die Klausel "DISTINCT" vorhanden ist und ziehen Sie Zeilen nur aus einer der beiden Tabellen und (wahrscheinlich) einschließlich der PK-Spalte? –

+0

Ja. Oder etwas ähnliches. –

1

Es ist die Kombination von ORDER BY mit LIMIT.

Wenn Sie keinen Index auf (ord, sort_name) haben, wette ich, dass dies die Ursache für die langsame Performance ist. Oder vielleicht ist ein Index auf (start_ref, ord, sort_name) für diese bestimmte Abfrage notwendig. Schließlich, aufgrund dieser Verknüpfung, haben Sie vielleicht die linke/erste Tabelle, auf die Ihre ORDER BY-Kriterien zutreffen.

+0

Der Planer schätzt 326 Zeilen, um in der Reihenfolge zu sortieren ... sollte Millisekunden nicht Sekunden sein. OP muss EXPLAIN ANALYSE für ein besseres Bild veröffentlichen. – dbenhur

+0

Es muss auch nach dem Unique-Pass sortiert werden, um DISTINCT zu implementieren. geordnete Index-Scans sind nicht immer besser als seq scans + sort, tatsächlich können sie aufgrund von Suchzeiten oft viel schlechter sein. – dbenhur

+0

Ich habe einen Index auf 'ord', aber nicht auf' sort_name', und ich glaube nicht, dass ich wirklich nach 'sort_name' sortieren muss. Ich werde versuchen, es zu entfernen. –

1

Das scheint wie eine lange Zeit in der JOIN. Die Standardspeichereinstellungen in postgresql.conf sind für einen modernen Computer zu niedrig. Hast du dich daran erinnert, sie aufzustoßen?