Samstag, März 28, 2015

MV-Refresh mit out_of_place Parameter in 12c

Und wieder verweise ich auf Jonathan Lewis, der diesmal eine interessante neue (12c) Refresh-Option für Materialized Views vorstellt, die durch den Parameter out_of_place aufgerufen wird. Die Idee dabei ist sehr einfach: im Rahmen des Refreshs wird eine zusätzliche Hilfstabelle erzeugt und gefüllt, die dann am Ende der Operation gegen das bisher zur MV gehörende Segment ausgetauscht wird (über eine interne Folge von Rename-Operationen). Das Verfahren entspricht grundsätzlich einem Workaround, den der Herr Lewis in einem älteren Artikel vorgestellt hatte, in dem er die MV als Partitionierte Tabelle mit einer einzigen Partition anlegte und den Refresh als Partition Exchange Operation ausführte. Mir gefällt dieser Workaround tatsächlich immer noch besser als die neue out-of-place Refresh-Variante, was vor allem an folgenden Einschränkungen der Option liegt:
  • die Insert-Operation wird als konventionelle durchgeführt, also ohne Append-Hint.
  • es ist nicht möglich die Option einzusetzen, um eine MV zu aktualisieren, die als Subset einer einzelnen Basistabelle definiert ist (also Spalten oder Zeilen ausschließt).
Für beide Beschränkungen fehlt mir zunächst eine einleuchtende Erklärung - aber vielleicht gibt es auch keine und die Einschränkungen werden in folgenden Releases aufgehoben.

Freitag, März 27, 2015

Frequenz des ASH/AWR Samplings

Noch einmal Jonathan Lewis, diesmal mit einer Erläuterung des ASH- und AWR-Samplings von Session-Informationen:
  • einmal in jeder Sekunde werden die Informationen zu aktiven Sessions (state = 'ACTIVE') aus v$session nach v$active_session_history kopiert.
  • diese Snapshots werden als Sample bezeichnet.
  • ein Zehntel dieser Datensätze wird in der entsprechenden AWR-Tabelle dba_hist_active_sess_history persistiert.
  • Es handelt sich aber nicht um jeden zehnten Datensatz, sondern um die Datensätze der zehnten Sekunde. Im Ergebnis sieht die enthält die AWR-Tabelle also einen konsistenten Zustand von v$session auf Basis eines Samplings im Abstand von zehn Sekunden. Daher können hier für einzelne Sessions auch größere Lücken als zehn Sekunden auftreten (wenn die Session zum entsprechenden Zeitpunkt inaktiv war).
  • der Abstand von zehn Sekunden ist zeitlich nicht immer völlig akkurat und verschiebt sich allmählich.
  • in der ASH-View werden die in die AWR-Tabelle kopierten Sätze mit dem Flag is_awr_sample gekennzeichnet.
  • die Kontrolle der Sampling-Frequenz und -Zeitabstände erfolgt über die Underscore-Parameter _ash_sampling_interval (Defalt: 1000 ms) und _ash_disk_filter_ratio (Default: 10). Eine Anpassung dürfte aber nur in Ausnahmefällen sinnvoll sein.

ANSI Join Syntax und das 1000-Spalten-Limit

Mein Titel klingt mal wieder wie der Name einer eher lieblos fortgeschriebenen Krimi-Serie, aber wahrscheinlich gelingt es mir eher, den Eintrag über eine solche Überschrift wiederzufinden, als anhand des Titels ANSI expansion, den Jonathan Lewis seinem Artikel gegeben hat, den ich hier abkürzend nacherzähle. Worum es geht ist Folgendes: im OTN-Forum wurde ein gut beschriebener Testfall vorgelegt, in dem ein ANSI-Join (mit recht breiten Views) in 12c (aber nicht in 11g) einen Fehler "ORA-01792: maximum number of columns in a table or view is 1000" hervorruft - was insofern verwundert, als in der Select-Liste der Query nur auf die Spalten einer deutlich schmaleren Tabelle zugegriffen wird. Ersetzt man das "Select *" durch eine explizite Liste der zugehörigen Spalten, so tritt das Problem nicht auf, und auch die Umformung des ANSI-Joins in einen traditionellen Join (*) führt zu einer problemlosen Verarbeitung. Im Artikel erklärt der Herr Lewis die Hintergründe des Verhaltens, wobei er die neue Routine dbms_utility.expand_sql_text verwendet, um die in der Query verwendeten Views auf die zugrunde liegenden Objekte zurückzuführen. Dabei wird deutlich, dass das 1000-Spalten-Limit im Select-*-Fall beim Aufbau zwischengeschalteter Sub-Queries überschritten wird, die sämtliche Spalten der verwendeten Views enthalten, obwohl im Test auf oberster Ebene nur zwei Spalten relevant sind. Bei einer expliziten Adressierung der relevanten Spalten tritt der Effekt nicht auf. Dieses Problem hat dabei anscheinend keine Auswirkungen auf die internen Transformationen der Query: im CBO-Trace erscheint für alle Fälle ein simpler Join in klassischem Stil. Angesichts der Häufigkeit, mit der ANSI-Probleme im OTN-Forum erscheinen, komme ich allmählich zum Schluss, die traditionelle Join-Syntax wieder zu präferieren - obwohl die neue ANSI-Syntax manche Query deutlich lesbarer macht.

