Materialized Views für Data-Warehouses Art der Info

Werbung
Betrifft
DWH1: Materialized Views für Data-Warehouses
Art der Info Technische Info, Oracle8i
Quelle
Aus dem AI8-EF Kurs der Trivadis (Enterprise Features)
Autor
Andri Kisseleff ([email protected])
Schlüsselworte
Datawarehouses, Materialized Views, Materialized View Logs, Query Rewrite
Ziel dieses Artikels
Mit Oracle8i wurden diverse neue Möglichkeiten im Warehousing-Bereich eingeführt. In
diesem ersten Artikel der Warehousing-Artikel-Serie werden Materialized Views und Query
Rewrite erläutert, welche typischerweise in Data-Warehouses von grossem Nutzen sein
können.
Der einfachen Verständlichkeit halber werden die neuen Features anhand von Beispielen mit
EMP/DEPT (oder BIG_EMP/DEPT) erläutert.
Im zweiten Artikel dieser Serie werden weitere Möglichkeiten im Zusammenhang mit
Materialized Views unter Verwendung von normalisierten und denormalisierten Dimensionen
und Hierarchien geschildert. Im dritten Artikel werden diverse Neuerungen wie ROLLUP,
CUBE, TOP-n und SAMPLE erläutert.
Weitere für Warehouses relevante Themen werden in weiteren Artikeln abgehandelt werden.
Materialized Views: das Konzept
Oracle8i erlaubt die Ergebnismenge einer View abzuspeichern, das heisst zu materialisieren.
Diese abgespeicherten Redundanzen (auch Summary-Tables genannt) können von Oracle
automatisch (COMPLETE, FAST oder ON COMMIT) oder on-demand nachgeführt
werden. Der Optimizer ist in der Lage SQL-Befehle, welche ein bereits vorberechnetes und
redundant abgespeichertes Resultat verwenden können zu identifizieren und umzuschreiben.
Das Resultat einer Abfrage wird somit nicht aus den Basis-Tabellen berechnet, sondern aus
der Summary-Table gelesen. Dieser Vorgang wird QUERY REWRITE genannt und ist für
die Applikation transparent, das heisst die SQL-Befehle müssen nicht angepasst oder mit
Hints versehen werden.
Solche MVIEWs können indexiert werden und können auch partitioniert abgespeichert
werden.
Privilegien und Voraussetzungen
Für das Erstellen von MVIEWs ist das CREATE MATERIALIZED VIEW oder das
CREATE ANY MATERIALIZED VIEW System-Privileg notwendig. Um Query-Rewrite zu
ermöglichen ist das QUERY REWRITE oder das GLOBAL QUERY REWRITE SystemPrivileg notwendig. Der INIT.ORA-Parameter QUERY_REWRITE_ENABLED muss auf
TRUE gesetzt sein oder auf Session-Ebene aktiviert werden.
GRANT CREATE MATERIALIZED VIEW TO olap;
GRANT QUERY REWRITE TO olap;
QUERY_REWRITE_ENABLED=true/false
ALTER SESSION SET QUERY_REWRITE_ENABLED = TRUE;
Will man ein automatisches Refresh der MVIEWs (inkrementell (FAST) oder komplett
(COMPLETE)) verwenden, so ist mindestens ein Job-Queue-Prozess zu konfigurieren.
JOB_QUEUE_INTERVAL
JOB_QUEUE_PROCESSES
= n
= n
# Wakeup-Interval in Sek.
# Anzahl Prozesse
Damit MVIEWs verwendet werden können muss mit dem Cost-based Optimizer gearbeitet
werden und die Tabellen, sowie die MVIEWs müssen über Optimizer-Statistiken verfügen.
Materialized Views: die Möglichkeiten
MVIEWs können bei Oracle8i 8.1.5 für Query Rewrite für Aggregationen, für Joins und für
die Kombination von beidem verwendet werden.
Beispiel Aggregation:
CREATE MATERIALIZED VIEW emp_summary
ENABLE QUERY REWRITE
AS SELECT deptno, job, SUM(sal)
FROM
emp
GROUP BY deptno, job;
Beispiel Join:
CREATE MATERIALIZED VIEW emp_dept
ENABLE QUERY REWRITE
AS SELECT e.*, dname, loc
FROM
emp e, dept d
WHERE e.deptno = d.deptno;
Beispiel Aggregation und Join:
CREATE MATERIALIZED VIEW emp_dept_summary
ENABLE QUERY REWRITE
AS SELECT dname, job, SUM(sal) AS sum_sal
FROM
emp e, dept d
WHERE e.deptno = d.deptno
GROUP BY dname, job;
In diesen Beispielen wurden die MVIEWs direkt bei der Erstellung für QUERY REWRITE
freigegeben. Es ist auch möglich, diese erst nachträglich mit ENABLE freizugeben,
respektive mit DISABLE das Query Rewrite zu unterbinden:
ALTER MATERIALIZED VIEW emp_dept_summary
ENABLE QUERY REWRITE;
QUERY REWRITE ist nicht unterstützt für Materialized Views, welche eine einfache
Selektion (ohne Aggregation) auf eine einzelne Tabelle (ohne Join) darstellen. Folgende
MVIEW wird für Query Rewrite nicht verwendet:
CREATE MATERIALIZED VIEW emp10
ENABLE QUERY REWRITE
AS SELECT empno, ename, job, sal, comm
FROM
emp
WHERE deptno = 10;
Bei der Erstellung von Materialized Views ist es ebenfalls möglich Tablespace, Storage
Parameter, etc. zu spezifizieren.
Ebenfalls unterstützt ist das erstellen der Tabelle (z.B. Parallel, SQL*Loader Direct Path, etc.)
und das nachfolgende Definieren der MVIEW auf die bereits existierende Tabelle:
CREATE MATERIALIZED VIEW emp_summary
ON PREBUILT TABLE
REFRESH FAST
ENABLE QUERY REWRITE
AS SELECT deptno, job, sum(sal) AS sum_sal,
COUNT(sal) count_sal, COUNT(*) count_all
FROM
emp
GROUP BY deptno, job
Materialized Views: Query-Rewrite
Sind alle Voraussetzungen erfüllt (siehe Integritätsfragen und Restriktionen weiter unten), so
kann so eine MVIEW nun vom Oracle-Optimizer für Query Rewrite verwendet werden.
Führt man z.B. den folgenden Befehl aus, so wird wie im Execution Plan erkennbar nicht die
EMP- und die DEPT-Tabelle für das Join und die Aggregation gelesen, da das Resultat ja in
der MVIEW EMP_DEPT_SUMMARY vorberechnet abgespeichert ist.
SELECT dname, job, SUM(sal) AS sum_sal
FROM
emp e, dept d
WHERE e.deptno = d.deptno
GROUP BY dname, job;
Execution Plan
---------------------------------------------------------0
SELECT STATEMENT Optimizer=CHOOSE
1
0
TABLE ACCESS (FULL) OF 'EMP_DEPT_SUMMARY'
Dass hier bei grossen Datenmengen ein erheblicher Performancevorteil vorliegt ist
selbstsprechend. Interessant ist aber ebenfalls, dass die Funktionalität (die Verwendbarkeit)
von MVIEWs auch mit kleinsten Datenmengen getestet werden kann.
Materialized Views: Refreshes
Falls auf den Basis-Tabellen, in unserem Beispiel EMP und DEPT, Transaktionen ausgeführt
werden, so ergibt sich die Notwendigkeit die redundant in den MVIEWs abgespeicherten
Resultate mit- oder nachzuberechnen.
Diese Refreshes können automatisch (mit internen Triggern und/oder Job-Queue Prozessen),
oder manuell (on-demand) nachgeführt werden.
Für das Refresh "ON COMMIT" ist weiter unten ein Kapitel reserviert.
Complete Refresh "on-demand"
Für das Refresh on-demand steht ein Package DBMS_MVIEW zur Verfügung. Dieses kann
komplette oder inkrementelle Refreshes durchführen. Bei einem COMPLETE Refresh wird
der Inhalt der MVIEW mit per Default mit DELETE gelöscht und die MVIEW vollständig
neu berechnet (INSERT INTO SELECT FROM). Dieses Vorgehen ist dann angebracht, wenn
die Datenmengen nicht allzu gross sind und die Refreshes nicht allzu häufig nachgeführt
werden müssen. Für diese Art von Refresh sind keine LOG-Tabellen auf den Basis-Tabellen
notwendig.
EXECUTE DBMS_MVIEW.REFRESH('emp_dept_summary');
(Wer "Snapshots" von Oracle7 und Oracle8 kennt: DBMS_MVIEW ist ein PUBLIC
SYNONYM für DBMS_SNAPSHOT! "Snapshots" im Sinne von "Snapshots für
Replikation", wie wir diese von früher her kennen, heissen übrigens neu ebenfalls
"Materialized Views".)
Fast Refresh "on-demand"
Bei FAST-Refreshes, werden nur die Änderungen ( INSERT, UPDATE, DELETE), welche
auf der oder den Basistabellen ausgeführt werden inkrementell auf die MVIEW appliziert,
diese also nicht komplett neu aufgebaut. Damit Oracle in der Lage ist nur die Änderungen zu
applizieren muss auf den betroffenen Basis-Tabellen je ein Materialized View LOG erstellt
werden. Dieses Log (eine Tabelle), wird von Oracle durch jede Transaktion auf der BasisTabelle durch interne Trigger automatisch nachgeführt.
Ein Beispiel der Erstellung eines solchen MVIEW LOGs für inkrementelle FAST-Refreshes
ist:
CREATE MATERIALIZED VIEW LOG ON emp
WITH ROWID (deptno,job,sal) INCLUDING NEW VALUES;
Für welche Art von MVIEW welche Art von MVIEW-Log supported ist (Primary Key,
ROWID, INCLUDING NEW VALUES, etc.) ist in der Tabelle 29-1 des Tuning Guide
beschrieben.
Für intelligente FAST-Refreshes z.B. bei aggregierenden Funktionen, sollte Oracle die
Möglichkeit haben für die Neuberechnung der Aggregate nur die DELTAs zu berücksichtigen
und nicht immer bei jedem Refresh die ganze Aggregation für die ganze Basis-Tabelle(n) neu
durchführen zu müssen. Hierzu sind je nach Funktion, welche verwendet wird (AVG, MAX,
MIN, SUM, etc.) Zusatzattribute in der MVIEW-Definition zwingend notwendig. Die Tabelle
29-2 im Tuning Guide zeigt die notwendigen Zusatzattribute in den MVIEWs für FAST
REFRESHES.
Ein Beispiel wo ein FAST-Refresh on-demand mit einem entsprechenden MVIEW-LOG
(siehe oben) funktioniert ist:
CREATE MATERIALIZED VIEW emp_summary
REFRESH FAST /* on-demand, but fast */
ENABLE QUERY REWRITE
AS SELECT deptno, job, sum(sal) as sum_sal,
COUNT(sal) count_sal, COUNT(*) count_all
FROM
emp
GROUP BY deptno, job;
Wie auch bei den Materialized Views, können selbstverständlich auch hier TABLESPACE,
STORAGE-Clause, etc. spezifiziert werden.
Das individuelle Refresh für diese MVIEW würde dann mit DBMS_MVIEW.REFRESH
durchgeführt (F=FAST, C=COMPLETE, ?=was möglich ist, je nach dem ob es ein LOG gibt
oder nicht):
SQL> EXECUTE DBMS_MVIEW.REFRESH('emp_summary','F');
PL/SQL procedure successfully completed.
SQL>
Ebenfalls besteht die Möglichkeit alle MVIEWs zu refreshen:
SQL> EXECUTE DBMS_MVIEW.REFRESH_ALL;
PL/SQL procedure successfully completed.
SQL>
Oder so:
SQL> DECLARE v_nbr_of_failures INTEGER;
2 BEGIN
3
DBMS_MVIEW.REFRESH_ALL_MVIEWS
4
(
5
NUMBER_OF_FAILURES
=> v_nbr_of_failures,
6
METHOD
=> '?',
7
ROLLBACK_SEG
=> 'RS01',
8
REFRESH_AFTER_ERRORS
=>
FALSE,
-only
TRUE
if
ATOMIC_REFRESH=FALSE
9
ATOMIC_REFRESH
=> TRUE
-- in one single TX for all MVIEWs
10
);
11
DBMS_OUTPUT.PUT_LINE('Errors: '||TO_CHAR(v_nbr_of_failures));
12 END;
13 /
Errors: 0
PL/SQL procedure successfully completed.
SQL>
Weitere Procedures für z.B. alle "dependent" MVIEWs, etc. bestehen ebenfalls in
DBMS_MVIEW.
Die in den MVIEW-Logs nicht mehr notwendigen Transaktions-Einträge können nach Bedarf
(wo notwendig) gepurged werden:
SQL> EXECUTE DBMS_MVIEW.PURGE_LOG('emp');
PL/SQL procedure successfully completed.
SQL>
etc., etc.
Automatisches Refresh
Anstelle der Möglichkeit das Refresh manuell gezielt mit DBMS_MVIEW durchzuführen,
besteht auch die Möglichkeit diese Aufgabe mit JOBs zu automatisieren. Beim Erstellen der
MVIEW werden der erstmalige REFRESH-Zeitpunkt und das REFRESH-Intervall
spezifiziert.
Folgendes Beispiel macht ein erstes Refresh heute Nacht um 22:00 und dann jeweils wieder
ein Refresh jede Nacht um 22:00.
CREATE MATERIALIZED VIEW emp_summary
ON PREBUILT TABLE
REFRESH FAST
START WITH to_date(to_char(sysdate,'dd-mon-yy')||' 22:00','DD-MON-YY HH24:MI')
NEXT trunc(sysdate+1)+1/24*22
ENABLE QUERY REWRITE
AS SELECT deptno, job, sum(sal) as sum_sal,
count(sal) count_sal, count(*) count_all
FROM
emp
GROUP BY deptno, job;
Es wird hierbei ein JOB mit DBMS_JOB submitted, welcher sich dann jeweils selber wieder
neu submitted:
Jobs of current Schema in Job-Queue
DB1.TTCNT01.TTC.TRIVADIS.COM
25.10.1999 13:55 - 02.11.1999 14:32 (192.6 h)
JOB LAST_DATE LAST_SEC THIS_SEC NEXT_DATE NEXT_SEC BROKEN FAIL
------ --------- -------- -------- --------- -------- ------ ---INTERVAL
-------------------------------------------------------------------------------WHAT
-------------------------------------------------------------------------------81
02-NOV-99 22:00:00 N
trunc(sysdate+1)+1/24*22
dbms_refresh.refresh('"OLAP"."EMP_SUMMARY"');
Refresh Fast on COMMIT
Bei Refresh Fast on Commit ändert eine Transaktion auf der Basis-Tabelle in der selben
Transaktion auch die MVIEW. Die Daten in der MVIEW sind somit immer up-to-date.
Diverse Einschränkungen sind zu berücksichtigen und einzuhalten:
ON COMMIT Refreshes sind nur bei Single Table Aggregationen oder join-only
MVIEWs erlaubt, nicht aber deren Kombination
Single Table Aggregationen bei Refresh ON COMMIT:
SUM, AVG, STDDEV und VAR brauchen neben COUNT(*) noch zwingend weitere
COUNT und SUM Ausdrücke in der SELECT Clause
Immer COUNT(*) und in den meisten Fällen COUNT(expr) (Tabelle 29-2 im Tuning
Guide)
Ein REFRESH ON COMMIT ohne "FAST" und/oder ohne MATERIALIZED VIEW
LOG ist auf jeden Fall zu vermeiden
Oracle vermeidet dies leider nicht automatisch, so kann man falsche (für die Performance
untaugliche) REFRESH ON COMMIT definieren
Das folgende Beispiel zeigt das falsche Aufsetzen von einer ON COMMIT MVIEW und
das Resultat aus V$SQLAREA, welches erklärt, weshalb man beim COMMIT auf der
BIG_EMP Tabelle einige Minuten warten muss...
CREATE MATERIALIZED VIEW big_emp_aggr
REFRESH ON COMMIT
ENABLE QUERY REWRITE
AS SELECT deptno,SUM(sal) SUM_SAL, COUNT(*) COUNT_ALL,
COUNT(SAL) COUNT_SAL
FROM big_emp
GROUP BY deptno
/
UPDATE BIG_EMP SET SAL=SAL+1 WHERE ROWNUM = 1;
SET TIMING ON
COMMIT; -- seeeeehr lange warten
In V$SQLAREA sieht man, wie das Refresh gemacht wird (!):
delete from "ORA8I"."BIG_EMP_AGGR";
INSERT INTO "ORA8I"."BIG_EMP_AGGR"("DEPTNO","SUM_SAL","COUNT_ALL","COUNT_SAL")
SELECT "BIG_EMP"."DEPTNO",SUM("BIG_EMP"."SAL"),COUNT(*),COUNT("BIG_EMP"."SAL") FROM
"BIG_EMP" "BIG_EMP" GROUP BY "BIG_EMP"."DEPTNO";
ALTER SUMMARY "ORA8I"."BIG_EMP_AGGR" COMPILE;
Für ein richtiges Refresh on COMMIT sind zusätzlich wesentlich:
Das MATERIALIZED VIEW LOG muss für MVIEWs welche mit ON COMMIT
nachgeführt werden sollen immer ein ROWID-MVIEW-Log sein
Wenn die MVIEW aggregierende Funktionen beinhaltet so müssen alle Attribute
(alte und neue Werte), welche in der MVIEW sind auch im MVIEW-Log
vorhanden sein
Das MVIEW-Log und die MVIEW müssen somit richtigerweise so aufgebaut werden:
CREATE MATERIALIZED VIEW LOG ON big_emp WITH ROWID (deptno,sal) INCLUDING NEW
VALUES;
CREATE MATERIALIZED VIEW big_emp_aggr
REFRESH FAST ON COMMIT
ENABLE QUERY REWRITE
AS SELECT deptno,SUM(sal) sum_sal, COUNT(*) count_all, COUNT(sal) count_sal
FROM big_emp
GROUP BY deptno;
Für eine ON COMMIT MVIEW mit einem Join ist zu berücksichtigen, dass die ROWIDs der
Basistabellen in der MVIEW ebenfalls spezifiziert werden müssen.
Das folgende Beispiel zeigt:
Die richtige Syntax für zwei MVIEW-Logs und eine MVIEW (FAST ON
COMMIT) für eine MVIEW mit Join
Hier wird die Tabelle vorkreiert und dann auf die bestehende Tabelle die MVIEW
generiert
Die Indexes auf den ROWID-Attributen in der MVIEW sind für eine gute
Performance beim COMMIT auf die Master-Tables von grösster Bedeutung
Das Beispiel von A-Z:
CREATE MATERIALIZED VIEW LOG ON big_emp
WITH ROWID;
CREATE MATERIALIZED VIEW LOG on dept
WITH ROWID;
REM Tabelle vorkreieren:
CREATE TABLE big_emp_dept STORAGE (INITIAL 10M NEXT 1M)
AS SELECT empno, ename, dname,
e.rowid big_emp_rowid, d.rowid dept_rowid
FROM big_emp e, dept d
WHERE e.deptno = d.deptno;
REM Indexes auf die ROWIDs:
CREATE UNIQUE INDEX big_emp_emp_rowid
ON big_emp_dept(big_emp_rowid)
STORAGE (INITIAL 10M NEXT 1M);
CREATE INDEX big_emp_dept_rowid
ON big_emp_dept(dept_rowid)
STORAGE (INITIAL 10M NEXT 1M);
REM weitere Indexes auf der MVIEW:
CREATE UNIQUE INDEX big_emp_emp_empno ON big_emp_dept(empno)
STORAGE (INITIAL 10M NEXT 1M);
REM MVIEW erstellen:
CREATE MATERIALIZED VIEW big_emp_dept
ON PREBUILT TABLE
REFRESH FAST ON COMMIT
ENABLE QUERY REWRITE
AS select empno, ename, dname,
e.rowid big_emp_rowid, d.rowid dept_rowid
FROM big_emp e, dept d
WHERE e.deptno = d.deptno;
Wird eine ON COMMIT zu refreshende MVIEW richtig aufgesetzt, so verfügt man bei
Transaktionen auf den Basis-Tabellen über eine hervorragende Performance.
Integrität der Materialized Views
Wird eine MVIEW mit REFRESH FAST ON COMMIT nachgeführt, so ist diese per
Definition zu jeder Zeit integer, das heisst die Daten sind konsistent mit den Daten in der oder
den Basis-Tabellen.
In Warehouses ist das ON COMMIT aber nicht die typische Verwendung, da vielleicht die
Daten des aktuellen Monates erst nach der Monatsendverarbeitung und –Bereinigung im
Warehouse sichtbar sein sollen.
Hier stellt sich nun die Frage, ob Oracle eine MVIEW für QUERY REWRITE verwendet,
falls die in der MVIEW abgespeicherten Daten nicht mit den Basis-Daten übereinstimmen.
Dies kann mit einem INIT.ORA-Prameter, respektive auch auf Session-Ebene (mit ALTER
SESSION) beeinflusst werden:
QUERY_REWRITE_INTEGRITY=ENFORCED/TRUSTED/STALE_TOLERATED
ENFORCED (Default):
Nur MVIEWs mit aktuellen Daten (up-to-date) und Beziehungen, welche auf Enforced
Constraints beruhen, werden für Query Rewrite berücksichtigt.
TRUSTED:
Die Daten in MVIEWs müssen up-to-date sein, getraut wird aber den Dimensionen und
den Constraints die mit NOVALIDATE aktiviert wurden und mit RELY als "trusted"
markiert wurden. (ALTER CONSTRAINT xxx RELY;)
STALE_TOLERATED:
Alle vorhanden MVIEWs, Dimensionen und Constraints werden für QUERY
REWRITE wenn möglich genutzt, auch wenn die Daten nicht up-to-date sind.
Restriktionen für MVIEWs
Wir haben mit all diesen Beispielen gesehen, dass Materialized Views eine sehr mächtige
Feature darstellen. Die Technik ist nicht ganz einfach zu erlernen, aber hat man diese einmal
im Griff, so werden Materialized Views für sehr viele Applikationen verwendet werden
können.
Dass eine MVIEW mit Oracle8i 8.1.5 für Query Rewrite verwendet werden kann sind noch
folgende Restriktionen zu berücksichtigen:
Nicht-deterministische Ausdrücke in der Query (SYSDATE, etc.) sind nicht erlaubt
Referenzen auf RAW Datentypen oder Objekt REFs sind nicht zugelassen
Nur Single Block Queries sind möglich, keine Mengenfunktionen wie UNION, etc.
Falls die Materialized View auf einer existierenden Tabelle erstellt wird, muss die
Spaltenpräzision gleich oder kleiner als bei der Originaltabelle sein
Nur lokale Tabellen können als Basistabellen für Materialized Views verwendet
werden
Weder Basistabellen noch MVIEW dürfen dem User SYS gehören
Die FROM Zeile einer Materialized View Definition darf eine Tabelle nur einmal
enthalten
GROUP BY Listen müssen in der Query und in der Materialized View Definition
identisch sein (Ausnahme: Optimierung mit Dimensionen, siehe Artikel 2 dieser
DWH-Serie)
Es sind keine Ausdrücke in den Spalten (z.B. ||) einer Materialized View Definition
erlaubt
Aggregationsfunktionen sind nur auf äusserster Ausdrucksebene, also z.B. AVG(a+b),
nicht aber AVG(a) + AVG(b) erlaubt
Die WHERE Clause einer Materialized View Definition darf nur AND verbundene
inner und outer Joins enthalten
HAVING und CONNECT BY sind nicht erlaubt
Zusammenfassung
In diesem Artikel haben wir aufgezeichnet, dass Oracle8i mit der Einführung der Materialized
Views mächtige neue Möglichkeiten in Warehouses und anderen Applikationen unterstützt.
Zu hoffen ist, dass einige der doch recht einschränkenden Restriktionen in den nächsten
Releases eliminiert werden.
Oracle8i: OLAP – Fazit
Oracle8i: Die erste Datenbank fürs Internet Computing.....
Oracle8i: Die erste Datenbank fürs Warehousing!
Literatur
Oracle8i Tuning: Part No. A67775-01, Kapitel 29-31
Oracle8i SQL Reference: Part No. A67779-01
Oracle8i Application Developer's Guide – Fundamentals: Part No. A68003-01
Trivadis AG
Andri Kisseleff
Rebhaldenweg 1
CH-5507 Mellingen
Tel: +41 (0)56 470 61 31
Fax: +41 (0)56 470 61 32
Herunterladen