AQO - Adaptive Abfrageoptimierung in PostgreSQL

Moderne DBMS verwenden bei der DurchfĂŒhrung von Abfragen das Kostenoptimierungsmodell - basierend auf den in den Konfigurationsdateien gespeicherten Koeffizienten und den gesammelten Statistiken berechnen sie den „Preis“ des Empfangs und das Volumen der resultierenden ZeilensĂ€tze. Wenn Abfragen erneut ausgefĂŒhrt werden, werden Kosten und SelektivitĂ€t neu berechnet. Sie können eine Abfrage ausfĂŒhren und die tatsĂ€chlichen Werte dieser Parameter anzeigen. WĂ€hrend der (Standard-) Neuplanung verwendet der DBMS-Optimierer diese Informationen jedoch in keiner Weise.



Was aber, wenn der Optimierer die tatsĂ€chlichen Werte fĂŒr Kosten, SelektivitĂ€t und andere erforderliche Parameter fĂŒr die AusfĂŒhrung von Abfragen beibehĂ€lt und sich bei Wiederholung nicht nur auf die standardmĂ€ĂŸig gesammelten Statistiken konzentriert, sondern auch auf diejenigen, die nach der vorherigen AusfĂŒhrung gespeichert wurden?



Dies wird als adaptive Abfrageoptimierung bezeichnet und ist vielversprechend. Einige DBMS verwenden solche Technologien bereits.



Unternehmen Postgres Professional arbeitet seit mehreren Jahren an der AQO-Erweiterung fĂŒr PostgreSQL, die (in irgendeiner Form) adaptive Optimierung implementiert. Die Arbeiten sind noch im Gange, aber es gibt bereits etwas zu testen.



Schauen wir uns zunÀchst den Themenbereich der Abfrageoptimierung genauer an.



Warum ein Planer einen suboptimalen Plan auswÀhlen kann



SQL-Abfragen können auf verschiedene Arten ausgefĂŒhrt werden. Wenn beispielsweise zwei Tabellen verknĂŒpft sind, kann dies auf verschiedene Arten erfolgen - mithilfe verschachtelter Schleifen, ZusammenfĂŒhren und Hashing. Je mehr Tabellen an der Abfrage beteiligt sind, desto mehr Variationen ihrer VerknĂŒpfungen. Die Aufgabe des Planers besteht darin, aus vielen Variationen den AusfĂŒhrungsplan der Abfrage mit den niedrigsten Kosten auszuwĂ€hlen.



Wie bereits erwÀhnt, verwenden die Scheduler vieler DBMS in ihrer Arbeit statistische Informationen, die entweder automatisch oder manuell erfasst werden. Der Planer berechnet anhand dieser Statistiken die geschÀtzten Kosten.



Im Allgemeinen funktionieren die Planer moderner DBMS in den meisten Situationen gut. In einigen FÀllen kann der gewÀhlte Plan jedoch weit vom Optimum entfernt sein.



Beispielsweise,Das Fehlen aktueller statistischer Informationen fĂŒhrt dazu, dass sich der Planer bei seiner Arbeit an (höchstwahrscheinlich) falschen Daten zur Anzahl der Zeilen in den zu verbindenden Tabellen orientiert. ÜbermĂ€ĂŸiges Understatement (oder Übertreiben) der KardinalitĂ€t fĂŒhrt zur Auswahl nicht optimaler Methoden fĂŒr den Zugriff auf Daten in Tabellen.



Ein weiterer wichtiger Grund kann das Fehlen notwendiger Indizes sein . In Ermangelung von Indizes hat der Scheduler eine begrenzte Auswahl an Datenzugriffsmethoden.



