Mittwoch, Oktober 29, 2014

first_rows und first_rows_n

Jonathan Lewis weist in seinem Blog darauf hin, dass die Optimizer-Modi first_rows und first_rows_n deutlich weniger gemein haben, als man vielleicht annehmen könnte. Während first_rows seit langem nur noch aus Gründen der Abwärtskompatibiltät unterstützt wird und diverse Heuristiken benutzt, um die Optionen des Optimizers einzuschränken, arbeiten die first_rows_n-Varianten strikt kostenbasiert, ohne dem Optimizer massiv in die Arbeit zu pfuschen. Tatsächlich gibt es allerdings einen Fall, in dem first_rows_n sich so verhält wie first_rows, nämlich die - durchaus merkwürdige - Variante first_rows(0). In diesem Fall wechselt der Optimizer intern tatsächlich auf die Strategie first_rows. Allerdings gibt der Herr Lewis zu Recht zu bedenken: "Mind you, if you only want the first zero rows of the result set there’s a much better trick you can use to optimise the query – don’t run the query."

Für mich persönlich brachte der Artikel noch mal die Erinnerung, dass der Session-Parameter und der Hint first_rows_n eine leicht abweichende Parametrisierung besitzen (die auch explizit dokumentiert ist): während der Parameter nur die Werte 1, 10, 100 und 1000 als gültig betrachtet, kann man im Hint für n jeden beliebigen ganzzahligen Wert angeben. Nachlesen kann man die Details in meinen Kommentaren zum Artikeln und der erläuternden (man könnte auch sagen: richtig stellenden) Antwort des Autors.

Der Vollständigkeit halber hier noch ein Link auf meinen älteren Versuch, dem Costing für first_rows_n auf die Spur zu kommen, zum dem mir damals Randolf Geist die Pointe geliefert hatte (man könnte auch sagen: die Richtigstellung).

Nachtrag 02.11.2014: in einem weiteren Artikel zeigt Jonathan Lewis ein Beispiel, in dem first_rows_10 einen HASH JOIN großer Tabellen hervorruft, weil die cardinalities falsch bestimmt werden (nämlich als 10 für beide Eingangsmengen).

Montag, Oktober 27, 2014

Temp Table Transformation

Randolf Geist erläutert in seinem Blog das Verhalten der Temp Table Transformation, die hauptsächlich (aber nicht ausschließlich) dann anzutreffen ist, wenn eine CTE im Plan mehrfach referenziert wird und mindestens ein (Filter- oder Join-) Prädikat enthält, und weist insbesondere darauf hin, dass diese Transformation nicht kostenbasiert erfolgt, sondern automatisch. In Fällen, in denen eine zusätzliche Bedingung jenseits des CTE-Kontexts eine dramatische Reduzierung der Ergebnismenge hervorrufen würde, ist diese Materialisierung demnach ausgesprochen kontraproduktiv. Darüber hinaus rufen die materialiserten CTEs in solchen Fällen seit 11.2.0.4 offenbar extrem abwegige Cardinality-Schätzungen hervor, die in komplexeren Plänen sehr unglückliche Konsequenzen hervorrufen könnten. Insgesamt scheint es gute Gründe zu geben, die Materialisierung von CTEs sehr genau zu beobachten und ggf. durch den (nach wie vor undokumentierten) Inline-Hint zu vermeiden.

Nachtrag 22.05.2015: in einem Folgeartikel zeigt Randolf, dass die temp table transformation offenbar auch dazu führt, dass alle Spalten der CTE materialisiert werden und nicht nur die, die im Rahmen der folgenden Schritte tatsächlich benötigt werden. In vielen anderen Fällen ist Oracle sehr gut darin, die projection zu optimieren (Ausnahme: MERGE), aber hier ist das offenbar nicht so.

Sonntag, Oktober 26, 2014

Histogramme, Cardinality und der Optimizer Mode first_rows_100

Eine interessante Antwort auf die Frage nach unplausiblen Cardinalities, die sich trotz frequency histogram ergeben, hat Jonathan Lewis in einem OTN-Forums-Thread gegeben. Der vom Fragesteller gelieferte handliche Testfall zeigte sehr deutlich, dass in seinem System mit 11.2.0.4 für einfache Queries sehr merkwürde Cardinalities ermittelt wurden, obwohl das vorhandene Histogramm die tatsächliche Verteilung exakt abbildete. Diverse Beiträger konnten nur feststellen, dass das Verhalten in ihren Systemen (zwischen 11.2.0.1 und 12.1.0.2) nicht reproduzierbar war, aber der Herr Lewis konnte das fehlende Puzzle-Teil benennen: wenn man den Optimizer-Mode auf first_rows_100 umstellt, werden aus den plausiblen Schätzungen augenblicklich recht nutzlose Zahlen. Ich pflege die Wirkung der first_rows_n-Einstellungen regelmäßig zu unterschätzen und sollte mir endlich merken, wie invasiv dieser Modus tatsächlich ist.

Dienstag, Oktober 21, 2014

Index Reorganisation

Franck Pachot beantwortet in seinem Blog eine Frage, die mir gelegentlich durch den Kopf gegangen ist, ohne dass ich mich ernsthaft um ihre Beantwortung gekümmert hätte: welche Form der Reorganisation eines Index ist unter welchen Voraussetzungen die passendste? Dazu liefert er einen einfachen Testfall, zu dem er die relevanten Statistiken zum Index und zur Ressourcen-Nutzung im Rahmen der Reorganisation beisteuert. Die Ergebnisse lauten ungefähr:
  • COALESCE: eine Online-Operation, die die leaf blocks defragmentiert, aber nicht wieder frei gibt, sondern für die zukünftige Verwendung in der Index-Struktur hält.
  • SHRINK SPACE COMPACT: ebenfalls eine Online-Operation, die ungefähr das gleiche leistet wie coalesce, aber etwas mehr LIOs hervorruft und die leaf Blocks etwas effektiver aufräumt (Status: FS4 statt FS2).
  • SHRINK SPACE: wieder eine Online-Operation mit nahezu identischer Ressourcen-Nutzung wie das shrink space compact, aber in diesem Fall werden die geräumten Blocks tatsächlich wieder freigegeben.
  • REBUILD: erfordert ein share lock, das konkurrierende DML-Operationen blockiert, benötigt aber weniger Ressourcen und führt zu einer vollständig aufgeräumten Index-Struktur: in diesem Fall wird auch die Index-Höhe auf den passenden Wert zurückgeführt - was im Fall des kompletten Neuaufbaus keine besondere Überraschung darstellt.
  • REBUILD ONLINE: ein EE-Feature, das - wie der Name vermuten lässt - den Neuaufbau ohne umfangreicheres Locking ermöglicht (in der Regel auf Kosten erhöhter Ressourcen-Nutzung.
Insbesondere die Tatsache, dass das shrink space mehr leistet als das shrink space compact ist ein Aspekt, der mir schon mehrfach entfallen ist..

Montag, Oktober 20, 2014

Advanced Index Compression in 12c

Leider schaffe ich es derzeit nicht, mir eigene Gedanken zu machen, so dass ich mich darauf beschränken muss, anderer Leute Ergebnisse zu reproduzieren. Einer der Autoren, bei denen ich das recht gerne mache, ist Richard Foote, der inzwischen wieder sehr regelmäßig neue Artikel schreibt. Zwei davon betreffen die in 12c eingeführte Advanced Index Compression:
  • Index Compression Part V: 12c Advanced Index Compression (Little Wonder): erläutert die Hintergünde dieser neuen Option (die eigens lizenziert werden muss). Bisher war es bei der klassischen Index--Komprimierung, bei der Duplikate in den führenden Spalten eines Index durch Referenzen auf eine Prefix-Sektion im Block ersetzt werden, ohne Weiteres möglich, den Index durch die Komprimierung zu vergrößern - dann nämlich, wenn es zu wenig Wiederholungen gab, um den Overhead der eingeführten Symboltabelle zu rechtfertigen. Bei der Advanced Index Compression wird dieses Problem auf zwei Ebenen behandelt: zum einen automatisiert die Option die Bestimmung eines sinnvollen Komprimierungsgrads, zum anderen wählt sie die Komprimierung blockspezifisch aus, so dass einzelne Bereiche des Index, die sich gut komprimieren lassen, besser gepackt werden als andere Bereiche mit wenigen Duplikaten.
  • Index Compression Part VI: 12c Index Advanced Compression Block Dumps (Tumble and Twirl): beschäftigt sich mit den technischen Details der Implementierung und mit der Repräsentation der Komprimierung im Block-Dump. Der zentrale Punkt sind die Einträge kdxlepnro (nro = number of rows?) und kdxlepnco (nco = number of columns?) im Index-Block-Dump, von denen der erste die Anzahl der Einträge in der Prefix-Tabelle angibt und der zweite die Anzahl komprimierter Spalten. Bei kdxlepnco taucht zusätzlich in Klammern die Angabe (Adaptive) auf, die auf die Verwendung des neuen Features hinweist.
  • Index Advanced Compression vs. Bitmap Indexes (Candidate): beantwortet die Frage, ob die Advanced Index Compression die Nutzung von Bitmap Indizes uninteressant macht, mit einem schallenden Nein. Während B*Tree-Indizes sich nur auf Block-Ebene komprimieren lassen, kann ein Bitmap-Index die Werte der gesamten Tabelle zusammenfassen. Bei geeigneter Werte-Clusterung kann ein Bitmap-Index extrem kompakt werden - und sehr viel kleiner als jeder noch so gut gepackte B*Tree-Index.
Weitere Artikel sind angekündigt, die ich dann voraussichtlich ebenfalls hier zusammenfasse werde. Grundsätzlich sieht das wie eine sehr sinnvolle Funktionalität aus - ich hatte mich in der Vergangenheit häufiger gefragt, wieso ich zunächst einen plausiblen Kompressionsgrad ermitteln musste (per ANALYZE INDEX), ehe ich diesem Vorschlag folgte, da ich an dieser Stelle recht wenig Spielraum für individuelle Anpassungen und vom Vorschlag abweichende Entscheidungen sah.

Freitag, Oktober 17, 2014

Detailsinformationen zu den system statistics

Franck Pachot liefert im dbi services Blog ein nettes Skript zur Analyse der Systemstatistiken, auf denen das Costing des Optimizers basiert. Dabei ergänzt er zu den Basisinformationen aus (SYS.)AUX_STATS$ diverse Angaben zu ihrer Berechnung, die man sich ohne Skript mehr oder minder mühsam zusammensuchen muss.

Mittwoch, Oktober 15, 2014

Optimierung für pg_dump und pg_restore

Josh Berkus erwähnt in seinem Blog einige Optionen, mit denen man die Performance der postgres-Export und Import-Tools pg_dump und pg_restore verbessern kann. Die Liste enthält unter anderem folgende Punkte:
  • Parallelisierung über die -j multiprocess Option (für pg_restore und seit 9.3 auch für pg_dump).
  • Komprimierung des Dumps bei der Erstellung.
  • Deaktivierung der Binär-Replikation im Zielsystem während des Imports.
  • Anpassungen der postgresql.conf des Ziel-Clusters zur Unterstützung einer schnellen Wiederherstellung. Die Wiedergabe der genannten Optionen erspare ich mir.
Da das Importieren von postgres-Dumps ohne Optimierung eher zäh abläuft, werde ich diese Hinweise bei nächster Gelegenheit berücksichtigen.

Sonntag, Oktober 12, 2014

Read Privileg in 12.1.0.2 (ohne Select for Update)

Zu den eher unverständlichen Entscheidungen, die man bei Oracle im Zusammenhang der Definition von Privilegien gemacht hat, gehörte sicherlich die Zuordnung des Rechts zur Ausführung der Operation SELECT FOR UPDATE zum einfachen SELECT-Privileg. Dadurch konnten Benutzer allerlei Unfug anstellen, weil die Operation recht massive Sperren erzeugt. In 12.1.0.2 gibt es jetzt das neue READ-Privileg, das SELECT FOR UPDATE explizit nicht mehr enthält. Den Hinweis auf diese Ergänzung habe ich bei Gary Myers gefunden.