Freitag, Januar 30, 2015

Einzelne Query aus dem Shared Pool löschen

Eigentlich war ich mir ziemlich sicher, dass ich hier irgendwo einen Link auf Kerry Osbornes Erläuterungen zum Löschen einer einzelnen Query aus dem Shared Pool untergebracht hätte - aber anscheinend ist das nicht der Fall. Daher hole ich zunächst dieses Versäumnis nach:
Auf die Suche nach diesen Links habe ich mich gemacht, weil Franck Pachot gerade einen Artikel zum gleichen Thema veröffentlicht hat, der mit einem Bind Peeking Problem einsetzt. Grundsätzlich ist die Möglichkeit der Löschung einer einzelnen Query aus dem Cache natürlich immer dann interessant, wenn man nicht den kompletten Shared Pool löschen will, sondern nur ein punktuelles Hard Parse hervorrufen möchte.

Dienstag, Januar 27, 2015

In-Memory Performance

Gerade habe ich noch mal überprüft, dass es Fälle gibt, in denen ich Artikel aus dem Oracle Scratchpad von Jonathan Lewis hier nicht verlinke. Das geschieht aber selten und gewöhnlich nur aus Gründen der Trägheit. Allerdings ist in der Tat nicht jeder Artikel von gleicher Bedeutung, manchmal sind die Themen auch relativ abgelegen und betreffen seltene Ausnahmefälle. Der aktuelle Artikel In-memory DB jedoch befasst sich mit einer recht grundsätzlichen Frage: wieso ist eine (column-store) In-Memory Datenbank effektiver als eine traditionelle (row-store) Datenbank, deren Daten sich komplett im Buffer Cache befinden?

Die Antwort erläutert zunächst das Verfahren beim traditionellen Tablescan einer komplett im Cache verfügbaren Tabelle: dabei muss für jeden einzelnen Block eine recht komplizierte Schrittfolge durchgeführt werden:
Oracle has to acquire the “cache buffers chains” latch, pin the block, drop the latch, and scan the block testing each row, then acquire the latch, unpin the block, and drop the latch.  Scanning the block requires a walk through the row directory and, for each row pointer, jumping to the correct location in the block for the row, stepping along the row one column at a time to get to the correct row, and then checking the column  value.
Im Fall des In-Memory Scans ist das Vorgehen ein grundsätzlich anderes:
  1. Für jede einzelne Spalte wird eine eigene In-Memory-Repräsentation erstellt, so dass das relevante Datenvolumen, auf das zugegriffen werden muss, unter Umständen massiv reduziert werden kann.
  2. Diese In-Memory-Repräsentation kann das im Speicher vorzuhaltende Datenvolumen durch Komprimierung/Deduplizierung noch einmal deutlich reduzieren. Diese Komprimierung ist ein grundsätzlicher Vorteil der column stores.
  3. Die In-Memory-Repräsentation wird in einzelne Chunks unterteilt, zu denen jeweils die low und high value Angaben bekannt sind, was es ermöglichen kann, komplette Chunks zu überspringen.
  4. Durch SIMD-Operationen (Single Instruction,  Multiple Data) kann der Zugriff im einzelnen Chunk weiter optimiert werden.
Für den Zugriff auf wenige Spalten ist das In-Memory-Verfahren in der Regel sehr effektiv - aber wenn sehr viele Spalten benötigt werden, kann dieser Vorteil gegenüber dem row-store verschwinden. Dabei gilt:
  • Using the In-memory Database, you can identify the rows you want very quickly but it’s relatively slow to reconstruct them.
  • Using a fully cached traditional row-store, it’s relatively slow to identify the rows you want, but once you’ve found them you spend no time reconstructing them.
Über diese grundsätzliche Analyse hinaus liefert der Artikel auch noch eine größere Anzahl von Links auf weitere Untersuchungen zum In-Memory-Thema - unter anderem auf Maria Colgans Artikelserie, aber auch auf andere relevante Beiträge.

Montag, Januar 26, 2015

Oracle Sample Schemas auf GitHub