Verwendung abhĂ€ngiger (korrelierter) Bedingungenkann sich auch negativ auf den Betrieb des DBMS auswirken. Der Scheduler geht (standardmĂ€ĂŸig) davon aus, dass alle Bedingungen in einer Abfrage unabhĂ€ngig voneinander sind, dh der Wert einer Bedingung wirkt sich in keiner Weise auf die andere aus. Dies ist nicht immer der Fall. Wenn abhĂ€ngige Bedingungen verwendet werden (z. B. Postleitzahl und Stadt), berechnet der Planer auch die falschen Kosten und die falsche KardinalitĂ€t der Verbindungen.



Die Verwendung von Funktionen in Bedingungen kann sich auch auf den Scheduler auswirken . Eine Funktion fĂŒr den Planer ist eine „Black Box“. Er weiß nicht, wie viele Zeilen die Funktion zurĂŒckgeben wird, was auch zu fehlerhaften Plankosten fĂŒhren kann.



Möglichkeiten, die Arbeit des Schedulers zu beeinflussen



Aktuelle Statistiken sind eine unabdingbare Voraussetzung fĂŒr die angemessene Arbeit des Schedulers. Stellen Sie zunĂ€chst sicher, dass das System so konfiguriert ist, dass regelmĂ€ĂŸig statistische Informationen erfasst werden.


Es gibt verschiedene Möglichkeiten, um die oben beschriebenen Situationen zu beheben und dem Planer bei der Auswahl der optimalsten AbfrageausfĂŒhrungsplĂ€ne zu helfen.



Ohne Indizes hat der Scheduler nur eine Möglichkeit, Daten zu erhalten - das sequentielle Scannen von Tabellen (und dies ist nicht immer schlecht und teuer). In einigen FĂ€llen kann das Erstellen der erforderlichen Indizes den Datenzugriff beschleunigen. Sie mĂŒssen nicht die gesamte Tabelle scannen. Die Verwendung von Indizes (Suche nach dem Notwendigen, Erstellung, Pflege) ist jedoch nicht kostenlos. Idealerweise sollten sie genau dort eingesetzt werden, wo sie benötigt werden. Und wo nicht benötigt - nicht verwenden.



Wenn korreliert mit Join - Bedingungen in Abfragen können Sie erzeugen eine erweiterte Statistik- den Optimierer explizit auffordern, dass die Bedingungen miteinander zusammenhĂ€ngen. Dazu muss der DBA (oder Entwickler) seine Daten gut kennen und abhĂ€ngige Bedingungen in Abfragen verfolgen, da die Anzahl der Kombinationen abhĂ€ngiger Spalten im Voraus schwer vorherzusagen ist. Erweiterte Statistiken mĂŒssen fĂŒr jede dieser Optionen manuell erstellt werden.



Beim Erstellen einer Funktion können Sie die ungefĂ€hren AusfĂŒhrungskosten und / oder eine SchĂ€tzung der Anzahl der von der Funktion zurĂŒckgegebenen Zeilen angeben . In Version 12 wurde es möglich, Hilfsfunktionen zu verwenden, um die SchĂ€tzungen des Planers in AbhĂ€ngigkeit von den Argumenten zu verbessern. Dies ist auch ein manueller Weg, der nicht immer ein optimales Ergebnis liefert.



Wenn alles andere fehlschlÀgt, können Sie die Abfrage manuell neu schreibenVerwenden Sie beispielsweise materialisierte Ansichten, Common Table Expressions (CTE). Oder um die Anforderungen des Themenbereichs zu klÀren und möglicherweise die Logik der Anforderung radikal umzuschreiben.



Und es ist eine andere Art von „Hinweise“ an den Scheduler - adaptive Abfrageoptimierung ( a daptive q uery o ptimization). Die Idee dieser Methode ist, dass nach der AusfĂŒhrung der Abfrage echte statistische Informationen gespeichert werden und der Optimierer sich darauf verlassen kann, wenn die angegebene (oder Ă€hnliche) Abfrage wiederholt wird.



