Donnerstag, November 26, 2015

Korrigierte Histogramm-Statistiken im SQL Server anlegen

Nachdem ich viele Jahre lang Tom Kytes Mantra wiederholt habe, dass alle RDBMS unterschiedlich sind und man die Unterschiede kennen muss, um sinnvoll mit den Systemen umzugehen, behaupte ich in jüngerer Vergangenheit immer häufiger, dass die relationalen Datenbanken im Prinzip alle ziemlich ähnlich sind und sich in mancher Hinsicht immer ähnlicher werden. In jedem Fall bin ich immer wieder froh, wenn ich neue Gemeinsamkeiten feststelle, so etwa auch diese hier: im immer wieder lesenswerten SQL Performance.com Blog erläutert Dan Holmes anhand eines umfangreichen Beispiels, wie man mit Hilfe der (nicht supporteten) Option STATS_STREAM des UPDATE STATISTICS Kommandos Optimizer-Statistiken exportieren und importieren kann, um auf diese Weise ein passenderes Histogram einer ungleichen Datenverteilung zu erstellen, als das durch die WITH SAMPLE Option von UPDATE STATISTICS erzeugte. Im Oracle-Universum ist diese Strategie nicht unbekannt (und wird dort sogar offiziell unterstützt) - ein entsprechendes Beispiel liefert (wie üblich) Jonathan Lewis.

Montag, November 23, 2015

Ein griffiges Beispiel für Lateral Inline Views

Auf den Lateral Join, der in 12c eingeführt wurde, habe ich hier schon vor geraumer Zeit hingewiesen - aber was damals fehlte, war ein griffiges Beispiel dafür, was man damit eigentlich anstellen kann. Das hat nun Oren Nakdimon in Teil 7 seiner überaus lesenswerten Serie Write Less with More ergänzt. In seinem Beispiel wird eine Projekt-Tabelle mit einer last_days_to_show_in_reports Spalte vorgestellt, in der für die einzelnen Einträge unterschiedliche Zeiträume angegeben sind (als Anzahl von Tagen). Will man an diese Tabelle eine der last_days_to_show_in_reports Angabe entsprechende Anzahl von Datensätzen joinen, so könnte man vor 12c einen Join mit einer Generator-Query und einer geeigneten ">=" Bedingung definieren (oder alternativ eine table function mit Collection Unnesting, was für mich aber deutlich komplizierter aussieht). In 12c lässt sich dazu in der FROM Klausel eine sehr übersichtliche korrelierte Subquery einsetzen, die nach dem Schlüsselwort LATERAL erscheint. Na gut: ich gebe zu, dass das bei wiederholtem Lesen auch auch nicht selbsterklärend klingt - aber wenn man sich das Code-Beispiel im Artikel ansieht, wird der Fall deutlich klarer.

Donnerstag, November 19, 2015

Ein Wait Interface für Postgres

Ein schöner Hinweis im Artikel Wait interface in PostgreSQL, dessen Autor ich leider nicht ohne weitere Recherche namentlich benennen kann: für postgres 9.4 gibt es eine von Ildus Kurbangaliev entwickelte Erweiterung pg_stat_wait, die möglicherweise in 9.6 Teil des Standards werden wird (aktuell aber noch ein Recompile von postgres und eine Einbindung via shared_preload_libraries erfordert). Nach der Installation stehen diverse Funktionen und Views zur Verfügung, die Informationen zu Events unterschiedlicher Typen bereitstellen (LWLocks, Storage, Locks, Network, Latch, CPU). Zu den Wait Events werden Angaben zur Häufigkeit des Auftretens und zur Dauer der Wartesituationen erfasst. Dabei ist die Erfassung Sample-basiert (wodurch sich nur ein geringer Overhead ergibt), aber man kann auch ein explizites Tracing der Session aktivieren (pg_start_trace), das ein lückenlose Erfassung der Waits ermöglicht. Insgesamt eine Ergänzung, die meine Begeisterung für die Analyse von Performance-Fragestellungen in postgres deutlich erhöhen würde, und die aus meiner Sicht unbedingt in den Standard gehört.

Dienstag, November 17, 2015

Wait Event Analyse mit Flame Graphs

Luca Canali hat im Databases at CERN Blog wieder einmal eine detaillierte Analyse vorgestellt, in der er Flame Graphs mit Extended Stack Profiling verbindet und damit die Aussagen des Oracle Wait Interfaces näher beleuchtet. Der eigentliche Anlass der Untersuchung ist dabei die Beobachtung des Auftretens von Fällen, in denen die DB time größer ist als die Summe von CPU time und Wait time, was a) nicht vorkommen sollte und b) mit dem Einsatz moderner Storage zu tun haben kann. Eine Nacherzählung der umfangreichen Analyseschritte erscheint mir eher witzlos - mir genügt hier der Vermerk, dass das verwendete Instrumentarium sehr genaue Aussagen über das Systemverhalten erlaubt. Wenn ich gelegentlich wieder mal mit komplexen und OS-nahen Problemen zu tun habe, sollte ich mich hier bedienen. Nützlich ist dabei auch, dass der Herr Canali seine älteren Artikel zu ähnlichen Themen und die verwendeten Tools verlinkt hat.