Wieder nur eine kurze Notiz, aber eine mit einem aus meiner Sicht recht bedeutsamen Inhalt: wie man von Christopher Jones erfährt gibt es Oracles Beispiel-Schemata seit kurzem als Repository auf GitHub:
This repository contains a copy of the Oracle Database sample schemas that are installed with Oracle Database Enterprise Edition 12c. These schemas are used in Oracle documentation to show SQL language concepts. The schemas themselves are documented in Oracle Database Sample Schemas, 12c Release 1 (12.1)
The schemas are:
  • HR: Human Resources
  • OE: Order Entry
  • PM: Product Media
  • IX: Information Exchange
  • SH: Sales History
  • BI: Business Intelligence
Due to widespread dependence on these scripts in their current form, no pull requests for changes can be accepted.
Klingt vielleicht nicht besonders aufregend, vereinfacht den Aufbau nachvollziehbarer Beispiele aber ganz erheblich.

Mittwoch, Januar 21, 2015

Größe des Initial Extents für Partitionierte Tabellen

Dom Brooks weist in seinem Blog auf zwei Punkte hin, von denen mir (wie ihm) der erste komplett entgangen war:
  • die Default-Größe des Initial Extents für partitionierte Tabelle ist seit 11.2.0.2 auf 8MB erhöht worden, davor betrug sie 64KB. Das kann im Fall geringfügig gefüllter Partitionen zur Verschwendung von Speicherplatz führen, wobei solche Partitionen natürlich unter Umständen auch ein Anlass sein könnten, über die Partitionierungsstrategie nachzudenken.
  • der zweite Punkt war mir klar: da die Maximalanzahl der Partitionen in einer Tabelle 1024K -1 = 1048575 beträgt, kann man bei Interval Partitionierung bei Auswahl kleinerer Intervalle relativ leicht an die Begrenzungen stossen.
Beide Effekte werden anhand aussagekräftiger Beispiele erläutert.

Dienstag, Januar 20, 2015

NLS_LANG für Oracle unter Windows

Vor ein paar Jahren habe ich hier eine ausgesprochen knappe Notiz untergebracht, um mich daran zu erinnern, den Registry-Schlüssel NLS_LANG unter Windows auf GERMAN_GERMANY.WE8PC850 zu setzen, um eine korrekten Darstellung von Umlauten in sqlplus zu erhalten. Dass man den Sachverhalt auch umfassend analysieren kann, hat mir jetzt Franck Pachot gezeigt, der das unterschiedliche Verhalten von WE8MSWIN1252 und WE8PC850 detailliert beschreibt: dabei ist WE8MSWIN1252 die default-Einstellung in der Registry und für GUI-Tools durchaus geeignet. Sqlplus allerdings ist ein DOS commend line Tool und verwendet eine andere Codepage - nämlich 850. Für die korrekte Darstellung innerhalb von sqlplus ist daher WE8PC850 gut geeignet - aber leider werden Daten, die aus sqlplus via spool in eine Datei geschrieben werden, die man dann mit einer Windows-Applikation (sagen wir: notepad.exe) öffnet, ebenfalls mit Codepage 850 kodiert - und das führt dann unter Umständen in der Ausgabe zu Problemen. Im Artikel wird der Sachverhalt noch genauer erläutert, aber ich breche die Nacherzählung an dieser Stelle ab und verweise auf die Quelle.

P.S.: vermutlich wusste ich das alles mal, hatte es aber inzwischen komplett aus meinem Gedächtnis gelöscht.

Dienstag, Januar 13, 2015

Parallel Query, OR Expansion und überflüssige Buffer Sorts

