Freitag, April 17, 2015

Table Functions (Steven Feuerstein)

Wenn ich an PL/SQL denke, ist meine erste Assoziation dazu der Name Steven Feuerstein. Nun denke ich nicht furchtbar oft on PL/SQL, aber wenn der Herr Feuerstein über ein Thema schreibt, dem ich mit einer gewissen Regelmäßigkeit begegne, dann ist das allemal eine Verlinkung und Zusammenfassung der zugehörigen Artikel wert:
  • Table Functions: Introduction and Exploration, Part 1: skizziert die vorgesehene Struktur der Artikelserie und erläutert zunächst das Grundkonzept von table functions: der table operator wandelt eine collection in ein relationales Dataset um, so dass man darauf via Select zugreifen kann. Diese Datasets können als Grundlage in (parametrisierten) Views verwendet werden und mit anderen Tabellen/Datasets gejoint werden, man kann die Ergebnisse gruppieren, sortieren etc. In älteren Releases konnten nur nested tables und varrays vom table operator transformiert werden, aber seit 12.1 bieten auch integer-indexed associative arrays eine verwendbare Grundlage. Der Herr Feuerstein erwähnt die einschlägigen Artikel zum Thema von Adrian Billington und Tim Hall, auf die ich hier auch schon häufiger verwiesen habe. Es folgt eine Liste möglicher use cases (inklusive diverser Beispiele dazu):
    • Session-spezifische Daten mit Tabellendaten mergen, um die Vorteile Set-orientierter SQL-Operationen zu erhalten.
    • Programmatische Erzeugung spezieller Datasets für die Ausgabe.
    • Parametrisierte Views.
    • Parallelisierte Selects auf der Basis von pipelined table functions.
    • Verringerte PGA-Nutzung durch pipelined table functions, da die zugehörigen Collections nicht komplett im Speicher gehalten werden müssen, sondern sukzessive aufgebaut und weiterverarbeitet werden können.
  • Table Functions: Returning complex (non-scalar) datasets, part 2: geht über die im ersten Artikel vorgestellten Beispiel mit (skalaren) Einzelwerten hinaus und zeigt den Umgang mit rows, die mehrere unterschiedliche Attribute enthalten. Zunächst zeigt er dabei, wie man es besser nicht machen sollte (z.B. eine Table mit VARCHAR2(4000), deren Ergebnisse über String-Operationen zerlegt werden). Leider steht die plausiblere Lösung, einen PL/SQL record type als Datentyp einer nested table zu verwenden, nicht zur Verfügung, weil SQL an dieser Stelle etwas kleinlich ist. Stattdessen muss man einen object type verwenden, was syntaktisch kaum einen Unterschied macht.
Die folgenden Artikel versuche ich hier zu ergänzen, sobald sie folgen.

Donnerstag, April 09, 2015

Truncate Table Cascade

Eine nützliche Ergänzung in 12c, die mir bisher entgangen war, ist das TRUNCATE CASCADE, mit dessen Hilfe man eine Parent-Tabelle entleeren kann und zugleich alle Datensätze in via FK verbundenen Tabellen löscht. Das funktionierte bislang nur mit DELETE - und auf DELETE verzichte ich gerne, wo immer das möglich ist. Dazu ein kleines Beispiel:

drop table child;
drop table parent;

create table parent(
    parent_id number primary key
);

insert into parent(parent_id)
select rownum 
  from dual
connect by level <= 10000;  

create table child(
    id number primary key
  , parent_id number
  , constraint fk_parent_id foreign key(parent_id) references parent(parent_id) on delete cascade
);

insert into child(id, parent_id)
select rownum 
     , rownum
  from dual
connect by level <= 10000;  

insert into child(id, parent_id)
values(-1, null);

create index child_fk_idx on child(parent_id);

Ausgehend von diesem Versuchsaufbau lassen sich die Unterschiede in Performance und Verhalten leicht überprüfen:

delete from parent cascade;

--> 10000 Zeilen gelöscht.
--> Abgelaufen: 00:00:01.54

select * from child where id < 0;

        ID  PARENT_ID
---------- ----------
        -1

rollback;

truncate table parent cascade;

--> Tabelle mit TRUNCATE geleert
--> Abgelaufen: 00:00:00.07

select * from child where id < 0;

Es wurden keine Zeilen ausgewählt

Ursprünglich wollte ich hier noch diverse v$sesstat-Angaben unterbringen, aber das unterschiedliche Verhalten hinsichtlich des verwaisten Satzes mit id -1 in der child-Tabelle macht ziemlich deutlich, was hier passiert:
  • das DELETE CASCADE löscht alle Sätze aus parent und alle via FK verknüpften Sätze aus child. Das dauert für 10000 Datensätze auf meiner Workstation bereits 1,5 sec, lässt aber den Orphan-Datensatz aus child (id = -1) stehen.
  • das TRUNCATE CASCADE leert parent und child komplett, was deutlich schneller durchgeführt werden kann als das DELETE, aber den Orphan-Satz nicht stehen lässt. Es handelt sich also um ein TRUNCATE auf beiden Ebenen - nicht um ein TRUNCATE für parent und ein DELETE aller Sätze ohne FK-Beziehung in der abhängigen Tabelle.
Wenn man die Dokumentation liest, ehe man das Verhalten testet, ist das Ergebnis nicht besonders erstaunlich:
If you specify CASCADE, then Oracle Database truncates all child tables that reference table with an enabled ON DELETE CASCADE referential constraint. This is a recursive operation that will truncate all child tables, granchild tables, and so on, using the specified options.
Ich kann mir durchaus Fälle vorstellen, in den dieses Verhalten genau das ist, was man haben möchte - aber auch solche, in denen es nicht den Erwartungen der Anwender entspricht. Auf jeden Fall sollte man sich einprägen, dass DELETE CASCADE und TRUNCATE CASCADE unterschiedliche Ergebnisse hervorrufen.

Mittwoch, April 08, 2015

Concurrent UNION ALL (Randolf Geist)

Wie bereits gelegentlich erwähnt hat sich im Release 12c im Bereich der Parallel Execution allerlei getan - und Randolf Geist hat sich umfassend zu den meisten neuen Features geäußert. Eine wichtige Neuerung ist das Concurrent UNION ALL, also die Möglichkeit, mehrere über UNION ALL verbundene Teil-Operationen parallel zu verarbeiten. Wie üblich sind die Ausführungen des Herrn Geist ausgesprochen detailliert - und wie üblich werde ich ihnen in meiner Zusammenfassung nicht gerecht. Daher seien die folgenden Punkte wieder einmal nur als Link-Sammlung zu verstehen, auf die gelegentlich zurückzukommen wäre:
  • 12c Parallel Execution New Features: Concurrent UNION ALL - Part 1: vor 12c führte Oracle niemals mehrere getrennte Branches (also etwa über UNION ALL verknüpfte Blöcke) eines Execution Plans gleichzeitig aus: die Parallelisierung betraf immer nur Einzeloperationen. Mit 12c hat sich das geändert. Der Artikel nennt die zugehörigen kontrollierenden Hints (PQ_CONCURRENT_UNION, NO_PQ_CONCURRENT_UNION) und erläutert einen Fall, in dem das Feature sehr nützlich sein könnte, nämlich die parallele Verarbeitungen von Operationen, bei denen via DB-Link auf Remote-Quellen zugegriffen wird (was vor 12c eine Do-it-yourself-Parallelisierung erforderte). Der Artikel untersucht weiterhin den Parallelisierungsgrad der zugehörigen Operationen und ergänzt umfangreiche Tests. Im Plan sind parallele UNION ALL Elemente über einen PX SELECTOR Eintrag dargestellt.
  • 12c Parallel Execution New Features: Concurrent UNION ALL - Part 2: zeigt, dass das Feature automatisch aktiv wird, wenn man parallele und serielle Branches innerhalb eines UNION ALL mischt (dann erscheint für die seriellen Branches der PX SELECTOR und für die parallelen z.B. ein PX BLOCK ITERATOR). Es folgt eine umfassende Analyse des Laufzeitverhaltens (die ich hier nicht wiedergebe).
  • 12c Parallel Execution New Features: Concurrent UNION ALL - Part 3: erläutert die in Teil 1 skizzierte Optimierung der parallelen Verarbeitung verteilter Operationen genauer. Auch dazu gibt es wieder einen umfassenden Versuchsaufbau und auch dazu schreibe ich an dieser Stelle nicht mehr.
Wie gesagt: der Eintrag dient eher als Link-Sammlung, denn der Kommentierung. Um sinnvollere Kommentare zu ergänzen, müsste ich die Details besser verstanden haben - aber aktuell ist das Thema für mich ausschließlich von akademischem Interesse.

Dienstag, April 07, 2015

Exadata und In-Memory

Obwohl ich bisher weder mit Exadata noch mit der IN-Memory-Option ernsthaft gearbeitet habe, halte ich beide Möglichkeiten für hochinteressant - und das gilt folglich auch für die von Maria Colgan gesammelten 10 Reasons to run Database In-Memory on Exadata. Die könnte ich jetzt nacherzählen, spare mir das aber, weil mir dazu insbesondere auf Exadata-Seite das Sachwissen zur Ergänzung erhellender Kommentare fehlt. Daher bleibt hier nur der Faden liegen, dem ich möglicherweise eines Tages nachgehen könnte.

Donnerstag, April 02, 2015

Postgres-Performance seit 7.4

Da ich mir ziemlich sicher bin, dass ich diese Beiträge irgendwann wieder suchen werden, hinterlege ich an dieser Stelle die Links auf Thomas Vondras interessante Artikel zur Entwicklung der Performance von postgres seit Version 7.4:
  • Performance since PostgreSQL 7.4 / pgbench: mit dem schönen Fazit "The newer the PostgreSQL release, the better the performance. Yay!" Darüber hinaus gibt es noch folgende Einsichten:
    • "some releases make a huge improvement"
    • "Older releases perform much better with more conservative settings"
    • "Newer hardware matters"
  • Performance since PostgreSQL 7.4 / TPC-DS: behandelt das Verhalten mit analytischen (soll heißen: großen) Datenvolumina und liefert folgende Einsichten:
    • "Loading is much faster than on 8.0 - about 2x as fast. Most of the speedup happened in 8.1 / 8.2, and we're keeping about the same performance since then."
    • "The query speedup is even better - PostgreSQL 9.4 is about 7x faster than 8.0. Again, most of the speedup happened in 8.1/8.2, but there are signifincant improvements in the following releases too."
  • Performance since PostgreSQL 7.4 / fulltext: weist insbesondere auf die Beschleunigung von GIN zwischen 9.3 und 9.4 hin. Die Details spare ich aus (bis zu dem Tag, an dem ich mich endlich mal mit GIN und GIST beschäftige).
Alle Artikel beinhalten dabei sehr umfangreiche und gut dokumentierte Tests mit ansprechender Visualisierung der Ergebnisse.