Das DBMS Postgres Pro Enterprise ist eine Erweiterung fĂŒr die adaptive Abfrageoptimierung mit dem Namen AQO... Diese Erweiterung ist auf github veröffentlicht: github.com/postgrespro/aqo , Sie können sie mit Vanilla PostgreSQL ausprobieren, mehr dazu weiter unten.



Das Funktionsprinzip des Moduls



Das AQO-Modul verwendet in seiner Arbeit maschinelles Lernen. Weitere Informationen zum Funktionsprinzip finden Sie in dem Artikel von Oleg Ivanov ĂŒber maschinelles Lernen zur Steigerung der PostgreSQL-Leistung und ausfĂŒhrlicher in der PrĂ€sentation Adaptive Query Optimization (Bericht auf YouTube ).



Das Wesentliche dieser Methode wird im Folgenden kurz beschrieben:



Um die Kosten zu bewerten, benötigt der Planer eine Bewertung der KardinalitĂ€t, und dafĂŒr ist wiederum eine Bewertung der SelektivitĂ€t der Bedingungen erforderlich.



FĂŒr einfache Bedingungen (wie „Attribut = Konstante“ oder „Attribut> Konstante“) verfĂŒgt der Scheduler ĂŒber ein Modell, anhand dessen er die SelektivitĂ€t schĂ€tzt. Dazu verwendet er statistische Informationen: die Anzahl der eindeutigen Attributwerte, Histogramme usw.

FĂŒr Bedingungen, die aus einfachen Elementen bestehen, die logische VerknĂŒpfungen verwenden, verwendet der Planer einfach zu berechnende Formeln:



  • sel (nicht A) = 1 - sel (A)
  • sel (nicht A) = 1 - sel (A)
  • sel (A und B) = sel (A) * sel (B)
  • sel (A oder B) = sel (nicht (nicht A und nicht B)) = 1 - (1 - sel (A)) * (1 - sel (B))


Diese Formeln setzen die UnabhÀngigkeit (Unkorrelation) der Bedingungen A und B voraus, weshalb wir falsche SchÀtzungen erhalten, wenn diese Annahme verletzt wird.

AQO verkompliziert die Formel: fĂŒhrt fĂŒr jede einfache Bedingung einen eigenen Koeffizienten ein. Unter Verwendung von maschinellem Lernen (unter Verwendung der Regression des nĂ€chsten Nachbarn) passt AQO diese Koeffizienten so an, dass die durch die Formel berechnete SelektivitĂ€t am besten mit der tatsĂ€chlichen SelektivitĂ€t ĂŒbereinstimmt, die AQO zuvor beobachtet hat.



Dazu speichert das Modul:



  • , ;
  • .


AQO unterscheidet in seiner Arbeit zwischen Bedingungen bis hin zu Konstanten. Dies ermöglicht es, die KomplexitĂ€t des zu lösenden Problems zu verringern, und außerdem gehen in den meisten FĂ€llen immer noch keine Informationen verloren: AQO "sieht" den Wert der Konstante nicht, sondern "sieht" die SelektivitĂ€t der Bedingung.

Die Situation, in der der Verlust immer noch auftritt, sind die Bedingungen, die unabhĂ€ngig von den spezifischen Werten als Konstante bewertet werden. Beispielsweise kann der Planer fĂŒr einige Bedingungen keine vernĂŒnftigen SchĂ€tzungen vornehmen und wĂ€hlt eine Standardkonstante (zum Beispiel wird die SelektivitĂ€t der Bedingung "Ausdruck1 = Ausdruck2" immer mit 0,005 und "Ausdruck1> Ausdruck2" mit 1/3 bewertet).



Somit kann AQO die Bewertung der SelektivitĂ€t komplexer Bedingungen verbessern (und infolgedessen die Bewertung der Kosten, was zur Wahl eines angemesseneren Umsetzungsplans fĂŒhren kann).



Modulinstallation



Um die FunktionalitĂ€t des Moduls unter Vanilla PostgreSQL zu testen, mĂŒssen Sie einen speziellen Patch verwenden und das System dann aus dem Quellcode erstellen. Weitere Informationen finden Sie in der README-Datei auf github.



Wenn Postgres Pro Enterprise verwendet wird, wird die Installation des AQO-Moduls im Standardmodus fortgesetzt:



shared_preload_libraries = 'aqo'



Danach können Sie eine Erweiterung in der erforderlichen Datenbank erstellen.



Datenbankvorbereitung



Schauen wir uns ein konkretes Beispiel an, wie das AQO-Modul in einer Demo-Datenbank funktioniert . Wir werden eine große Datenbank mit Informationen zu FlĂŒgen fĂŒr das Jahr von September 2016 bis September 2017 verwenden.



Erstellen wir zunÀchst eine Erweiterung:



CREATE EXTENSION aqo;




Deaktivieren Sie als NÀchstes die parallele Abfrageverarbeitung, damit die Anzeige paralleler PlÀne nicht von der Hauptaufgabe ablenkt:



max_parallel_workers_per_gather = 0;



Damit der PostgreSQL-Scheduler mehr Optionen zum VerknĂŒpfen von Tabellen bietet, erstellen Sie zwei Indizes:



CREATE INDEX ON flights (scheduled_departure );
CREATE INDEX ON ticket_flights (fare_conditions );


Bei der Analyse der Ergebnisse konzentrieren wir uns auf den Wert von BUFFERS als die Anzahl der Seiten, die gelesen werden mĂŒssen, um die Aufgabe auszufĂŒhren. Schauen wir uns auch die AusfĂŒhrungszeit an (aber die Zeit auf einem geladenen System und einem Heim-Laptop kann sehr unterschiedlich sein).



Erhöhen wir den Puffercache und work_mem, damit alle Arbeiten im RAM ausgefĂŒhrt werden:



shared_buffers = '256MB';

work_mem = '256MB';




Verwenden des AQO-Moduls



Wir werden eine Anfrage formulieren: Es ist erforderlich, Passagiere zu empfangen, die ab einem bestimmten Datum eine Business Class geflogen sind und spÀtestens mit einer Stunde verspÀtet angekommen sind.

Lassen Sie uns die Anforderung ohne Verwendung von AQO ausfĂŒhren (im Folgenden wurde ein Teil der Informationen, die das VerstĂ€ndnis der Funktionsweise des Moduls nicht beeintrĂ€chtigen, aus den PlĂ€nen entfernt):



EXPLAIN (ANALYZE, BUFFERS, TIMING OFF) SELECT t.ticket_no
  FROM flights f
   	JOIN ticket_flights tf ON f.flight_id = tf.flight_id
   	JOIN tickets t ON tf.ticket_no = t.ticket_no
 WHERE f.scheduled_departure > '2017-08-01'::timestamptz
   AND f.actual_arrival < f.scheduled_arrival + interval '1 hour'
   AND tf.fare_conditions = 'Business';




Und sehen wir uns den resultierenden Plan an: In diesem Fall hat der Planer den optimalen Plan betrachtet, bei dem wir zuerst mithilfe eines Bitmap-Scans ( ) eine Reihe von Zeilen aus der Flugtabelle erhalten , die wir (einen Knoten ) mit einer Reihe von Zeilen aus der Tabelle ticket_flights haben, die unter Verwendung des Index erhalten wurden scan ( ). Das Ergebnis wird als externer Satz von Zeilen fĂŒr die endgĂŒltige Verbindung durch eine verschachtelte Schleife (Knoten ) verwendet. Der innere Satz fĂŒr diesen Join wird durch einen exklusiven Index-Scan der Tickets ( ) -Tabelle erhalten . Die "sperrigste" Operation besteht darin, das interne Rowset fĂŒr die verschachtelte Schleife abzurufen - 106 205 Puffer werden darin eingelesen.



Nested Loop (rows=33210) (actual rows=31677)

  Buffers: shared hit=116830 read=1

  ->  Hash Join (rows=33210) (actual rows=31677)

        Hash Cond: (tf.flight_id = f.flight_id)

        ->  Index Scan ... on ticket_flights tf  

              Index Cond: fare_conditions = 'Business'

        ->  Hash

              ->  Bitmap Heap Scan on flights f (rows=8331) (actual rows=7673)

                    Recheck Cond: scheduled_departure > '2017-08-01'

                    Filter: actual_arrival < scheduled_arrival + '01:00:00'::interval

                    ->  Bitmap Index Scan on ... [flights]

                          Index Cond: scheduled_departure > '2017-08-01'

                          Buffers: shared hit=44 read=1

  ->   Index Only Scan  ... on tickets t (rows=1 width=14) (actual rows=1 loops=31677)

        Index Cond: (ticket_no = tf.ticket_no)

        Buffers: shared hit=106205

 Planning Time: 9.326 ms

 Execution Time: 675.836 ms




Bitmap Heap Scan on flightsHash JoinIndex Scan ... on ticket_flightsNested LoopIndex Only Scan ... on tickets





Dieser Plan kann als relativ gut bezeichnet werden, da eine verschachtelte Schleife eine relativ kleine Anzahl von Zeilen in der Ă€ußeren Menge verbindet.



Jetzt fĂŒhren wir ein Experiment durch und sehen, wie sich der vorgeschlagene Plan abhĂ€ngig von der Änderung der Daten in der Anfrage Ă€ndern wird (oder nicht). Daten werden so ausgewĂ€hlt, dass der Zeilenbereich der Flugtabelle, der die Bedingung erfĂŒllt, konsistent vergrĂ¶ĂŸert wird, was zu einem Planungsfehler bei der Bewertung der KardinalitĂ€t des Zugriffs auf diese Tabelle fĂŒhrt. Im obigen Plan können Sie sehen, dass der Optimierer beim ersten Datum fast nicht in der KardinalitĂ€t verwechselt wird ( ). Ersetzen Sie die folgenden Daten in der Anfrage:Bitmap Heap Scan on flights f (rows=8331) (actual rows=7673)







  • 2017-04-01
  • 2017-01-01
  • 2016-08-01


Und sehen wir uns das Ergebnis an:



AbfrageplÀne ohne AQO
2017-04-01



Nested Loop (rows=31677) (actual rows=292392)

  Buffers: shared hit=991756

  ->  Hash Join (rows=31677) (actual rows=292392)

        Hash Cond: (tf.flight_id = f.flight_id)

        ->  Index Scan â€Š on ticket_flights tf

              Index Cond: fare_conditions = 'Business')

        ->  Hash

              ->  Bitmap Heap Scan on flights f (rows=7673) (actual rows=70553)

                    Recheck Cond: scheduled_departure > '2017-04-01'

                    Filter: actual_arrival < (scheduled_arrival + '01:00:00'::interval)

                    ->  Bitmap Index Scan on ... [flights]

                          Index Cond: scheduled_departure > '2017-04-01'

                          Buffers: shared hit=160

  ->  Index Only Scan ... on tickets t ( rows=1 width=14) (actual rows=1 loops=292392)

        Index Cond: (ticket_no = tf.ticket_no)

        Buffers: shared hit=980995

 Planning Time: 5.980 ms

 Execution Time: 2771.563 ms



, . . , (Bitmap Heap Scan on flights f (rows=7673) (actual rows=70553)), , Nested Loop, , .



() — Flights , ( , ).



2017-01-01