In den letzten Tagen hat Randolf Geist zwei Artikel zum Auftreten von überflüssigen Buffer Sort Operationen bei der Ausführung paralleler Queries veröffentlicht:
  • Unnecessary BUFFER SORT Operations - Parallel Concatenation Transformation: zeigt, dass die Kombination von Paralleler Query und einer OR Expansion Transformation (OR verknüpfte Prädikate werden in zwei über UNION ALL verbundene Teil-Queries aufgeteilt, wobei unerwünschte Duplikate durch Verwendung der LNNVL-Funktion vermieden werden) zum Auftreten überflüssiger Buffer Sort Operationen führen kann, die die Vorteile der Parallelisierung als blocking operation massiv reduzieren (und die Ressourcennutzung erhöhen). Ähnliche Effekte können auch beim manuellen Rewrite solcher UNION ALL-Konstrukte auftreten.
  • "SELECT * FROM TABLE" Runs Out Of TEMP Space: zeigt einen Fall, in dem sich ähnliche Buffer Sort Steps bei einem einfachen parallel ausgeführten "SELECT * FROM table" ergeben, wobei es sich um ein Exadata System und eine partitionierte Tabelle handelt, die über HCC komprimiert ist. Dabei führt die Zwischenspeicherung der Ergebnisse zu extremen Laufzeiten, weil die ersten Datensätze erst geliefert werden, wenn die gesamte Tabelle in den Buffer geladen wurde - und potentiell kann es zu einem Abbruch der Ausführung kommen, wenn das Datenvolumen einer entpackten Partition größer ist als der TEMP space. Auch in diesem Fall ist eine Concatenation Transformation im Spiel, die auf eine andere Transformation folgt, nämlich auf eine "table expansion", die es erlaubt einige Partitionen (auf die ein selektiver Zugriff erfolgt) via Index zu lesen und andere (bei denen der Zugriff nicht selektiv ist) via Full Scan, indem dort die Index-Partionen unusable gemacht werden. Natürlich gibt es mehrere mögliche Workarounds (Verzicht auf Parallelisierung, Verzicht auf table expansion), aber offenbar ist die OR Expansion auch in dieser Situation ein potentielles Problem - obwohl sie im ersten Moment so harmlos wirkt...
Ich habe in der Überschrift und auch im Text mehrfach von OR Expansion gesprochen, obwohl die Transformation in den Artikeln als Concatenation Transformation angesprochen wird, gehe aber bis ich eines Besseren belehrt werde davon aus, dass es sich nur um zwei Namen für die gleiche Sache handelt.

Nachtrag 14.01.2015: die Antwort auf die Frage nach der Identität von OR Expansion und Concatenation Transformation bleibt erst mal offen: Randolf Geist bestätigte meine Einschätzung, dass es sich um die gleiche Transformation handelt, aber Stefan Koehler ist anderer Meinung - und auch der Herr Koehler hat in der Regel sehr gute Gründe für solche Aussagen.

Mittwoch, Januar 07, 2015

SQL Server Admin-Skripts

Wenn ich mich wieder mal als DBA für einen SQL Server ausgeben möchte, sollte ich mich an folgenden Rat von Brent Ozar erinnern:
If you’re a production database administrator responsible for backups, corruption checking, and index maintenance on SQL Server, try Ola Hallengren’s free database maintenance scripts. They’re better than yours (trust me), and they give you more flexibility than built-in maintenance plans.
Im Fall meiner SQL Server "Skriptsammlung" hat der Herr Ozar jedenfalls recht: die Hallgrenschen Skripte sind besser als meine.

Und gerade sehe ich, dass Erin Stellato zuletzt mehrere Artikel zur Durchführung von Health Checks im SQL Server veröffentlicht hat, die ich hier auch noch verlinke:

Dienstag, Januar 06, 2015

Merge mit Prüfung auf Ungleichheit

Wenn man im Merge-Statement die Aktualisierung im "when matched" Zweig auf Fälle beschränken will, bei denen sich tatsächlich Änderungen der Daten ergeben haben - was nicht nur aus Gründen der Performance eine gute Idee ist, sondern auch dann, wenn man in ETL-Operationen eine Historie von Veränderungen pflegen möchte, dann kann man dazu eine Where-Bedingung verwenden, die die Gleichheit neuer und existierender Werte für alle betroffenen Felder überprüft. Zu diesem Zweck habe ich in der Vergangenheit gelegentlich recht unübersichtliche OR-Verknüpfungen verwendet, bei denen NULL-Werte via COALESCE auf einen in den Daten nicht erscheinenden Wert gesetzt wurden. Eine deutlich elegantere Lösung zum Problem findet man bei Stew Ashton, der für dieses Mapping die DECODE-Funktion einsetzt. Dazu ein Beispiel:

drop table test_merge_src;
create table test_merge_src
as
select rownum id
     , 1 col1
     , 2 col2
  from dual
connect by level <= 10;

select * from  test_merge_src;

        ID       COL1       COL2
---------- ---------- ----------
         1          1          2
         2          1          2
         3          1          2
         4          1          2
         5          1          2
         6          1          2
         7          1          2
         8          1          2
         9          1          2
        10          1          2

drop table test_merge_dest;
create table test_merge_dest
as
select rownum id
     , 1 col1
     , 2 col2
  from dual
connect by level <= 10;

update test_merge_dest
   set col2 = null 
 where id > 5;

select * from  test_merge_dest;

        ID       COL1       COL2
---------- ---------- ----------
         1          1          2
         2          1          2
         3          1          2
         4          1          2
         5          1          2
         6          1
         7          1
         8          1
         9          1
        10          1
        
merge into test_merge_dest dst
using (select * from test_merge_src) src
on (src.id = dst.id)
when matched then update 
 set dst.col1 = src.col1
   , dst.col2 = src.col2
where 1 in ( decode(dst.col1, src.col1, 0, 1)
           , decode(dst.col2, src.col2, 0, 1)
           );

5 Zeilen zusammengeführt.

Natürlich kann diese Liste einzelner DECODE-Elemente auch recht unhandlich werden, bleibt aber in jedem Fall kompakter als eine Verknüpfung zahlloser OR-Elemente.

Nachtrag 20.01.2015: über Twitter bin ich von einem ehemaligen Kollegen an die Funktion SYS_OP_MAP_NONNULL erinnert worden, die man in diesem Zusammenhang natürlich auch einsetzen kann - und die seit 12c auch offiziell dokumentiert ist.

Montag, Januar 05, 2015

Join Cardinality mit Like-Vergleichen

Noch ein wichtiger Hinweis aus dem Scratchpad von Jonathan Lewis: zwischen den Releases 11.1.0.7 und 11.2.0.4 hat sich die Arithmetik der Berechnung der Cardinality beim Join über Like-Operatoren verändert: in der älteren Version wurde mit dem Standard-Wert von 5% für Vergleiche des Typs "column >= unbekannterWert" gerechnet, aber in 11.2.0.4 ist ein Bug-Fix am Werk, dessen Beschreibung lautet: "use 1/NDV+1/NROWS for col1 LIKE col2 selectivities" - was zu deutlich niedrigeren Schätzungen führen kann. In erster Näherung würde ich solche Joins über Like vermutlich eher vermeiden, aber mit Sicherheit kann diese Änderung dramatische Auswirkungen auf die Zugriffspläne entsprechender Queries haben.

Adaptive Reoptimization

Vermutlich habe ich schon mal darauf hingewiesen, dass mir die diversen in 12c eingeführten Reoptimierungs-Optionen in ihrem Zusammenspiel recht komplex erscheinen - und dass ich fürchte, dass dabei Optimizer und analysierender Entwickler (also ich) unter Umständen den Überblick verlieren könnten. Immerhin hat sich jetzt Tim Hall die Mühe gemacht, einige Überblicksartikel zum Thema zu veröffentlichen - und da der Herr Hall in solchen Artikeln häufig prägnanter und pointierter erklärt als die Dokumentation, ist diese Einführung nützlich.

Jonathan Lewis hat mit Bezug auf Tim Halls Artikel noch eine Ergänzung geliefert, die eine Frage beantwortet, die ich mir (und Oracle-L) vor kurzem gestellt (und hier festgehalten) hatte: wo und in welcher Form werden die Informationen der dynamic statistics gespeichert und wie kann man sie überprüfen - zumindest unter bestimmten Umständen werden die Ergebnisse des Samplings als Hints in v$sql_reoptimization_hints gespeichert und müssen somit bei der wiederholten Ausführung nicht neu erzeugt werden - im Plan wird diese Wiederverwendung als statistics feedback angezeigt.