Exadata & In Memory: Real World Performance (Teil 1)
Sowohl die Exadata-Plattform mittels der sogenannten „Smart Scan“- Technologie als auch die mit Datenbank Version 12.1.0.2 eingeführte „In Memory Column Store“- Technologie können Full Table Scans extrem beschleunigen.
Mit diesen neuen Technologien geht einher, dass Oracle diese auch mit dem Argument anpreist, weniger Zeit mit SQL Tuning und Design verbringen zu müssen, da diese neuen Features so schnelle Ausführungszeiten ermöglichen.
Wie ein realer Fall bei einem meiner Kunden aber zeigte, ist es auch mit diesen neuen Technologien möglich, in Situationen zu geraten, bei denen trotz anscheinend optimaler Voraussetzungen – im konkreten Fall für die Exadata „Smart Scans“ – die Ausführungszeiten nicht den möglichen Erwartungen entspricht und schließlich nur mittels entsprechender Änderungen, entweder bei der Modellierung oder beim physischen Design die Erwartungen erfüllt werden können.
Ein kurzer Überblick der Exadata-Technologie
Die Exadata Plattform liefert extreme Performance, wenn die Exadata-spezifischen Features entsprechend eingesetzt werden können. Insbesondere die sogenannten Exadata „Smart Scans“ können Full Segment Scans extrem beschleunigen und optimieren.
Der Clou der Plattform liegt darin, dass Teile der Datenverarbeitung in die sogenannten „Storage Cells“ im „Storage Layer“ verlagert werden können (grüne Elemente in der Grafik). Wir haben es hier also mit „intelligentem“ Storage zu tun, der versteht, was in den abgelegten Daten abgespeichert ist und bestimmte Operationen bereits im „Storage Layer“ durchführen kann.
Auf den „Storage Cells“ läuft eine spezielle „Cell Software“, die diese Intelligenz des „Storage Layers“ implementiert und die Daten bereits vorverarbeiten kann. Auf den sogenannten „Compute Nodes“ (blaue Elemente in der Grafik) läuft die normale Oracle Database Software, wie man sie von herkömmlichen Systemen kennt und kommuniziert mit dem „Storage Layer“ mittels eines speziellen Protokolls sowie einer speziellen ASM (Automatic Storage Management)-Implementierung.
Dies hat mehrere Vorteile: Zum einen können diese Operationen bereits auf Storage Layer-Ebene parallelisiert und optimiert werden, zum anderen kann durch die Aufteilung der Operationen zwischen Storage Layer und Compute Node Layer die Menge der Daten, die zwischen den beiden Layern ausgetauscht werden muss, reduziert werden – der Storage Layer kann die Daten also schon vorfiltern und eine reduzierte Datenmenge an die Compute Nodes liefern, die dann weitere Schritte einer SQL-Verarbeitung durchführen, wie Joins oder Aggregate.
Ein spezielles Feature bei den sogenannten „Smart Scans“ sind die „Storage Indizes“, die auf Storage-Layer-Ebene automatisch erzeugt und gepflegt werden, und die es ermöglichen, bei entsprechenden Daten und Filtern I/O zu vermeiden, da die „Storage Indizes“ anzeigen, dass in einem Teilbereich des zu durchsuchenden Segments die gesuchten Daten nicht vorkommen können, da zum Beispiel der anzuwendende Filter außerhalb der Minimal-/Maximalwerte der Spalte in diesem Teilbereich liegt.
Die Verbindung zwischen den Compute Nodes und den Storage Cells verwendet Infiniband (rote Elemente in der Grafik) und ein dafür optimiertes Protokoll. Auch die Compute Nodes sind über Infiniband verbunden. Allerdings findet keine Kommunikation zwischen den Storage Cells statt – auf Storage Cell Ebene ist Exadata also eine „Shared Nothing“-Konfiguration, was einige interessante Implikationen hat.
Wie man den offiziellen Datenblättern entnehmen kann, können Smart Scans in einem Exadata Full Rack (oder den Xn-8 (X3-8 / X4-8) Modellen) bei den aktuellen Modellen in Verbindung mit Flash Cache eine Scan-Geschwindigkeit von bis zu 100GB pro Sekunde – wohlgemerkt unkomprimiert – erreichen. Oracle spricht hier offiziell von „SQL Processing Speed“ oder auch „Maximum SQL bandwidth“.
Der reale Fall
Ausgangspunkt der gesamten Analyse war eine bestimmte, extrem zeitkritische Abfrage eines Kunden, die es zu optimieren galt. Es handelte sich dabei um eine SQL-Abfrage, die 20 Tabellen mittels Outer Join verknüpfen musste.
Wie viel Sinn es macht, als Teil eines extrem zeitkritischen Vorgangs eine solche Abfrage im Design vorzusehen, ist eine ganz andere Frage. Hier zeigt sich die oben in der Einleitung erwähnte und von Oracle durchaus auch als Verkaufsargument vorgebrachte Idee, dass solche Plattformen es erlauben, Zeit bei der Modellierung und dem Design einsparen zu können, da sie ja so schnell sind.
Die zu verarbeitende Gesamtdatenmenge der 20 Tabellen war ca. 210GB, wobei durch die Struktur der Abfrage und Daten nur Full Table Scans der Tabellen in Frage kam. Dabei kam den Exadata spezifischen Features entgegen, dass nur ca. 1/12 der Daten (ca. 18GB) tatsächlich für die Weiterverarbeitung der Abfrage in den Compute Nodes benötigt wurde (ungefähr 1/4 durch Selektion (WHERE-Klauseln in SQL) und davon 1/3 durch Projektion (verwendete Spalten) ausgewählt), und außerdem eine gewisse physische Ordnung der Daten vorlag, was wiederum den Einsatz von „Storage Indizes“ zur Vermeidung von physischem I/O ermöglichen kann.
Allerdings erlaubt das hier beschriebene Szenario nicht den Einsatz von sogenannten „Bloom Filtern“, eine weitere Technologie, die es ermöglicht, Joins, die Daten filtern, beim Einsatz von Smart Scans zu optimieren, da der Bloom Filter das Filtern des Joins bereits in den Storage Cells verarbeitet und somit die an die Compute Nodes zu sendende und in den Joins zu verarbeitende Datenmenge minimiert werden kann. Da Outer Joins per Definition nicht filtern, kann dieses Feature hier also nicht zum Einsatz kommen.
Angesichts der zu verarbeitenden Datenmenge von 210GB, der eben beschriebenen Exadata-freundlichen Eigenschaften der Abfrage und der oben genannten Smart Scan-Geschwindigkeit von bis zu 100GB pro Sekunde ergaben sich Erwartungen an die Laufzeit der kritischen Abfrage im Bereich von wenigen Sekunden, schließlich musste ja nur weniger als ein Zehntel der Daten in den Compute Nodes verarbeitet werden und der Großteil der Daten konnte bereits in den „Storage Cells“ ausgefiltert werden, auf dem Papier also ein ziemlich optimaler Einsatz der Exadata-Technologie.
Tatsächlich ergaben sich dann aber Laufzeiten von 40 Sekunden oder mehr, und es galt die Frage zu beantworten, warum denn die tatsächliche Laufzeit so weit von den Erwartungen abwich.
Laufzeit-Betrachtungen verschiedener Variationen der SQL-Abfrage
Die Grafik zeigt die gemessenen Laufzeiten in Sekunden unterschiedlicher Variationen der kritischen SQL-Abfrage auf der Exadata-Plattform des Kunden (Exadata X2-8). Die ursprüngliche Abfrage kann auch in dieser Übersicht gefunden werden mit einer Laufzeit von 40 Sekunden.
Das Interessante und möglicherweise Überraschende an dieser Übersicht ist, dass alle Abfragen genau auf der gleichen zu durchsuchenden Datenmenge beruhen, nämlich die oben genannten 20 Tabellen mit insgesamt 210GB Datenvolumen.
Das bedeutet: Die 20 Tabellen (insgesamt 210GB) können auf der Plattform des Kunden in weniger als drei Sekunden (2,5 Sekunden) verarbeitet werden – sie können aber auch mehr als zwei Minuten brauchen (123 Sekunden), um ein Ergebnis zurückzuliefern.
Es handelt sich hierbei keinesfalls um künstlich erzeugte Extremfälle, sondern allesamt um sehr simple und an reale Gegebenheiten angelehnte Abfragen. Eine so große Bandbreite von möglichen Laufzeiten für die gleiche Grunddatenmenge macht hoffentlich neugierig, genauer hinzuschauen, wie denn diese Laufzeiten im Detail zustande kommen, wie man sich diese erklären kann und welche Schlüsse daraus gezogen werden können.
Testumgebungen
Zur besseren Einordnung der Ergebnisse hier eine kurze Beschreibung der im Test zum Einsatz gekommenen Umgebungen:
Exadata X2-8 Single Compute Node
64 Cores, 128 CPUs, 1TB RAM, 8 Infiniband-Adapter a 40Gbit/sec (theoretische Bandbreite 40GB pro Sekunde zwischen Compute Node und Storage Cells)
14 Storage Cells, 12 CPU Cores pro Storage Cell, insgesamt 168 CPU Cores, 5,3TB Flash Cache, High Capacity Disks.
Laut offiziellem Datenblatt maximal 50GB pro Sekunde „uncompressed Flash data bandwidth“ bei Einsatz von Flash Cache.
Exadata X3-8 Two Node RAC
80 Cores, 160 CPUs, 2 TB RAM, 8 Infiniband-Adapter a 40Gbit/sec pro Node (theoretische Bandbreite 40GB pro Sekunde zwischen Compute Node und Storage Cells) pro Compute Node
14 Storage Cells, 12 CPU Cores pro Storage Cell, insgesamt 168 CPU Cores, 44TB Flash Cache, High Capacity Disks
Laut offiziellem Datenblatt maximal 100GB pro Sekunde „Maximum SQL flash bandwidth“ bei Einsatz von Flash Cache.
Analyse Exadata Features
Über entsprechende Statistiken kann ausgewertet werden, welche Exadata-spezifischen Features zu welchem Grad eingesetzt werden. Dies ist wichtig für ein genaueres Verständnis, wie eine bestimmte Performance im Exadata-Umfeld zustande kommt.
Unter anderem folgende Features können darüber analysiert werden:
- Smart Scans: Einsatz von Smart Scans und zu welchem Grad Smart Scans verwendet wurden, da je nach Szenario (zum Beispiel Read Consistency, Chained Rows oder hohe CPU-Auslastung der „Storage Cells“) auch bei Smart Scans die Menge der bereits in den Zellen verarbeiteten Daten stark variieren kann
- Offloading Percentage: Wie viele Daten wurden in den „Storage Cells“ verarbeitet im Verhältnis zu der Menge an Daten, die zwischen den Zellen und den „Compute Nodes“ ausgetauscht wurden. Hier sind auch durchaus negative Prozente möglich, da bei Einsatz von Kompression oder Spiegelung von Schreibvorgängen (ASM Mirroring) mehr Daten zwischen „Storage Cells“ und „Compute Nodes“ ausgetauscht werden können als aus „Compute Node“-Sicht verarbeitet werden
- Flash Cache: Einsatz von Flash Cache und Menge an I/O Requests / Daten verarbeitet mittels Flash Cache
- Storage Indexes: Vermeidung von I/O mittels Storage Indexes
Zur Auswertung der entsprechend relevanten Statistiken bietet sich Tanel Poder’s „ExaSnapper“ Tool an, dass ein Delta der entsprechenden Statistiken automatisch erzeugen und graphisch darstellen kann.
Einfache SELECT COUNT(*)-Abfrage
Fangen wir also mit den ersten Ergebnissen der obigen Übersicht an. Um zu sehen, ob die Exadata Smart Scans tatsächlich die auf den Datenblättern beschriebene Performance im konkreten Fall des Kunden liefern können, wurden die den Original-Filter-Kriterien entsprechenden Zeilen in den 20 Tabellen mittels einer einfachen UNION ALL-Abfrage gezählt. Dabei galt es ca. 400 Millionen Zeilen aus ca. 1,6 Milliarden Zeilen zu finden und mittels COUNT(*) zu zählen (eben die oben beschriebenen ca. 25% Prozent der Gesamtdaten).
Folgendes Bild ergab sich für die SELECT COUNT(*)-Abfrage bei einem Parallel DOP von 64 auf der X2-8 und einem DOP von 128 auf der X3-8 und Verwendung von Flash Cache für die Full Table Scans (auf X3-8 mit aktueller Firmware automatisch gecacht, auf X2-8 mittels CELL_FLASH_CACHE KEEP Storage-Klausel explizit eingeschaltet):
X2-8: 2,54 Sekunden, im Durchschnitt ca. 85GB pro Sekunde gelesen, Peak 90GB pro Sekunde
X3-8: 2 Sekunden, im Durchschnitt ca. 105GB pro Sekunde gelesen, Peak 105GB pro Sekunde
Erstaunlicherweise werden bei der einfachsten Form der Abfrage also sogar (deutlich) höhere Durchsätze erzielt als gemäß Datenblatt spezifiziert (zum Beispiel tatsächlich 85GB vs. 50GB pro Sekunde gemäß Datenblatt bei X2-8). Wie ist das möglich? Handelt es sich um Messfehler oder veraltete Angaben in den Datenblättern? Hier ist der Blick in die oben genannten Statistiken sehr hilfreich für das Verständnis:
Die Statistiken bedeuten im Einzelnen folgendes:
- DB_PHYSICAL_IO: Aus Datenbank-Sicht die Gesamtmenge an I/O (lesend und schreibend), hier also ca. 212GB (217361MB)
- PHYSICAL_READ_FLASH_CACHE: Datenmenge die über den Flash Cache in den „Storage Cells“ gelesen werden konnte, in diesem Fall ca. 54GB (55756MB)
- STORAGE_INDEX_SAVED: I/O-Datenmenge, die aufgrund von Storage Indizes in den „Storage Cells“ vermieden werden konnte, da die gesuchten Daten in diesen Teilbereichen nicht vorkommen können, in diesem Fall ca. 158GB (161606MB)
- SPINNING_DISK_READ: I/O-Datenmenge, die von den Festplatten in den „Storage Cells“ gelesen wurden, in diesem Fall 0MB
- TOTAL_INTERCONNECT: Die I/O-Datenmenge, die zwischen „Storage Cells“ und „Compute Nodes“ für diese Abfrage ausgetauscht wurden. In diesem Fall nur etwa 4,5GB (4574MB), da der „Smart Scan“ nur die ROWIDs an die „Compute Nodes“ für das Zählen der 400 Millionen Zeilen liefern musste.
- SMART_SCAN_RETURNED: Die I/O-Datenmenge, die der „Smart Scan“ an die „Compute Nodes“ geliefert hat – die gleichen 4,5GB in diesem Fall. Wie wir später sehen werden, stimmen diese beiden Größen nicht immer überein.
Der Grund für die über dem jeweils angegebenen Maximum liegende Lesegeschwindigkeit ist also offensichtlich die Tatsache, dass nur ca. ein Viertel der Daten tatsächlich gelesen wurde und drei Viertel der Daten per Storage Indexes überhaupt nicht verarbeitet werden mussten.
Desweiteren wird offensichtlich, dass durch den „Smart Scan“ nur ein Bruchteil der tatsächlich in den „Storage Cells“ verarbeiteten Daten an die „Compute Nodes“ zurückgeliefert wurde, hier also ca. 4,5GB der 54 bzw. 212GB, also auch deutlich weniger als die oben genannten 18GB für die eigentliche Abfrage.
Aus „Compute Node“-Sicht ergibt sich die in Abb. 7 zu sehende relative Aktivitätsverteilung bei dieser Abfrage.
Und folgendes Bild (s. Abb. 8) für die absolute Datenbankzeit, also die Aufteilung der Zeit, die die dazugehörigen Prozesse in den „Compute Nodes“ verbracht haben. Da es sich um „Parallel Execution“ handelt, können hier auch innerhalb weniger Sekunden mehrere Minuten an Datenbankzeit verbracht werden, da viele Prozesse gleichzeitig an der Abfrage arbeiten.
Die beteiligten Prozesse in den „Compute Nodes“ verbringen also mehr als die Hälfte Ihrer Zeit mit Warten auf Ergebnisse aus den „Storage Cells“ (ca. 87 Sekunden der insgesamt 154 Sekunden Datenbankzeit).
Wird der Einsatz von Storage Indizes verhindert, ergibt sich folgende Performance für die gleiche Art der Abfrage:
X2-8: 5,1 Sekunden, im Durchschnitt ca. 43GB pro Sekunde gelesen, Peak 45GB pro Sekunde
X3-8: 3,1 Sekunden, im Durchschnitt ca. 70GB pro Sekunde gelesen, Peak 75GB pro Sekunde
Hier werden also die laut Datenblatt erreichbaren Scan-Geschwindigkeiten nicht mehr ganz erreicht.
Die Unwirksamkeit der Storage Indizes in diesem Fall kann bestätigt werden, wenn wir wiederum auf die Session Statistiken schauen:
Im Gegensatz zum vorherigen Beispiel müssen jetzt also tatsächlich alle 212GB in den Zellen verarbeitet werden, der größte Teil wird aus dem Flash Cache gelesen, ein kleiner Teil (ca. 3,5GB) sogar von den Festplatten, da der Flash Cache wohl teilweise an seine Kapazitätsgrenze gebracht wird. Auch das Aktivitätsprofil verändert sich entsprechend (s. Abb. 10).
Und eine entsprechende Verschiebung / Erhöhung der Datenbankzeit kann ebenso gesehen werden (s. Abb. 11).
Es wird jetzt also deutlich mehr auf I/O gewartet (83% der Gesamtzeit) als bei der Verwendung von Storage Indizes. Die Gesamtdatenbankzeit aller in den „Compute Nodes“ beteiligten Prozesse erhöht sich entsprechend auf über 5 Minuten.
Einfache SELECT MAX()-Abfrage
Als nächste Komplexitätsstufe wird die COUNT(*)-Abfrage in eine SELECT MAX()-Abfrage verändert. Dabei werden alle Spalten mit MAX(COLUMN) verwendet, die auch bei der Original-Abfrage in der Projektion zur Verwendung kommen. Dabei ergaben sich folgende Laufzeiten bei Verwendung von Storage Indizes, also zu vergleichen mit dem ersten SELECT COUNT(*)-Beispiel:
X2-8: 7,0 Sekunden, im Durchschnitt ca. 31GB pro Sekunde gelesen, Peak 31GB pro Sekunde
X3-8: 3,7 Sekunden, im Durchschnitt ca. 58GB pro Sekunde gelesen, Peak 60GB pro Sekunde
Im Vergleich zu der einfachen SELECT COUNT(*)-Abfrage unter Verwendung von Storage Indizes ist hier eine deutliche Verlängerung der Laufzeit zu beobachten, als auch eine verringerte Lesegeschwindigkeit auf I/O-Seite.
Die Analyse der Session Statistiken zeigt folgendes Bild:
Die eigentliche Abfrage
X2-8: 39,3 Sekunden, im Durchschnitt ca. 5,5GB (7GB) pro Sekunde gelesen, Peak 28GB pro Sekunde
X3-8: 23,7 Sekunden, im Durchschnitt ca. 9GB (12GB) pro Sekunde gelesen, Peak 27GB pro Sekunde Das ist erst mal ein extremer Unterschied zu den vorherigen Zahlen – insbesondere sind auch die Lesegeschwindigkeiten weit von dem entfernt, was die Plattform grundsätzlich in der Lage ist zu liefern. Tatsächlich befinden sich die Lesegeschwindigkeiten in Bereichen, die von den Festplatten geliefert werden können, wir also möglicherweise hier nicht mehr wirklich vom Einsatz des Flash Caches profitieren. Die Zahlen in Klammern bezüglich der Lesegeschwindigkeit beziehen sich auf die Gesamtmenge an I/O, während die Zahlen vor den Klammern sich auf die Grundmenge von ca. 210GB beziehen. Der Zusammenhang wird klarer, wenn wir auf die entsprechenden Session-Statistiken schauen: