Frage

Die Situation

Ich habe Probleme mit meinem Abfrageausführungsplan für eine mittelgroße Abfrage über eine große Datenmenge in Oracle 11.2.0.2.0.Um die Sache zu beschleunigen, habe ich einen Bereichsfilter eingeführt, der ungefähr so ​​funktioniert:

PROCEDURE DO_STUFF(
    org_from VARCHAR2 := NULL,
    org_to   VARCHAR2 := NULL)

  -- [...]
  JOIN organisations org
    ON (cust.org_id = org.id
   AND ((org_from IS NULL) OR (org_from <= org.no))
   AND ((org_to   IS NULL) OR (org_to   >= org.no)))
  -- [...]

Wie Sie sehen, möchte ich das einschränken JOIN von organisations unter Verwendung eines optionalen Bereichs von Organisationsnummern.Client-Code kann anrufen DO_STUFF mit (soll schnell sein) oder ohne (sehr langsam) der Einschränkung.

Die Schwierigkeiten

Das Problem ist, dass PL/SQL Bind-Variablen für das oben Genannte erstellt org_from Und org_to Parameter, was ich in den meisten Fällen erwarten würde:

  -- [...]
  JOIN organisations org
    ON (cust.org_id = org.id
   AND ((:B1 IS NULL) OR (:B1 <= org.no))
   AND ((:B2 IS NULL) OR (:B2 >= org.no)))
  -- [...]

Der Workaround

Nur in diesem Fall habe ich gemessen, dass der Abfrageausführungsplan viel besser ist, wenn ich die Werte nur einfüge, d. h.wenn die von Oracle ausgeführte Abfrage tatsächlich so etwas ist

  -- [...]
  JOIN organisations org
    ON (cust.org_id = org.id
   AND ((10 IS NULL) OR (10 <= org.no))
   AND ((20 IS NULL) OR (20 >= org.no)))
  -- [...]

Mit „viel“ meine ich 5-10x schneller.Beachten Sie, dass die Abfrage sehr selten ausgeführt wird, d. h.einmal pro Monat.Daher muss ich den Ausführungsplan nicht zwischenspeichern.

Meine Fragen

  • Wie kann ich Werte in PL/SQL einbetten?ich weiss Bescheid SOFORT AUSFÜHREN, aber ich würde es vorziehen, wenn PL/SQL meine Abfrage kompiliert und keine Zeichenfolgenverkettung durchführt.

  • Habe ich nur etwas gemessen, das zufällig passiert ist, oder kann ich davon ausgehen, dass das Inlining von Variablen tatsächlich besser ist (in diesem Fall)?Der Grund, warum ich frage, ist, dass ich denke, dass Bind-Variablen Oracle dazu zwingen, eine zu entwickeln allgemein Ausführungsplan, während inline-Werte eine Analyse ermöglichen würden sehr spezifische Spalten- und Indexstatistiken.Ich kann mir also vorstellen, dass das kein Zufall ist.

  • Vermisse ich etwas?Vielleicht gibt es eine ganz andere Möglichkeit, eine Verbesserung des Abfrageausführungsplans zu erreichen, als das Inlining von Variablen (beachten Sie, dass ich auch einige Hinweise ausprobiert habe, aber kein Experte auf diesem Gebiet bin)?

War es hilfreich?

Lösung

In einem Ihrer Kommentare sagten Sie:

„Außerdem habe ich verschiedene Bindungswerte überprüft.Mit Bind-Variablen bekomme ich einige FULL TABLE SCANS, während mit hartcodierten sieht der Plan viel besser aus."

Es gibt zwei Wege.Wenn Sie NULL für die Parameter übergeben, wählen Sie alle Datensätze aus.Unter diesen Umständen ist ein vollständiger Tabellenscan die effizienteste Methode zum Abrufen von Daten.Wenn Sie Werte übergeben, sind indizierte Lesevorgänge möglicherweise effizienter, da Sie nur eine kleine Teilmenge der Informationen auswählen.

Wenn Sie die Abfrage mithilfe von Bindevariablen formulieren, muss der Optimierer eine Entscheidung treffen:Sollte davon ausgegangen werden, dass Sie die meiste Zeit Werte oder Nullen übergeben?Schwierig.Betrachten Sie es also anders:Ist es ineffizienter, einen vollständigen Tabellenscan durchzuführen, wenn Sie nur eine Teilmenge von Datensätzen auswählen müssen, oder indizierte Lesevorgänge durchzuführen, wenn Sie alle Datensätze auswählen müssen?

Es scheint, als hätte sich der Optimierer für vollständige Tabellenscans entschieden, da dies die am wenigsten ineffiziente Operation ist, um alle Eventualitäten abzudecken.

Wenn Sie hingegen die Werte fest codieren, erkennt der Optimierer dies sofort 10 IS NULL wird als FALSE ausgewertet und kann daher die Vorteile der Verwendung indizierter Lesevorgänge zum Auffinden der gewünschten Teilmengendatensätze abwägen.


Was also tun?Da Sie sagen, dass diese Abfrage nur einmal im Monat ausgeführt wird, wäre meiner Meinung nach nur eine kleine Änderung der Geschäftsprozesse erforderlich, um separate Abfragen zu haben:eine für alle Organisationen und eine für eine Untergruppe von Organisationen.


"Übrigens, das Entfernen der :R1 IS NULL-Klausel ändert den Ausführungsplan nicht viel, was mich mit dem anderen Seite der ODER-Bedingung, :R1 <= org.no, bei denen NULL keinen Sinn ergeben würde Wie auch immer, da org.no NICHT NULL ist"