Nested Loop (rows=187710) (actual rows=484569)

  Buffers: shared hit=1640723 read=49

  ->  Hash Join (rows=187738) (actual rows=484569)

        Hash Cond: (tf.flight_id = f.flight_id)

        ->  Index Scan ... on ticket_flights tf

              Index Cond: fare_conditions = 'Business'

        ->  Hash

              ->  Seq Scan on flights f (rows=45352) (actual rows=116985)

                    Filter: scheduled_departure > '2017-01-01'::date 

                              AND actual_arrival < scheduled_arrival + '01:00:00'::interval

  ->  Index Only Scan ... on tickets t (rows=1) (actual rows=1 loops=484569)

        Index Cond: (ticket_no = tf.ticket_no)

        Buffers: shared hit=1630118 read=49

 Planning Time: 6.225 ms

 Execution Time: 4498.741 ms



, . flights, ( ) .

tickets — (1 630 118).



2016-08-01



Hash Join (rows=302200) (actual rows=771441)

   Hash Cond: (t.ticket_no = tf.ticket_no)

   Buffers: shared hit=25499 read=34643

   ->  Seq Scan on tickets t (rows=2949857) (actual rows=2949857)

   ->  Hash

         ->  Hash Join (rows=302236) (actual rows=771441)

               Hash Cond: (tf.flight_id = f.flight_id)

               ->  Index Scan on ticket_flights tf

                     Index Cond: fare_conditions = 'Business'

               ->  Hash

                     ->  Seq Scan on flights f (rows=73005) (actual rows=188563)

                           Filter: scheduled_departure > '2016-08-01'::date) 

                                     AND actual_arrival < scheduled_arrival + '01:00:00'::interval

 Planning Time: 9.990 ms

 Execution Time: 3014.577 ms



((rows=302236) (actual rows=771441)). , , : Hash Join Nested Loop.



Zusammenfassend funktioniert der Planer ohne Verwendung des AQO-Moduls wie folgt:

Datum             Puffer Zeit, ms Ein Kommentar
2017-08-01   116 831       675,836 Es wird eine verschachtelte Schleife und ein Hash-Join verwendet. FlĂŒge und Tickets werden nach Index gescannt
2017-04-01   991 756      2771,563 Der gleiche Plan, aber nicht optimal. Bei der Auswahl des Zugriffs nach Index fĂŒr die Tabellen "FlĂŒge" und "Tickets" wird festgestellt, dass der Planer bei der Berechnung der KardinalitĂ€t sehr falsch ist
2017-01-01 1,640,772      4498.741 Der gleiche suboptimale Plan. Der Planer beschließt jedoch, zu einem sequentiellen Scan der Flugtabelle zu wechseln.
2016-08-01       60142      3014,577 Der Plan hat sich endlich geĂ€ndert - der Optimierer erkennt, dass er viele Zeilen aus den Tabellen auswĂ€hlen muss, und wechselt daher zum sequentiellen Scannen der FlĂŒge und Tickets. Eine ineffektive (in diesem Fall) verschachtelte Schleife wird durch einen Hash-Join ersetzt.
AbfrageplÀne mit AQO
AQO. :



SET aqo.mode = 'learn';



, :



2017-08-01



, , . AQO .



2017-04-01



Hash Join (rows=293891) (actual rows=292392)

  Hash Cond: (t.ticket_no = tf.ticket_no)

  Buffers: shared hit=25658 read=34640

  ->  Seq Scan on tickets t  (rows=2949857) (actual rows=2949857)

  ->  Hash

        ->  Hash Join  (rows=293734) (actual rows=292392)

              Hash Cond: (tf.flight_id = f.flight_id)

              ->  Index Scan ... on ticket_flights tf

                    Index Cond: (fare_conditions)::text = 'Business'::text

              ->  Hash

                    ->  Bitmap Heap Scan on flights f

                          Recheck Cond: scheduled_departure > '2017-04-01'::date

                          Filter: actual_arrival < scheduled_arrival + '01:00:00'::interval

                          ->  Bitmap Index Scan on ... [flights]

                                Index Cond: scheduled_departure > '2017-04-01'::date

                                Buffers: shared hit=160

 Planning Time: 9.652 ms

 Execution Time: 2218.074 ms



“”, AQO — . Tickets . . , AQO.



2017-01-01



Hash Join  (rows=484452) (actual rows=484569)

  Hash Cond: (t.ticket_no = tf.ticket_no)

  Buffers: shared hit=25534 read=34608

  ->  Seq Scan on tickets t (rows=2949857) (actual rows=2949857)

  ->  Hash (rows=484464) (actual rows=484569)

        ->  Hash Join (rows=484464) (actual rows=484569)

              Hash Cond: (tf.flight_id = f.flight_id)

              ->  Index Scan ... on ticket_flights tf

                    Index Cond: fare_conditions::text = 'Business'::text

              ->  Hash

                    ->  Seq Scan on flights f (rows=116971) (actual rows=116985)

                          Filter: scheduled_departure > '2017-01-01'::date

                                    AND actual_arrival < scheduled_arrival + '01:00:00'::interval

 Planning Time: 6.264 ms

 Execution Time: 2746.485 ms



— Flights .



2016-08-01



.



Schauen wir uns das Ergebnis noch einmal an:

Datum             Puffer Zeit, ms Ein Kommentar
2017-08-01   116 831      662,966 Der Plan ist der gleiche wie ohne Verwendung des Moduls
2017-04-01     60298    2218.074 Anhand der Modulhinweise versteht der Optimierer, dass eine große Anzahl von Zeichenfolgen zum Verbinden geplant ist, und verbessert bereits in diesem Schritt den Plan, indem die verschachtelte Schleife durch einen Hash-Join ersetzt wird
2017-01-01     60142    2746.485 Der Plan hat sich etwas verbessert - anstatt auf die Bitmap zur Flugtabelle zuzugreifen, wird deren sequentieller Scan verwendet
2016-08-01     60142    3253,861 Der Plan blieb unverĂ€ndert - der beste Plan in diesem Fall
Wenn AQO aktiviert ist, erkennt der Scheduler schnell, dass er von einer verschachtelten SchleifenverknĂŒpfung und Indexverwendung zu einer HashverknĂŒpfung und einem sequentiellen Scan wechseln muss.



Zusammenfassen



Die Verwendung des AQO-Moduls zur adaptiven Abfrageoptimierung hat sowohl Vor- als auch Nachteile.



Einer der Vorteile der Verwendung eines Moduls besteht darin, dass Sie abhĂ€ngige Bedingungen in Abfragen nicht verfolgen mĂŒssen. In einigen FĂ€llen kann sich die Geschwindigkeit der AbfrageausfĂŒhrung erhöhen. Es gibt verschiedene Modi fĂŒr die Verwendung des Moduls. Mit AQO können Sie beispielsweise nur bestimmte Arten von Abfragen optimieren.



Unter den Nachteilen des Moduls können wir zusĂ€tzliche Gemeinkosten fĂŒr die Schulung und das Speichern von Statistiken in den Modulstrukturen herausgreifen. Die vom Modul gesammelten statistischen Informationen werden nicht an Replikate ĂŒbertragen.



Das AQO-Modul ist keine „Silberkugel“ aller möglichen Probleme des Schedulers. In einigen Situationen kann das Modul beispielsweise erweiterte Statistiken ersetzen (wenn es nicht von Hand erstellt wurde) oder irrelevante Statistiken nicht berĂŒcksichtigen. Das Modul erstellt jedoch nicht die erforderlichen Indizes und schreibt darĂŒber hinaus den Abfragetext nicht neu.



Daher sollten Sie das Modul nicht fĂŒr alle Anforderungen aktivieren. Ideale Optimierungskandidaten fĂŒr AQO sind Abfragen, bei denen der Fehler des Planers bei der Berechnung der KardinalitĂ€t der Knoten zu einem schlechten Plan fĂŒhrt. Aus irgendeinem Grund ist es nicht möglich, die Verfeinerung dieser SchĂ€tzung zu beeinflussen.



All Articles