Dienstag, November 10, 2015

Falsche Ergebnisse in 12c mit ROWSETS

Gerne würde ich an dieser Stelle erklären, was ein ROWSET (außerhalb von Java) in Oracle 12c eigentlich ist, aber dazu findet man nicht allzu viele Erklärungen. Aber zumindest kann man offenbar mit einiger Sicherheit sagen, dass es sich nicht um ein besonders ausgereiftes neues Feature handelt...

In einem aktuellen Scratchpad-Artikel beschreibt Jonathan Lewis einen Fall, in dem die Auswahl unterschiedlicher Arraysize-Angaben dazu führt, dass die gleiche Query (auf der gleichen Datenbasis) bei mehrfacher Ausführung eine unterschiedliche Anzahl von Ergebniszeilen zurückliefert. Nun sollten Queries bei unveränderter Datenbasis grundsätzlich die gleichen Ergebnisse liefern - und die Arrayssize, die nur bestimmt, wie viele Datensätze im Rahmen einer Fetch-Operation an den Client geschickt werden, sollte ganz gewiß keine Ergebnisänderung hervorrufen. Die Ursache der unterschiedlichen Ergebnisse zeigt dbms_xplan.display_cursor mit aktivierter Anzeige der Projection. In dieser findet sich in runden Klammern eine Angabe (rowset=200). Zur Bedeutung des Features sagt der Herr Lewis nur: "This is reporting a feature new in 12c (and not to be confused with Oracle Java Rowsets) that should improve the performance of some queries." Und viel mehr habe ich dazu auch an anderer Stelle dazu nicht gefunden. Der Hinweis auf die mögliche Rolle der Rowset verdankte sich dabei übrigens Stefan Koehler, der vor einigen Wochen in den Kreis der Oak Table aufgenommen wurde - was sicherlich eine sehr plausible Ergänzung dieser Tafelrunde darstellt.

Basierend auf dem Scratchpad-Artikel (und weiteren bekannten Bugs) hat inzwischen Mike Dietrich die offizielle Empfehlung ausgesprochen, vorläufig auf die Verwendung von rowsets zu verzichten.

Nachtrag 17.11.2015: Mike Dietrich hat inzwischen in einem weiteren Artikel zusätzliche Details zum Thema geliefert. Einerseits nennt er die Ursache des Problems: "When a hash join operation receives rowsets from its right input but then produces one row at a time as output. This explains why one of the bugs had as potential workaround hash_join_enabled=false (and please don't use this as a w/a!!!)." Zusätzlich liefert er neben der globalen Deaktivierung des Features noch zwei weitere Workarounds: das Einspielen des zugehörigen Bug-Fixes (der aber zum Zeitpunkt der Veröffentlichung des Artikels noch nicht verfügbar war) und die Verwendung eines speziellen Events im spfile, das die rowset Verwendung nur im angesprochenen Problemfall deaktiviert.

Mittwoch, November 04, 2015

Clustering Factor in RAC-Systemen

Da ich seit einigen Monaten wieder verstärkt mit RAC-Datenbanken (und überhaupt mit Oracle) zu tun habe, ist der folgende Hinweis von Jonathan Lewis für mich recht interessant: in einem RAC-System besitzt jede Instanz im Rahmen des ASSM-Speichermanagements ihren eigenen level 1 (L1) bitmap Block und schreibt neue Datensätze insofern auch in ihr zugeordnete Datenblöcke. Wenn konkurrierende Inserts über mehrere Instanzen hinweg erfolgen, landen die neusten Einträge daher in vielen unterschiedlichen Blocks, denn neben der Anzahl der Instanzen spielt dabei auch die Strategie von ASSM eine Rolle, Inserts auch in einer einzelnen Instanz auf 16 unterschiedliche Blocks zu verteilen. Der Clustering Factor eines Index würde unter diesen Umständen in nahezu jedem Fall extrem schlecht aussehen, wenn es nicht die Möglichkeit gäbe, mit Hilfe des Parameters table_cached_blocks dafür zu sorgen, dass Oracle ein gewisses Erinnerungsvermögen zeigt, wenn bestimmt wird, wie stark die Daten in der Tabelle im Hinblick auf einen Index geordnet sind. Ursprünglich wurde hier nur gezählt, wie oft sich die data block Adresse ändert, wenn man den sortiereten rowid-Verweisen der Index-Struktur folgt - was in Abwesenheit von RAC und ASSM immer noch eine plausible Strategie ist. Ausgehend von diesen Überlegungen schlägt der Herr Lewis als Ausgangswert für table_cached_blocks in einem RAC-System 16 * Anzahl Instanzen vor - und das ist angesichts dieser Überlegungen nachvollziehbar.