Okay, die Sache ist also, dass Sie ein Paar Bind-Variablen haben, die angeben eine Reichweite.Abhängig von der Werteverteilung können unterschiedliche Bereiche für unterschiedliche Ausführungspläne geeignet sein.Das heißt, dieser Bereich würde (wahrscheinlich) für einen indizierten Bereichsscan geeignet sein ...

WHERE org.id BETWEEN 10 AND 11

...während dies wahrscheinlich eher für einen vollständigen Tabellenscan geeignet ist ...

WHERE org.id BETWEEN 10 AND 1199999

Hier kommt Bind Variable Peeking ins Spiel.

(abhängig natürlich von der Werteverteilung).

Andere Tipps

Da die Abfragepläne tatsächlich durchweg unterschiedlich sind, bedeutet dies, dass die Kardinalitätsschätzungen des Optimierers aus irgendeinem Grund falsch sind.Können Sie anhand der Abfragepläne bestätigen, dass der Optimierer erwartet, dass die Bedingungen nicht ausreichend selektiv sind, wenn Bind-Variablen verwendet werden?Da Sie 11.2 verwenden, sollte Oracle verwenden Adaptive Cursor-Freigabe Daher sollte es sich nicht um ein Problem mit der Suche nach Bind-Variablen handeln (vorausgesetzt, Sie rufen die Version mit Bind-Variablen viele Male mit unterschiedlichen Variablen auf NO Werte in Ihren Tests.

Sind die Kardinalitätsschätzungen des guten Plans tatsächlich korrekt?Ich weiß, dass Sie gesagt haben, dass die Statistiken über die NO Die Werte in der Spalte sind korrekt, aber ich wäre misstrauisch gegenüber einem Streuhistogramm, das beispielsweise durch Ihren regulären Statistikerfassungsprozess möglicherweise nicht aktualisiert wird.

Sie können jederzeit einen Hinweis in der Abfrage verwenden, um die Verwendung eines bestimmten Index zu erzwingen (obwohl Sie a Gespeicherte Gliederung oder Optimierungsplanstabilität wäre aus Sicht der langfristigen Instandhaltung vorzuziehen).Jede dieser Optionen wäre dem Rückgriff auf dynamisches SQL vorzuziehen.

Ein zusätzlicher Test wäre jedoch, die SQL 99-Join-Syntax durch die alte Oracle-Syntax zu ersetzen, d. h.

SELECT <<something>>
  FROM <<some other table>> cust,
       organization org
 WHERE cust.org_id = org.id
   AND (    ((org_from IS NULL) OR (org_from <= org.no)) 
        AND ((org_to   IS NULL) OR (org_to   >= org.no)))

Das sollte natürlich nichts ändern, aber es gab Parser-Probleme mit der SQL 99-Syntax, also sollten Sie das überprüfen.

Es riecht nach Bind Spähen, aber ich verwende nur Oracle 10, daher kann ich nicht behaupten, dass das gleiche Problem in 11 besteht.

Dies scheint ein Bedarf an adaptiver Cursorfreigabe in Kombination mit SQLPlan-Stabilität zu sein.Ich denke, was passiert, ist, dass capture_sql_plan_baselines parameter is true.Und das Gleiche gilt für use_sql_plan_baselines.Wenn dies zutrifft, geschieht Folgendes:

  1. Wenn eine Abfrage zum ersten Mal gestartet wird, wird sie analysiert und erhält einen neuen Plan.
  2. Beim zweiten Mal wird dieser Plan in sql_plan_baselines als akzeptierter Plan gespeichert.
  3. Alle folgenden Ausführungen dieser Abfrage verwenden diesen Plan, unabhängig von den Bindungsvariablen.

Wenn die adaptive Cursorfreigabe bereits aktiv ist, generiert der Optimierer einen neuen/besseren Plan, speichert ihn in sql_plan_baselines, kann ihn jedoch nicht verwenden, bis jemand diesen neueren Plan als akzeptablen Alternativplan akzeptiert.Überprüfen dba_sql_plan_baselines und prüfen Sie, ob Ihre Abfrage Einträge mit enthält accepted = 'NO' and verified = nullSie können verwenden dbms_spm.evolve den neuen Plan weiterzuentwickeln und ihn automatisch akzeptieren zu lassen, wenn die Leistung des Plans mindestens 1,5-mal besser ist als ohne den neuen Plan.

Ich hoffe das hilft.

Ich habe dies als Kommentar hinzugefügt, werde es aber auch hier anbieten.Ich hoffe, das ist nicht zu einfach, und wenn ich mir die detaillierten Antworten ansehe, verstehe ich vielleicht das genaue Problem falsch, aber trotzdem ...

Anscheinend hat Ihre Organisationstabelle die Spalte Nr. (org.no), die als Zahl definiert ist.In Ihrem hartcodierten Beispiel verwenden Sie Zahlen um die Vergleiche durchzuführen.

JOIN organisations org
    ON (cust.org_id = org.id
   AND ((10 IS NULL) OR (10 <= org.no))
   AND ((20 IS NULL) OR (20 >= org.no)))

In Ihrem Verfahren passieren Sie varchar2:

PROCEDURE DO_STUFF(
    org_from VARCHAR2 := NULL,
    org_to   VARCHAR2 := NULL)

Um also Varchar2 mit Zahl zu vergleichen, Oracle muss die Konvertierungen durchführen, Dies kann daher zu vollständigen Scans führen.

Lösung: Ändern Sie proc, um Zahlen zu übergeben

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top