(*) Ich gelobe, mir in Zukunft die Hinweise darauf zu sparen, dass letztlich beide Join-Syntax-Varianten in (unterschiedlichen) ANSI-Standards enthalten sind.

Mittwoch, März 18, 2015

Function Based Index mit Trunc-Funktion für Date-Spalten

Franck Pachot erwähnt eine interessante Verbesserung in der Behandlung von Indizes in jüngeren Oracle-Versionen: seit 11.2.0.2 können funktionsbasierte Indizes, die die Trunc-Funktion für ein Datum beinhalten, auch verwendet werden, um eine Query zu unterstützen, in der auf das Datum ohne die Funktion eingeschränkt wird (also z.B. über einen Datums-Range). Das ist in der Beschreibung einmal mehr unhandlich, sollte aber durch ein kleines Beispiel leicht zu verdeutlichen sein:

drop table t;

create table t
as
select sysdate + interval '3' hour ts
  from dual
connect by level <= 1000;

create index t_idx_ts_trunc on t(trunc(ts));

explain plan for
select *
  from t
 where ts < sysdate - 10;

------------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                |     1 |     8 |     1   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T              |     1 |     8 |     1   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | T_IDX_TS_TRUNC |     9 |       |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("TS"<SYSDATE@!-10)
   2 - access(TRUNC(INTERNAL_FUNCTION("TS"))<=TRUNC(SYSDATE@!-10))

In älteren Oracle-Releases wäre der Zugriff in dieser Query nicht über den FBI möglich gewesen, da die Einschränkung für die Spalte TS nicht der Index-Definition mit der Trunc-Funktion entspricht. Da der Index die Uhrzeitangaben des Zeitstempels nicht enthält, ist hier allerdings noch ein table access notwendig, den man sich sparen könnte, wenn man den Index noch um die TS-Spalte erweitert, also: 
create index t_idx_ts on t(trunc(ts), ts);
Im gegebenen Beispiel ist das natürlich recht zweckfrei (weil der Index dadurch größer wird als die Tabelle), aber Franck Pachots Artikel liefert da Plausibleres - und auch umfassendere Erklärungen zum Verhalten.

Donnerstag, März 12, 2015

Hybrid Hash Distribution für Parallele Operationen in 12c

Randolf Geist hat in jüngerer Vergangenheit einige Artikel zu den in 12c eingeführten Features mit Bezug zur Parallel Execution geschrieben. Darunter findet man:
  • 12c Parallel Execution New Features: Hybrid Hash Distribution - Part 1: die Adaptive Broadcast Distribution erlaubt für kleine "Left row sources" die Auswahl zwischen Broadcast- und Hash- (bzw. Round-Robin- und Hash-) Distribution zum Zeitpunkt der Ausführung abhängig von den Datenmengen durchzuführen - es handelt sich also um einen Korrektur-Mechanismus, der fehlerhafte Mengenabschätzungen behandelt. Anders als etwa die in 12c eingeführten adaptive joins, wird das Verfahren bei jeder folgenden Ausführung den Datenvolumina angepasst (also nicht bei der zweiten Ausführung festgeschrieben). Zum allgemeinen Verhalten und zu seinen Problemen gibt es ausführliche Beispiele. Interessant sind auch die Kommentare zum Artikel, in denen sich Yasin Baskan, der Produkt Manager für Parallel Execution bei Oracle, zum Thema äußert.
  • 12c Parallel Execution New Features: Hybrid Hash Distribution - Part 2: die Hybrid Distribution wurde eingeführt, um Join-Bedingungen mit starker Ungleichverteilung zu optimieren (was der Autor in älteren Artikeln ausführlich erläutert hat). Allerdings erfordert das Verfahren eine wiederholte Redistribution, selbst dann, wenn ein folgender Join mit den gleichen Join-Bedingungen operiert. Das bedeutet einerseits einen zusätzlichen Overhead und führt darüber hinaus unter bestimmten Umständen dazu, dass Operationen als BUFFERED durchgeführt werden (und somit blockieren), die ohne die Optimierung nicht blockiert hätten. Auch dazu gibt es natürlich wieder aussagekräftige Beispiele.
Einmal mehr ist der Sinn dieser Auflistung in erster Linie die Verlinkung der Artikel - die Details muss ich ohnehin immer wieder nachlesen, wenn ich entsprechenden Phänomenen begegne.