Abfrageoptimierung
Wie Datenbanken SQL-Abfragen intern optimieren, umschreiben und ausführen
Definition: SQL-Abfragen so ausführen, dass sie möglichst schnell laufen und wenig Rechenaufwand verursachen. Die interne Struktur wird verändert - das Ergebnis bleibt immer identisch.
- 1SQL-Abfrage einlesenParser erzeugt einen internen Syntaxbaum (Parse Tree)
- 2Relationale AlgebraSyntaxbaum wird in algebraischen Ausdrucksbaum überführt
- 3Query RewritingOptimizer schreibt den Ausdruck äquivalent, aber effizienter um
- 4Ausführungsplan wählenAus mehreren möglichen Plänen wird der kostengünstigste ausgewählt
SQL wird intern in algebraische Ausdrücke umgewandelt. Grundlage der Datenbankoptimierung.
Filtert Zeilen anhand einer Bedingung. Entspricht der WHERE-Klausel.
Wählt bestimmte Spalten aus. Entspricht den Spalten im SELECT.
Verknüpft zwei Tabellen anhand einer gemeinsamen Bedingung. Entspricht JOIN.
Erst σ (filtern), dann π (projizieren) - das ist effizienter! Zuerst die Datenmenge reduzieren, dann Spalten auswählen.
Das DBMS schreibt SQL-Abfragen automatisch um. Ergebnis bleibt gleich, Ausführung wird effizienter.
WHERE-Filter werden so früh wie möglich angewendet - vor Joins. Weniger Daten müssen geladen werden.
Nur wirklich benötigte Spalten werden gelesen. SELECT * wird auf tatsächlich verwendete Spalten reduziert.
Korrelierte Subqueries werden, wenn möglich, in effizientere JOIN-Operationen umgeschrieben.
Optimizer nutzt algebraische Äquivalenzen, z.B. σ(A∧B) = σ(A)∘σ(B), um Ausdrücke umzuformen.
- JOIN erzeugt mehrere Zeilen pro Lieferant
- DISTINCT muss danach alle Duplikate entfernen
- Bei großen Tabellen sehr teuer
- Prüft nur, ob mindestens 1 Datensatz existiert
- Stoppt beim ersten Treffer (Short-Circuit)
- Keine Duplikate, kein DISTINCT nötig
| Merkmal | Rule-Based Optimizer (RBO) | Cost-Based Optimizer (CBO) ✓ |
|---|---|---|
| Funktionsweise | Festes Regelwerk ("Index immer vor Scan") | Schätzt Kosten anhand echter Statistiken |
| Statistiken | Kennt keine Tabellenstatistiken | Nutzt Kardinalität, Histogramme, Selektivität |
| Entscheidung | Unabhängig von tatsächlicher Datenmenge | Basiert auf I/O, CPU, Zeilenanzahl |
| Status | Veraltet (Oracle bis v7) | SQL Server, Oracle (ab v8), PostgreSQL, MySQL, MariaDB |
Anzahl Zeilen (Kardinalität) · Index-Selektivität · I/O-Operationen (Festplatte) · CPU-Aufwand (Sortierung, Hashing)
Ein Index ist eine separate Datenstruktur für schnellen Zugriff - ähnlich einem Inhaltsverzeichnis in einem Buch.
- Automatisch auf dem Primärschlüssel
- Garantiert Eindeutigkeit
- In SQL Server = Clustered Index auf PK
- Immer vorhanden solange PK definiert
- Bestimmt physische Speicherreihenfolge
- Nur EINER pro Tabelle möglich
- Tabelle IST der Index (B-Baum)
- Sehr effizient für Bereichsabfragen (BETWEEN, >, <)
- Separate Datenstruktur, zeigt auf Zeilen
- Mehrere pro Tabelle möglich
- Sinnvoll für häufig gefilterte Spalten
- Extra Speicher + Pflegeaufwand
| Index Scan | Index Seek ✓ |
|---|---|
| Liest ALLE Zeilen sequenziell durch | Springt direkt zur gesuchten Zeile im B-Baum |
| Ähnlich wie vollständiger Tabellenscan | Traversiert nur nötigen Pfad (logarithmisch) |
| Gut bei geringer Selektivität (viele Treffer) | Extrem effizient bei hoher Selektivität |
WHERE price > 35 (viele Zeilen) | WHERE tnr = 3 (genau eine Zeile) |
✓ Eine Tabelle sehr klein
✗ Teuer bei 2 großen Tabellen: O(n×m)
✓ Große, unsortierte Tabellen ohne Indizes
✗ Benötigt Arbeitsspeicher für Hash-Tabelle
✓ Daten bereits sortiert / Indizes vorhanden
✗ Sortierung kostet Zeit wenn unsortiert
Partition Pruning
Große Tabellen in kleinere, logische Segmente aufteilen. Optimizer liest nur relevante Partitionen.
- Range: nach Wertebereichen (z.B. Jahr)
- List: nach definierten Werten
- Hash: gleichmäßige Verteilung
Kostengrundlage des CBO
- Zeilenanzahl (Kardinalität) pro Tabelle
- Werteverteilung: Histogramme für Spalten
- Anzahl eindeutiger Werte (Selektivität)
- Veraltet → falscher Plan!
SQL Server: sp_updatestats
Oracle: DBMS_STATS.GATHER_TABLE_STATS
SET SHOWPLAN_ALL ON
Baumstruktur - Leserichtung: Bottom-Up!
- Unten: Zuerst ausgeführt (Clustered Index Scans)
- Mitte: Aggregation & Joins (Hash Match)
- Oben: Zuletzt (Sort, Filter/HAVING)
EXPLAIN PLAN FOR <SQL>;
Tabellarisch: Id, Operation, Rows, Cost, Time
access(..)→ Join-Bedingung, nutzt Indexfilter(..)→ Filterbedingung, kein Index
Zu viele Indizes
Beschleunigen SELECT, verlangsamen INSERT/UPDATE/DELETE. Balance ist notwendig.
Veraltete Statistiken
Führen zu suboptimalen Plänen. Regelmäßige Aktualisierung ist entscheidend.
Kleine Datenmengen
Bei wenigen Hundert Zeilen ist Tabellenscan genauso schnell wie Index.
Overhead Planberechnung
Bei sehr einfachen Abfragen kann der Optimierungsaufwand den Gewinn übersteigen.
Optimizer nicht optimal
Bei komplexen Queries mit vielen Joins. Dann helfen Optimizer Hints.
Kein Row-Level Trigger
SQL Server: nur Statement-Level. Oracle hat FOR EACH ROW.
Data Warehouse
Architektur · ETL · Schemata · Materialized Views · Optimierung
Definition (Bill Inmon, 1990): Ein DWH ist eine zentrale, themenorientierte, integrierte, nicht-flüchtige und zeitbezogene Sammlung von Daten zur Entscheidungsunterstützung.
Daten rund um Geschäftsobjekte: Kunden, Produkte, Umsatz
Einheitliches Format aus heterogenen Quellen
Daten werden nicht gelöscht, sondern akkumuliert
Historische Daten ermöglichen Trendanalysen
| Merkmal | OLTP | OLAP |
|---|---|---|
| Zweck | Transaktionen (täglicher Betrieb) | Analysen & Berichte |
| Daten | Aktuell | Historisch |
| Abfragen | Einfach, sehr häufig | Komplex, selten |
| Optimiert für | Schreiben | Lesen |
| Beispiel | Kassensystem, Buchung | Jahresberichte, Dashboards |
ERP · CRM · Flat Files
Rohdaten temporär
Transform · Bereinigen
3NF · Historie
Fachbereichsspezifisch
BI Tools · Dashboards
Operational Data Store - Kurzfristige operative Berichte, nahe an OLTP
Rohdaten in Originalformat (strukturiert & unstrukturiert)
Kombination: Flexibilität des Lake + Struktur des Warehouse
- Datenextraktion aus Quellsystemen
- Full Load vs. Delta Load
- Change Data Capture (CDC)
- Formate: SQL, CSV, JSON, XML
- Bereinigung (Null-Werte, Duplikate)
- Standardisierung & Normalisierung
- Aggregationen & Berechnungen
- Business-Regel-Anwendung
- Laden ins Ziel-DWH
- Full Load, Incremental, Upsert
- Slowly Changing Dimensions (SCD)
- Fehlerbehandlung & Logging
ELT (Extract-Load-Transform): Moderne Alternative - Transformation im DWH selbst (Snowflake, BigQuery). Schneller bei großen Datenmengen.
Atomare Werte, keine wiederholten Gruppen
1NF + keine partiellen Abhängigkeiten vom Schlüssel
2NF + keine transitiven Abhängigkeiten
Jede Determinante ist ein Superschlüssel
Normalisierung verbessert Schreib-Performance & Wartbarkeit · Denormalisierung verbessert Lese-Performance für Analysen
| Kriterium | Sternschema | Schneeflockenschema |
|---|---|---|
| Normalisierung | 1NF/2NF - denormalisiert | 3NF - normalisiert |
| Dimensionen | Alle Attribute in EINER Tabelle (Redundanz) | In Sub-Tabellen aufgeteilt (keine Redundanz) |
| Join-Ebenen | 1 Ebene (Fact → Dim) | Mehrere Ebenen (Fact → Dim → Sub-Dim...) |
| Lese-Performance | Sehr schnell | Langsamer (mehr Joins) |
| Speicher | Mehr (Redundanz) | Weniger |
| Wartbarkeit | Aufwändiger | Einfacher |
| Empfehlung | BI, OLAP, Self-Service | Großes DWH, häufige Updates, Inmon-Ansatz |
- Schnelle Abfrageperformance Priorität
- Business Users direkt abfragen (Power BI, Tableau)
- Dimensionen ändern sich selten
- Data Mart für einen Fachbereich
- Speicherplatz ist knapp (große Dimensionen)
- Dimensionsdaten ändern sich häufig
- Hohe Datenintegrität nötig
- Unternehmensweites Core-DWH (Inmon)
Viele DWHs nutzen Hybrid-Ansätze: Core DWH in 3NF (Schneeflockenschema), Data Marts in Sternschema für optimale Balance aus Flexibilität & Performance.
Keine Änderung
Historische Daten unveränderlich. Neue Werte werden ignoriert.
Bsp: Geburtsdatum
Überschreiben
Alter Wert wird überschrieben. Keine Historie.
Bsp: Tippfehler korrigieren
Neue Zeile
Neue Zeile eingefügt. Alte als inaktiv markiert (gültig_bis). Vollständige Historie.
Bsp: Adressänderung
Neue Spalte
Neuer Wert in neuer Spalte. Alter Wert bleibt. Begrenzte Historie.
Bsp: vorherige_Abteilung
| Normale View | Materialized View |
|---|---|
| Virtuelle Tabelle | Physisch gespeichertes Ergebnis |
| Abfrage wird bei Zugriff ausgeführt | Vorberechnetes Resultat |
| Immer aktuell | Muss regelmäßig refreshed werden |
| Langsam bei komplexen Abfragen | Sehr schnell bei Lesezugriff |
| Kein Speicherplatz | Benötigt Speicherplatz |
Vollständige Neuberechnung
Nur geänderte Daten (mit MV-Log)
Fast wenn möglich, sonst Complete
Zeitpunkt des Refreshs
NoSQL
Klassifizierung · CAP-Theorem · BASE-Theorem · Hadoop
NoSQL = "Not only SQL" - Nicht-relationaler Ansatz ohne feste Tabellenschemata bzw. Joins. Für Anwendungsfälle, bei denen herkömmliche relationale Datenbanken an ihre Grenzen stoßen.
| Merkmal | SQL | NoSQL |
|---|---|---|
| Datenmodell | Relational (Tabellen) | Key-Value, Document, Wide Column, Graph |
| Schema | Fest, starr vordefiniert | Dynamisch, schemafrei |
| Skalierbarkeit | Vertikal (mehr Ressourcen zu einem Server) | Horizontal (weitere Server/Knoten hinzufügen) |
| Transaktionen | ACID | CAP / BASE |
| Optimierung | Komplexe Abfragen & Transaktionen | Große Datenmengen & Echtzeitanalysen |
| Anwendungen | Finanzsysteme, CRM | Big Data, IoT, Echtzeitanalyse |
Einfachste Form
Sammlung von Key/Value-Paaren. 1 Wert je Schlüssel. Zugriff nur über Schlüssel: get(key), put(key, value)
RedisAmazon DynamoDB
Sehr hohe Datenmenge, geringe Abfragekomplexität
Semistrukturierte Daten
Speicherung von JSON-Dokumenten. Einfache Abfragesprache. Zugriff über Schlüssel.
MongoDBCouchbase
Dynamische Spalten
Tabellen mit Zeilen und sehr vielen dynamischen Spalten. Zugriff über Schlüssel oder SQL-ähnliche Abfragen.
CassandraApache HBaseBigTable
Knoten & Kanten
Daten als Knoten und Kanten mit Eigenschaften. Datenbankabfragen inkl. Graphenalgorithmen.
Neo4jOrientDB
Geringere Datenmenge, hohe Abfragekomplexität
Key-Value Stores: hohe Datenmenge, geringe Komplexität → Column Families → Document Stores → Graph Databases: geringere Datenmenge, hohe Komplexität
Veränderung vollständig oder gar nicht (All or Nothing)
Von einem konsistenten Zustand in den nächsten
Transaktionen dürfen sich nicht beeinflussen
Veränderungen sind dauerhaft abgespeichert
Bei verteilten Systemen können nur zwei von drei Eigenschaften gleichzeitig garantiert werden. (Brewer's Theorem)
Alle Clients sehen zur selben Zeit die gleichen Daten. Lesezugriffe auf jedem Knoten liefern den aktuellen wert.
System muss in vorher definierter Reaktionszeit reagieren. Knotenausfälle beeinflussen andere aktive Knoten nicht.
Gesamtsystem bleibt bei Ausfall einzelner Kommunikationsverbindungen lauffähig. Replikation auf mehrere Server nötig.
Kein verteiltes System (kein P). Daten konsistent solange alle Knoten online.
MySQLSQL ServerMariaDB
Konsistent & ausfallsicher. Verfügbarkeit wird geopfert - Transaktionen werden blockiert bis alle Knoten synchron.
HBaseMongoDBRedis
Immer verfügbar, auch bei Knotenausfall. Konsistenz nicht garantiert (Eventually Consistent).
CassandraCouchDBRiak
AP-Ansatz: priorisiert Verfügbarkeit und Ausfallsicherheit über Konsistenz. Gegensatz zu ACID.
Benutzer können jederzeit gleichzeitig zugreifen, müssen nicht auf andere Transaktionen warten.
Daten haben einen vorübergehenden Zustand (Übergangszustand) bis alle Transaktionen abgeschlossen sind.
Abfragen können unterschiedliche Ergebnisse liefern je nach Knoten. Konsistenz ist nicht zu jedem Zeitpunkt gewährleistet.
| ACID | BASE |
|---|---|
| Starke Konsistenz | Schwache Konsistenz (veraltete Daten ok) |
| Isolation | Availability first |
| Verfügbarkeit eingeschränkt | Best effort Verfügbarkeit |
| Konservativ (pessimistisch) | Schneller |
| Zuverlässige Datenqualität | Ungefähre Antworten sind ok |
Framework zur Speicherung und Verarbeitung von sehr großen Datenmengen (Big Data) auf vielen Computern gleichzeitig. Basiert auf dem MapReduce-Algorithmus und den Grundideen des Google-Dateisystems.
Verteiltes Dateisystem
NameNode verwaltet Metadaten. DataNodes speichern die Datenblöcke. Daten werden aufgeteilt und repliziert.
Resource Manager
ResourceManager + NodeManager + ApplicationManager. Verteilt Jobs auf CPU/Speicher im Cluster.
Parallele Verarbeitung
Gilt als veraltet. Wird durch DAG-basierte Engines (Apache Spark, TEZ) ersetzt.
Basis-Bibliotheken
Stellt grundlegende Tools und Bibliotheken für alle Komponenten bereit.
- 1Split (Aufteilen)Datenmenge wird in kleinere Teile aufgeteilt und an verfügbare Nodes verteilt. Jeder Datensatz hat einen Schlüssel-Wert (Key).
- 2Map()Map-Funktion generiert Key/Value-Paare aus den Eingabedaten und speichert diese zwischen.
- 3Shuffle & SortBerechnete Keys werden den Reduce-Nodes zugewiesen. Reduce-Nodes holen Datensätze entsprechend dem zugewiesenen Key.
- 4Reduce()Fasst Key/Value-Paare anhand des Schlüssels zusammen und berechnet die Summe. Ausgabe ist wieder ein Key/Value-Paar.
Input: "Deer Bear River / Car Car River / Deer Car Bear"
Map: Deer→1, Bear→1, River→1, Car→1, Car→1 ...
Shuffle: Gruppiert gleiche Schlüssel · Reduce: Bear→2, Car→3, Deer→2, River→2
| Eigenschaft | SQL wählen wenn... | NoSQL wählen wenn... |
|---|---|---|
| Datenstruktur | Strukturiertes tabellarisches Format | Flexibles Schema, unstrukturierte Daten |
| Skalierbarkeit | Vertikale Skalierung ausreichend | Horizontale Skalierung für hohen Traffic |
| Transaktionen | ACID-Compliance entscheidend | Eventually Consistent akzeptabel, Verfügbarkeit Priorität |
| Abfragen | Komplexe Joins, Aggregationen | Einfache Key-Value-Suchen, verteilte Abfragen |
| Performance | Konsistenz wichtiger als Geschwindigkeit | Echtzeitverarbeitung wichtiger |
Weder relationale Modelle noch NoSQL-Modelle sind für alle Projekte gleich gut nutzbar - Use whatever makes your product useful.