Die Aussage „Die Performance der Datenbank ist schlecht“ kennt wahrscheinlich jeder DBA.
„In der letzten Woche wurden die Daten noch schneller verarbeitet“, die Aussage kennt auch jeder DBA und noch vieles mehr…
Flashback Recovery Area (FRA) vollgelaufen, Tablespace auf Anschlag, das kennt ja auch jeder, lässt sich jedoch durch ein gutes Monitoring vermeiden. Im Idealfall wird eine Anomalie-Erkennung genutzt, welche bei ungewöhnlichem Wachstum von Segmenten im Vorfeld alarmiert. Hierzu ist natürlich eine Historisierung der Segment-Größen über einen längeren Zeitraum erforderlich.
Durch eine Auswertung der Transaktionslast pro Zeiteinheit ist eine eventuelle Erkennung von zukünftigen Speicherproblemen möglich. Die Auswertung könnte z.B. indirekt über die Redo-Log-Switch Rate ermittelt werden.
Zur Auswertung von ungewöhnlichem Wachstum, sowohl Transaktionen als auch Segmenten, könnte AI zum Einsatz kommen, eventuell Propeth von Meta, welches gewollte Lastschwankungen berücksichtigt.
Wer jemals eine Datenbank auf einer Shared-Infrastruktur, sei es SAN- oder NAS-Storage oder einem Hypervisor betrieben hat, kennt das Problem mit schwankender Leistung der Infra-Struktur. Die Thematik ist auch bei Container Datenbanken mit vielen „Pluggable Databases“ relevant.
In den meisten Umgebungen wird „quality of service“ eingesetzt, dieses garantiert jedoch nur die Untergrenze. Viele Konsumenten von Datenbank-Services sind jedoch nicht an die Untergrenze gewöhnt, somit kann es zu Beschwerden kommen.
Aus meiner Erfahrung heraus sind regelmäße Datenbank-Benchmarks eine gute Möglichkeit, um die Leistungsfähigkeit von Datenbank-Systemen zu vergleichen. Ich mache gerne einen Benchmark vor und nach Änderungen an der Infrastruktur z.B. Hardwarewechsel, Update Storage-System, Betriebssystem Upgrade und vieles mehr. Somit ist eine solide Diskussionsgrundlage mit dem Infrastrukturbetrieb gegeben. Ein typischer Benchmark für Oracle ist „Swing Bench“. Um Datenbankhersteller übergreifend zu vergleichen kann z.B. „HammerDB“ genutzt werden.
Limitierende Faktoren
Der beschränke Faktor ist die Infrastruktur, ohne Limits würde es keine Performance-Probleme geben. Selbst der schlechteste SQL würde ohne Indexe super schnell laufen.
Arbeitsspeicher (RAM)
Für die Datenbank sollte genügend Arbeitsspeicher bereitstehen. Bei Linux-Systemen haben sich „huge pages“ bewährt, jedoch gibt es eine Einschränkung mit „Automatic Memory Management“. Eine ausreichende Dimensionierung sollte regelmäßig geprüft werden, da sich Workloads im Laufe der Zeit ändern können.
Disk
Die Leistung der Disk hat sich in den letzten Jahren wesentlich verändert. Vor einigen Jahren kamen die SSDs auf den Markt, aktuell sind NVMe SSD eine gute Wahl für schnellen Storage.
Die Einführung von Oracle ASM hat auch zu einer wesentlichen Entspannung bei IO-Problemen beigetragen. Mein Favorit ist lokaler Storage, da hier die Zugriffszeiten und der Datendurchsatz konstant sind. Bei SAN- oder NAS-Storage ist dieses nicht gegeben.
Rechenleistung (CPU)
Das Speichermedium und der Arbeitsspeicher lassen sich in der Regel immer upgraden, bei der CPU kann es schwieriger werden. Deshalb sollte hier ein Spitzenmodell ausgewählt werden.
Da bei der Oracle Lizenzierung die Anzahl der CPU eine wesentliche Rolle spielt, kann durch eine teure CPU viel Geld gespart werden!
Die Client Anbindung über ein Netzwerk
Wird durch die Rechenzentrum Architektur vorgegeben.
Nach meiner Erfahrung ist in der Regel jedoch die konsumierende Anwendung das Nadelöhr.
Jedoch kann das Netzwerk nicht ausgeschlossen werden, wenn z.B. die Infrastruktur über mehrere Rechenzentren verteilt ist. Ein klassischer Fall ist die synchrone „Data Guard“ Replikation in ein Standby Rechenzentrum oder die Anwendung ist in Rechenzentrum 1 und die Datenbank in Rechenzentrum 2.
Systematisches Vorgehen, um die mögliche Ursache von Performance-Problemen einzugrenzen.
Wenn es die Möglichkeit gibt, können Zeiträume verglichen werden.
In einem Enterprise Umfeld mit den Oracle Optionen Diagnostic + Tuning können die Zeiträume sehr einfach und schnell mit einem Automatic-Workload-Repository Diff-Report verglichen werden.
Sollte diese Möglichkeit nicht vorhanden sein kann das bewährte Statspack herangezogen werden. Hier können die unterschiedlichen Snap-Shots verglichen werden. Vorab sollte das nötige Setup und die regelmäße Ausführung z.B. über einen Job gegeben sein.
Nun haben wir herausgefunden, dass die Hardware richtig funktioniert und die Datenbank ordentlich dimensioniert ist, der Fachbereich beschwert sich jedoch noch immer.
Welche Werkzeuge benutze ich:
Ein Blick in das Alertl.log ist zum Einstieg eine gute Quelle, auch gerne über den View V$DIAG_ALERT_EXT. Das Alert.log kann mit SQL ausgewertet werden oder sollte es keinen Zugriff auf das OS geben.
Das Listener.log kann eine gute Quelle für Probleme-Ursachen sein.
Der Oracle Logminer ist eine gute Option um vergangene DML einzusehen. Damit können die Redo-Logs und die archivierten Redo-Logs gelesen werden.
Ein Session Trace kann bei aktuellen Problemen sehr hilfreich sein, kann jedoch bei aktuellen Anwendungen mit Connection-Pooling sehr umfangreich werden.
Unified Audit Log kann zur Klärung beitragen z.B. in dem fehlgeschlagene SQL’s oder Connections auditiert werden. In regulierten Umgebungen ist dieser schon oft im Einsatz.
Zu Diagnose-Zwecken könnten ggf. zusätzliche Policies verwendet werden.
Eine detaillierte Protokollierung von Fehlern ist mit einem Event (SET EVENT) möglich.
Ein SERVERERROR Trigger kann dienlich sein aber nur mit Exception Handler, Rekursionsgefahr!
Die dynamischen Performance Views sind ein tägliches Handwerkszeug.
In Enterprise-Umgebungen ist aus meiner Sicht Diagnostic + Tunning Pack sehr dienlich.
Sollte Diagnostic + Tunning Pack nicht vorhanden sein kann das bewährte Statspack genutzt werden.
Zur Analyse von Inhalten nutze ich neben SQL*Plus und dem SQL-Developer gerne Python (Jupyter Notebooks) unter Microsoft Visual Studio Code mit einer GitHub Copilot Integration. Damit können ganz einfach z.B. mit „Pandas Dataframes“ Daten ausgewertet und verglichen werden. Eine einfache Visualisierung mit Matplotlib oder Seaborn ist möglich.
Probleme aus den letzten Tagen:
Bei akuten Performanz-Problemen schaue ich immer erst in die v$session nach den aktuellen SQL, der WAIT_CLASS , STATE und WAIT_TIME_MICRO, über USERNAME und MACHINE lassen die die betroffen Session gut finden.
Im nächsten Schritt prüfe ich den Ausführungsplan, die Ausführungshäufigkeit usw.
Hier sind v$sql, v$sqlarea und v$sql_plan ein guter Ansatzpunkt. Wie immer gibt es viele Möglichkeiten, ich nutze auch gerne das Package „DBMS_XPLAN.DISPLAY_CURSOR“ zur Anzeige von Plänen.
Hier könnten hunderte von Seiten gefüllt werden, aber es geht ja nur um einen grundsätzlichen Ansatz.
Es hat sich auch sehr bewährt zu prüfen wo die größten Waits sind.
Hier gibt es auch viele Views, die können einfach im „dict“ nachgeschlagen werden.
Beispiel:
SQL>
set pages 100;
set echo on;
col TABLE_NAME format a40;
select table_name from dict where table_name like '%WAIT%';SQL> SQL> SQL> SQL> SQL>
TABLE_NAME
----------------------------------------
DBA_HIST_CHANNEL_WAITS
DBA_HIST_PROCESS_WAITTIME
DBA_HIST_SERVICE_WAIT_CLASS
DBA_HIST_WAITCLASSMET_HISTORY
DBA_HIST_WAITSTAT
DBA_WAITERS
CDB_HIST_CHANNEL_WAITS
CDB_HIST_PROCESS_WAITTIME
CDB_HIST_SERVICE_WAIT_CLASS
CDB_HIST_WAITCLASSMET_HISTORY
CDB_HIST_WAITSTAT
CDB_WAITERS
GV$CHANNEL_WAITS
GV$CON_SYSTEM_WAIT_CLASS
GV$SERVICE_WAIT_CLASS
GV$SESSION_WAIT
GV$SESSION_WAIT_CLASS
GV$SESSION_WAIT_HISTORY
GV$SYSTEM_WAIT_CLASS
GV$WAITCLASSMETRIC
GV$WAITCLASSMETRIC_HISTORY
GV$WAITSTAT
V$CHANNEL_WAITS
V$CON_SYSTEM_WAIT_CLASS
V$SERVICE_WAIT_CLASS
V$SESSION_WAIT
V$SESSION_WAIT_CLASS
V$SESSION_WAIT_HISTORY
V$SYSTEM_WAIT_CLASS
V$WAITCLASSMETRIC
V$WAITCLASSMETRIC_HISTORY
V$WAITSTAT
V$WAIT_CHAINS
Jetzt geht es an das Eingemachte, welche Möglichkeiten könnten die Ausführung beschleunigen:
- Index-Optimierung
- Optimizer Statistiken sammeln
- SQL umschreiben
- Query Rewrite
- Bind-Variablen (richtig) nutzen
- Parallele Ausführung aktivieren
- Adaptive Query Optimierung
- Partitionierung
- Plan Fixen (Plan Baseline / SQL Profile)
- In Memory Option
- Data Compression
- Oracle Parameter anpassen
Einige der aufgeführten Möglichkeiten sind kostenpflichtige Optionen!
Zum Schluss noch Beispiele aus den letzten Tagen:
Index-Optimierung:
Wir haben über das Automatic Workload Repository, Statspack oder die dynamischen Performanc-Views viele „Full Table Scans“ auf eine Tabelle mit einer „where claus“ gefunden.
Nun prüfen wir die Selektivität der Spalten aus der „where clause“, die Selektivität ist hoch. Wir erzeugen den passenden Index.
Eine Tabelle wurde frisch angelegt und mit Indexen versehen. Die Tabelle wird im Batch beladen wobei die Nutzung von Indexen vorgesehen war. Leider wurde der Index Ad-hoc nicht genutzt, da die Kosten als zu hoch eingestuft wurden. Als Workaround wurde der Parameter „optimizer_index_cost_adj = 10“ in der Session gesetzt. Somit konnte die Beladung um ein Vielfaches beschleunigt werden.
Bind Variablen (richtig) nutzen:
Wir haben ein SQL mit einer „where clause“ auf einer Spalte mit Index und einer sehr hohen Selektivität. Der Index wir jedoch nicht genutzt. Dem SQL wurden die Parameter als Bind-Variablen übergeben. Die Spalte mit dem Index war vom Typen „Number“, die Bind Variable wurde jedoch als „String“ übergeben.
Die Datenbank hat einen Type-Cast auf die „Number“ Spalte nach „String“ durchgeführt, somit war der Index nicht mehr nutzbar. Das Problem wurde durch einen Fix in der Anwendung gelöst, ein „Function-based Index“ hätte ebenso funktioniert.
Die Binde-Variablen wurden in DBA_HIST_SQLBIND und V$SQL_BIND_CAPTURE nachgeschlagen. Auch hier gibt es viel Möglichkeiten.
Data Compression
Ein nächtlicher Batch-Job läuft an machen Tagen in 3 Stunden an anderen Tagen in 7 Stunden.
Entgegen der Behauptung konnten über eine Diff-Report Unterschiede in der Verarbeitung festgestellt werden. An Tagen mit langer Laufzeit läuft der Job in einer anderen Ausprägung mit „select“ und „update“ auf eine große Tabelle. Die Indexe sind optimal gesetzt. Ein Range-Partitionierung würde sich anbieten, wurde jedoch vom Softwarehersteller nicht freigegeben. Da bei der betroffenen Datenbank die Leistung zwischen CPU und IO nicht ausgewogen ist habe ich die Vorteile der schnellen und vielen CPU genutzt und die Tabelle und die relevanten Indexe mit „basic compression“ verdichtet. Zudem ist in der Datenbank „adaptive Parallelität“ konfiguriert. Mit der Compression konnte der IO wesentlich verbessert werden, natürlich zu Lasten der CPU, was aber in dem speziellen Fall das kleinere Übel war.
MAX_IDLE_BLOCKER_TIME
Der Application-Manager einer Java-Anwendung welche einen Oracle UCP Connection Pool betrieben wird ruft an mit der Aussage „die Sessions sind aufgebraucht“.
Dem technischen User der Anwendung ist ein Profil mit einer Begrenzung von SESSIONS_PER_USER zugeteilt. Die Anwendung läuft in OpenShift auf mehreren Pods verteilt. Die Anwendung ist Teil einer Microservice-Architektur.
Die Analyse ergab, dass alle Session eine Transaktion halten und beim gleichen SQL stehen jedoch den Status „Idle“ haben. Die Analyse wurde mit den dynamischen Performanc-Views und dem Logminer durchgeführt.
Was ist geschehen:
Die Anwendung macht ein Update auf eine Zeile in einer Tabelle, ohne Commit. Danach wird eine REST-API aufgerufen, welche, da kein Timeout konfiguriert war, hängen bleibt. Nach einer gewissen Zeit kommt eine andere Session und bemerkt, dass es hier Arbeit gibt und macht ein Update auf die gleiche Zeile, hängt nun natürlich. Das ist solange gegangen bis alle Sessions an der gleichen Zeile hängengeblieben sind.
Da die Anwendung in einer isolierten Pluggable Database gelaufen ist, konnte der Parameter „max_idle_blocker_time“ nach Rücksprache mit dem Application-Manager als Workaround auf 10 Minuten gesetzt werden, bis ein Hot-Fix für die Anwendung entwickelt wurde.