Ich habe eine Tabelle mit 250 KB Zeilen in meiner Testdatenbank. (Es werden ein paar Hundert Millionen produziert, wir können dort dasselbe Problem beobachten.) Die Tabelle hat eine Zeichenfolgen-ID nvarchar2 (50), die nicht null ist und einen eindeutigen Index enthält (es ist nicht die PK).
Die Bezeichner bestehen aus einem ersten Teil mit 8 verschiedenen Werten in meiner Testdatenbank (und ungefähr tausend in der Produktion), einem @ -Zeichen und schließlich einer 1- bis 6-stelligen Zahl. Zum Beispiel könnte es 50.000 Zeilen geben, die mit 'ABCD_BGX1741F_2006_13_20110808.xml @' beginnen, gefolgt von 50.000 verschiedenen Zahlen.
Wenn ich eine einzelne Zeile basierend auf ihrem Bezeichner abfrage, wird die Kardinalität auf 1 geschätzt, die Kosten sind sehr gering und es funktioniert einwandfrei. Wenn ich mehr als eine Zeile mit mehreren Bezeichnern in einem IN-Ausdruck oder einem OR-Ausdruck abfrage, sind die Schätzungen für den Index völlig falsch, sodass ein vollständiger Tabellenscan verwendet wird. Wenn ich den Index mit einem Hinweis erzwinge, ist er sehr schnell, der vollständige Tabellenscan wird tatsächlich eine Größenordnung langsamer ausgeführt (und viel langsamer in der Produktion). Es ist also ein Optimierungsproblem.
Als Test habe ich die Tabelle (im selben Schema + Tablespace) mit genau derselben DDL und genau demselben Inhalt dupliziert. Ich habe den eindeutigen Index für die erste Tabelle aus gutem Grund neu erstellt und genau denselben Index für die Klontabelle erstellt. Ich habe eine DBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);
. Sie können sogar sehen, dass die Indexnamen fortlaufend sind. Der einzige Unterschied zwischen den beiden Tabellen besteht nun darin, dass die erste über einen langen Zeitraum in zufälliger Reihenfolge geladen wurde, wobei Blöcke auf der Festplatte verteilt waren (in einem Tablespace zusammen mit mehreren anderen großen Tabellen). Die zweite Tabelle wurde als Stapel geladen INSERT-SELECT. Ansonsten kann ich mir keinen Unterschied vorstellen. (Die Originaltabelle wurde seit der letzten großen Löschung verkleinert, und danach gab es keine einzige Löschung mehr.)
Hier sind die Abfragepläne für die Kranken- und die Klontabelle (Die Zeichenfolgen unter dem schwarzen Pinsel sind auf dem gesamten Bild und auch unter dem grauen Pinsel gleich.):
(In diesem Beispiel gibt es 1867-Zeilen, die mit dem schwarz gepinselten Bezeichner beginnen. Eine 2-zeilige Abfrage ergibt eine Kardinalität von 1867 * 2, eine 3-zeilige Abfrage ergibt eine Kardinalität von 1867 * 3 usw.) Zufall, Oracle scheint sich nicht um das Ende der Bezeichner zu kümmern.)
Was könnte dieses Verhalten verursachen? Offensichtlich wäre es ziemlich teuer, die Tabelle in der Produktion neu zu erstellen.
USER_TABLES: http://i.stack.imgur.com/nDWze.jpg USER_INDEXES: http://i.stack.imgur.com/DG9um.jpg Ich habe nur den Namen des Schemas und des Tabellenbereichs geändert. Sie können sehen, dass die Tabellen- und Indexnamen mit denen auf dem Bildschirmfoto des Abfrageplans übereinstimmen.
quelle
in
) auch die allgemeinen Statistiken übertroffen hätten, nicht wahr ? Ich denke, der CBO nimmt die Kardinalität 1 an, aber nur im einfachsten Fall. Ich nehme an, Sie könnten das Ganze mit einem großen umgehen,UNION ALL
aber es kann andere Gründe geben, dies nicht zu tun, und JL erwähnt andere mögliche Problemumgehungen in dem verlinkten Blog-Beitrag.method_opt=>'for all indexed columns'
?Ich habe die Lösung gefunden! Es ist so schön und ich habe tatsächlich eine Menge über Oracle gelernt.
Mit einem Wort: Histogramme.
Ich las viel darüber, wie Oracle CBO funktioniert, und stieß auf Histogramme. Ich habe es nicht ganz verstanden, also habe ich mir die Tabelle USER_HISTOGRAMS und voilá angesehen. Es gab mehrere Zeilen für den Kranken-Tisch und praktisch nichts für den geklonten Tisch. Für die Krankentabelle gab es eine Zeile für jedes der 8 verschiedenen Identifikator-Startteile. Und das ist der Schlüssel: Sie wurden mit 32 Zeichen vor dem @ -Zeichen abgeschnitten. Wie gesagt, der erste Teil der Tasten wiederholt sich stark, sie werden nach dem @ -Zeichen anders.
Es scheint, dass Histogramme leistungsfähiger sein können als die einfache Tatsache, dass ein eindeutiger Index für einen bestimmten Wert immer eine Kardinalität von 0 oder 1 hat. Als ich nach mehr als 2 Zeilen fragte, sah Oracle sich das Histogramm an, dachte, dass es Zehntausende von Werten für diesen Identifier-Startteil geben könnte, und warf den CBO vom Kurs ab.
Ich habe die Histogramme für diese Spalte in der alten Tabelle gelöscht und das Problem ist behoben!
Weitere Informationen: https://blogs.oracle.com/optimizer/entry/how_do_i_drop_an_existing_histogram_on_a_column_and_stop_the_auto_stats_gathering_job_from_creating
quelle
Ich habe Jonathan Lewis eine E-Mail darüber geschickt und eine sehr hilfreiche Antwort erhalten:
Ich empfehle dringend, die von ihm gelinkten Blog-Beiträge zu lesen. Sie beschreiben detailliert die Begrenzung der Histogramme, in denen Sie sich befinden, z. B .:
quelle