merging and cleaning up
This commit is contained in:
@@ -0,0 +1,384 @@
|
||||
#datenbank
|
||||
Kontext: Datenbanken
|
||||
|
||||
---
|
||||
ACID-Transaktionen sind ein zentrales Konzept in der Datenbankwelt – vor allem dort, wo Daten „kritisch“ sind (z.B. Banking, Buchhaltung, Bestellungen). Ich führe dich Schritt für Schritt ein, ohne Vorwissen vorauszusetzen.
|
||||
|
||||
---
|
||||
|
||||
## 1. Grundidee: Was ist eine Transaktion?
|
||||
|
||||
Stell dir eine Transaktion als **logische Einheit von mehreren Datenbankoperationen** vor, die entweder **komplett** oder **gar nicht** ausgeführt werden soll.
|
||||
|
||||
Beispiel (Banküberweisung):
|
||||
- Konto A: 100 € → 80 € (10 € werden abgebucht)
|
||||
- Konto B: 50 € → 70 € (10 € werden gutgeschrieben)
|
||||
|
||||
Diese beiden Schritte gehören zusammen:
|
||||
- Es darf nicht passieren, dass bei A 10 € abgebucht werden, ohne dass sie bei B ankommen.
|
||||
- Oder dass B 10 € bekommt, ohne dass sie bei A abgezogen werden.
|
||||
|
||||
Beide Aktionen zusammen bilden eine **Transaktion**.
|
||||
|
||||
---
|
||||
|
||||
## 2. Was bedeutet ACID?
|
||||
|
||||
**ACID** steht für vier Eigenschaften, die Transaktionen in klassischen relationalen Datenbanken gewährleisten sollen:
|
||||
|
||||
1. **A – Atomicity (Atomarität)**
|
||||
2. **C – Consistency (Konsistenz)**
|
||||
3. **I – Isolation**
|
||||
4. **D – Durability (Dauerhaftigkeit)**
|
||||
|
||||
Ich erkläre jede mit einfachen Beispielen.
|
||||
|
||||
---
|
||||
|
||||
### 2.1 Atomicity (Atomarität)
|
||||
|
||||
**Definition:**
|
||||
Eine Transaktion wird **entweder vollständig oder gar nicht** ausgeführt. Es gibt keinen „halben“ Zustand.
|
||||
|
||||
Beispiel:
|
||||
Wieder unsere Banküberweisung:
|
||||
1. 10 € von Konto A abbuchen
|
||||
2. 10 € Konto B gutschreiben
|
||||
|
||||
Passiert in der Mitte ein Fehler (z.B. Stromausfall nach Schritt 1), sorgt Atomicity dafür:
|
||||
- Entweder werden **beide** Schritte ausgeführt
|
||||
- oder **beide rückgängig gemacht** (Rollback)
|
||||
|
||||
Es darf nicht der Zustand entstehen:
|
||||
- Konto A: -10 €
|
||||
- Konto B: unverändert
|
||||
|
||||
Das wäre ein inkonsistenter Zustand – Atomicity verhindert das.
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Consistency (Konsistenz)
|
||||
|
||||
**Definition (aus Sicht der Datenbankregeln):**
|
||||
Eine Transaktion bringt die Datenbank von einem **gültigen Zustand** in einen **anderen gültigen Zustand**, gemäß den definierten Regeln (Constraints, Geschäftsregeln).
|
||||
|
||||
Wichtig: Konsistenz bezieht sich nicht nur auf „richtige Daten“, sondern auf die **Einhaltung von Regeln**, z.B.:
|
||||
|
||||
- Fremdschlüssel-Beziehungen (z.B. jede Bestellung gehört zu einem existierenden Kunden)
|
||||
- Eindeutigkeit (z.B. E-Mail-Adressen sind eindeutig)
|
||||
- Wertebereiche (z.B. Kontostand darf nicht negativ sein, wenn das System das verbietet)
|
||||
|
||||
Beispiel:
|
||||
- Regel: Kontostand darf nicht unter 0 fallen.
|
||||
- Transaktion: Konto mit 5 € soll 10 € abbuchen.
|
||||
- Datenbank oder Geschäftslogik verhindern, dass diese Transaktion „erfolgreich bestätigt“ wird.
|
||||
- Nach der Transaktion ist die Datenbank immer noch in einem Zustand, in dem alle Regeln gelten.
|
||||
|
||||
Konsistenz sagt also:
|
||||
„Wenn du eine Transaktion beginnst, die gültige Daten voraussetzt, dann endet sie (falls sie commitet wird) wieder mit gültigen Daten.“
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Isolation
|
||||
|
||||
**Definition:**
|
||||
Parallele Transaktionen **beeinflussen sich nicht gegenseitig** in einer Art und Weise, die zu inkonsistenten Ergebnissen führt.
|
||||
Jede Transaktion „fühlt sich so an“, als wäre sie die einzige, die gerade läuft.
|
||||
|
||||
Isolation wird in der Praxis über **Isolation Levels** gesteuert (z.B. READ COMMITTED, REPEATABLE READ, SERIALIZABLE), aber fürs Grundverständnis reicht:
|
||||
|
||||
Beispiel (gleichzeitige Transaktionen):
|
||||
|
||||
- T1: Bucht 10 € von Konto A zu Konto B um.
|
||||
- T2: Prüft den Kontostand von Konto A für eine Kreditprüfung.
|
||||
|
||||
Ohne Isolation könnte T2 gerade dann den Kontostand lesen, wenn T1 mitten in der Überweisung ist (z.B. A: 90 €, B: noch nicht aktualisiert).
|
||||
Je nach Isolationsebene verhindert das System:
|
||||
- „schmutzige“ Reads (lesen von unbestätigten Zwischenständen),
|
||||
- instabile Wiederholungen (derselbe SELECT liefert in einer Transaktion plötzlich andere Ergebnisse),
|
||||
- Phantom Reads (neue Datensätze tauchen „plötzlich“ in einer laufenden Transaktion auf).
|
||||
|
||||
Einfach gesagt: Isolation schützt davor, dass sich parallele Transaktionen gegenseitig „in die Quere kommen“.
|
||||
|
||||
---
|
||||
|
||||
### 2.4 Durability (Dauerhaftigkeit)
|
||||
|
||||
**Definition:**
|
||||
Wenn eine Transaktion **erfolgreich abgeschlossen** (committed) ist, bleiben die Änderungen **dauerhaft** gespeichert – auch bei:
|
||||
|
||||
- Stromausfall
|
||||
- Absturz des Servers
|
||||
- Neustart der Datenbank
|
||||
|
||||
Technisch wird das oft über:
|
||||
- Write-Ahead-Log (Transaktionslog),
|
||||
- Journaling,
|
||||
- redundante Speicherung
|
||||
umgesetzt.
|
||||
|
||||
Beispiel:
|
||||
- Überweisung wurde committed.
|
||||
- Direkt danach fällt der Strom aus.
|
||||
- Nach Neustart der Datenbank sind die Kontostände **so**, wie sie nach der Überweisung sein sollen.
|
||||
|
||||
Keine „zufällige Rücksetzung“ auf den Stand davor.
|
||||
|
||||
---
|
||||
|
||||
## 3. Abgrenzung zu verwandten Begriffen
|
||||
|
||||
### 3.1 Transaktion vs. einzelne SQL-Operation
|
||||
|
||||
- Eine **Transaktion** kann aus einer oder mehreren SQL-Anweisungen (z.B. mehreren INSERT, UPDATE, DELETE) bestehen.
|
||||
- Ohne explizite Transaktion wird oft jede Anweisung automatisch als eigene, kleine Transaktion behandelt (Auto-Commit-Modus).
|
||||
|
||||
Beispiel:
|
||||
- Zwei separate UPDATE-Befehle ohne Transaktion:
|
||||
- Es kann passieren, dass nur der erste erfolgreich ist und der zweite fehlschlägt – dann hast du einen halben Zustand.
|
||||
- In einer Transaktion:
|
||||
- Beides wird zusammen behandelt – im Fehlerfall wird alles zurückgerollt.
|
||||
|
||||
---
|
||||
|
||||
### 3.2 ACID vs. BASE (in verteilten / NoSQL-Systemen)
|
||||
|
||||
In vielen verteilten Systemen (z.B. große NoSQL-Datenbanken) wird statt ACID eher das Prinzip **BASE** verfolgt:
|
||||
|
||||
- **B**asically **A**vailable – Das System ist grundsätzlich verfügbar.
|
||||
- **S**oft-state – Der Zustand kann sich ändern, auch ohne explizite Transaktion.
|
||||
- **E**ventual consistency – Daten werden **irgendwann konsistent**, nicht unbedingt sofort.
|
||||
|
||||
Vergleich:
|
||||
- ACID: Strenge Garantien, dafür manchmal langsamer oder schwerer skalierbar.
|
||||
- BASE: Weniger strenge Garantien, dafür oft leicht horizontal skalierbar (über viele Server verteilt).
|
||||
|
||||
Beispiel:
|
||||
- Social-Media-Likes:
|
||||
- Es ist ok, wenn du für ein paar Sekunden oder Minuten leicht unterschiedliche Like-Zahlen auf verschiedenen Geräten siehst.
|
||||
- Bankkonto:
|
||||
- Es ist **nicht** ok, wenn der Kontostand „irgendwann mal“ stimmt. Hier will man ACID.
|
||||
|
||||
---
|
||||
|
||||
### 3.3 Konsistenz (ACID) vs. Konsistenz (CAP-Theorem)
|
||||
|
||||
Der Begriff „Konsistenz“ wird auch im **CAP-Theorem** verwendet (bei verteilten Systemen). Dort bedeutet er etwas anderes:
|
||||
|
||||
- ACID-Konsistenz: Einhaltung von Datenbankregeln (Constraints, Geschäftslogik).
|
||||
- CAP-Konsistenz: Alle Knoten eines verteilten Systems sehen dieselben Daten zur selben Zeit.
|
||||
|
||||
Es ist wichtig, diese beiden Bedeutungen nicht zu verwechseln.
|
||||
|
||||
---
|
||||
|
||||
## 4. Welche Probleme lösen ACID-Transaktionen?
|
||||
|
||||
### 4.1 Vermeidung halbfertiger Zustände
|
||||
|
||||
Ohne Atomicity:
|
||||
- Stromausfall mitten in einer Update-Sequenz → Daten inkonsistent.
|
||||
|
||||
Mit ACID:
|
||||
- Entweder alles oder nichts → Daten bleiben konsistent.
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Schutz vor parallelen Zugriffskonflikten
|
||||
|
||||
Typische Probleme ohne Isolation:
|
||||
|
||||
1. **Lost Update (verlorenes Update)**
|
||||
- Zwei Benutzer lesen denselben Datenstand, ändern ihn unabhängig und speichern beide.
|
||||
- Die zweite Speicherung überschreibt die erste, ohne es zu merken.
|
||||
|
||||
Beispiel:
|
||||
- Lagerbestand: 10 Stück
|
||||
- Benutzer A: Reserviert 3 (denkt: 10 → 7)
|
||||
- Benutzer B: Reserviert 4 (denkt: 10 → 6)
|
||||
- Ohne Schutz: Endergebnis könnte 6 sein (Reservierung von A geht „verloren“).
|
||||
- Mit Isolation/Locks: Das System stellt sicher, dass die Updates korrekt nacheinander verarbeitet werden.
|
||||
|
||||
2. **Dirty Read (schmutziges Lesen)**
|
||||
- Eine Transaktion liest Änderungen, die eine andere Transaktion noch gar nicht committed hat.
|
||||
- Wenn die zweite Transaktion zurückgerollt wird, hat man auf falschen Daten gearbeitet.
|
||||
|
||||
3. **Non-repeatable Read & Phantom Read**
|
||||
- Eine Transaktion liest dieselben Daten mehrmals, aber bekommt unterschiedliche Ergebnisse, weil andere Transaktionen dazwischen geschrieben haben.
|
||||
- Oder es tauchen plötzlich zusätzliche/fehlende Zeilen auf.
|
||||
|
||||
ACID + passende Isolationsebene reduziert oder verhindert solche Probleme.
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Sicherung kritischer Geschäftsprozesse
|
||||
|
||||
Wo ACID typisch ist:
|
||||
|
||||
- Bank- und Finanzsysteme
|
||||
- Flugsitz-Reservierungssysteme
|
||||
- Warenwirtschaft / Bestellabwicklung
|
||||
- Ticketverkauf (Konzert, Bahn, Flug)
|
||||
|
||||
Beispiel Tickets:
|
||||
- Nur 1 Ticket übrig.
|
||||
- 2 Leute versuchen gleichzeitig, es zu kaufen.
|
||||
- ACID-Transaktionen sorgen dafür, dass am Ende nur eine Person das Ticket bekommt, nicht beide.
|
||||
|
||||
---
|
||||
|
||||
## 5. Welche Herausforderungen bringen ACID-Transaktionen mit sich?
|
||||
|
||||
ACID klingt perfekt, hat aber trade-offs.
|
||||
|
||||
### 5.1 Performance und Skalierbarkeit
|
||||
|
||||
- Starke Isolation und Konsistenz können **langsamer** sein, weil:
|
||||
- Locks (Sperren) auf Zeilen/Tabellen gesetzt werden,
|
||||
- viele Logs geschrieben werden müssen,
|
||||
- parallele Zugriffe begrenzt werden.
|
||||
|
||||
Beispiel:
|
||||
- In einem stark frequentierten Online-Shop könnten lange laufende Transaktionen zu Warteschlangen führen:
|
||||
- Kunden müssen warten, bevor ihre Updates durchgeführt werden können.
|
||||
|
||||
---
|
||||
|
||||
### 5.2 Deadlocks (Verklemmungen)
|
||||
|
||||
Wenn mehrere Transaktionen sich gegenseitig sperren, kann es zu einem **Deadlock** kommen:
|
||||
|
||||
Beispiel:
|
||||
|
||||
- T1:
|
||||
- Sperrt Zeile X
|
||||
- Will dann Zeile Y sperren (die aber schon von T2 gesperrt ist)
|
||||
- T2:
|
||||
- Sperrt Zeile Y
|
||||
- Will dann Zeile X sperren (die aber schon von T1 gesperrt ist)
|
||||
|
||||
Beide warten aufeinander → Niemand kommt weiter.
|
||||
|
||||
Die Datenbank muss Deadlocks erkennen und eine Transaktion abbrechen (Rollback), damit sich das System wieder erholen kann.
|
||||
|
||||
---
|
||||
|
||||
### 5.3 Lange laufende Transaktionen
|
||||
|
||||
- Je länger eine Transaktion läuft, desto länger hält sie Sperren.
|
||||
- Das kann viele andere Benutzer blockieren.
|
||||
|
||||
Beispiel:
|
||||
- Ein Report, der mehrere Minuten läuft, wird innerhalb einer Transaktion ausgeführt.
|
||||
- In dieser Zeit können andere nicht ordentlich auf dieselben Daten zugreifen (je nach Isolationsebene).
|
||||
|
||||
Deshalb:
|
||||
In der Praxis versucht man, Transaktionen so **kurz wie möglich** zu halten.
|
||||
|
||||
---
|
||||
|
||||
### 5.4 Verteilte Transaktionen (über mehrere Systeme)
|
||||
|
||||
Wenn eine Transaktion **über mehrere Datenbanken oder Services** geht, wird es komplex:
|
||||
|
||||
Beispiel:
|
||||
- Bestellsystem:
|
||||
- Reserviert Ware in Lager-Datenbank A
|
||||
- Erstellt Rechnung in Finanz-Datenbank B
|
||||
|
||||
Um ACID über beide Systeme zu garantieren, braucht man z.B.:
|
||||
- 2-Phase-Commit (2PC),
|
||||
- spezielle Transaktionskoordinatoren.
|
||||
|
||||
Probleme:
|
||||
- Komplex, fehleranfällig,
|
||||
- schlechte Performance,
|
||||
- schlechte Skalierbarkeit in großen, verteilten Systemen.
|
||||
|
||||
Darum verzichten moderne Microservice-Architekturen oft auf verteilte ACID-Transaktionen und nutzen stattdessen:
|
||||
- Eventual Consistency,
|
||||
- Sagas (geschäftliche, verteilte Abläufe mit Kompensationsaktionen).
|
||||
|
||||
---
|
||||
|
||||
### 5.5 Komplexität in der Anwendungslogik
|
||||
|
||||
- Wenn Entwickler die Eigenschaften von Isolation, Locks, Deadlocks etc. nicht verstehen, können:
|
||||
- unerwartete Blockaden,
|
||||
- merkwürdige Nebenwirkungen,
|
||||
- Performanceprobleme
|
||||
auftreten.
|
||||
|
||||
Deshalb ist ein grundlegendes Verständnis von ACID und Transaktionen auch für Anwendungsentwickler wichtig, nicht nur für Datenbankadministratoren.
|
||||
|
||||
---
|
||||
|
||||
## 6. Praxisnahe Beispiele im Überblick
|
||||
|
||||
### Beispiel 1: Bestellprozess im Online-Shop
|
||||
|
||||
Schritte, die typischerweise in einer Transaktion laufen können:
|
||||
|
||||
1. Kundenbestellung speichern.
|
||||
2. Lagerbestand reduzieren.
|
||||
3. Reservierte Waren markieren.
|
||||
4. Zahlstatus erfassen (z.B. „Bezahlung ausstehend“).
|
||||
|
||||
Mit ACID:
|
||||
- Wenn in Schritt 2 der Lagerbestand negativ würde → Transaktion fehlschlägt → nichts wird dauerhaft gespeichert.
|
||||
- Kein Zustand „Bestellung existiert, aber Lagerbestand stimmt nicht“.
|
||||
|
||||
---
|
||||
|
||||
### Beispiel 2: Benutzerregistrierung
|
||||
|
||||
Schritte:
|
||||
|
||||
1. Benutzerkonto anlegen (INSERT in `users`).
|
||||
2. Authentifizierungsdaten anlegen (INSERT in `credentials`).
|
||||
3. Willkommensguthaben in ein Bonuskonto einzahlen (INSERT in `bonus_accounts`).
|
||||
|
||||
Mit ACID:
|
||||
- Entweder alle drei Inserts werden erfolgreich committed
|
||||
- oder keiner.
|
||||
- Kein Benutzer ohne Credentials, kein Bonuskonto ohne Benutzer.
|
||||
|
||||
---
|
||||
|
||||
### Beispiel 3: Rechnungsstellung
|
||||
|
||||
Schritte:
|
||||
|
||||
1. Rechnung anlegen.
|
||||
2. Rechnungsposten anlegen.
|
||||
3. Offenen Posten im Finanzsystem erzeugen.
|
||||
|
||||
Mit ACID:
|
||||
- Wenn das Anlegen der Rechnungsposten fehlschlägt, wird auch die Rechnung selbst zurückgerollt.
|
||||
- Es gibt keine „leere Rechnung“.
|
||||
|
||||
---
|
||||
|
||||
## 7. Zusammenfassung
|
||||
|
||||
- **Transaktionen** fassen mehrere Operationen zu einer Einheit zusammen.
|
||||
- **ACID**-Eigenschaften sorgen dafür, dass diese Einheiten:
|
||||
- **A**tomar sind (alles oder nichts),
|
||||
- **C**onsistent bleiben (Regeln bleiben gültig),
|
||||
- **I**soliert laufen (parallele Transaktionen stören sich nicht „unsichtbar“),
|
||||
- **D**auerhaft gespeichert werden (Commit ist endgültig, auch bei Abstürzen).
|
||||
|
||||
**Sie lösen vor allem:**
|
||||
- Teilweise Updates,
|
||||
- Inkonsistenzen durch Fehler,
|
||||
- Probleme durch parallele Zugriffe.
|
||||
|
||||
**Herausforderungen sind:**
|
||||
- Performance,
|
||||
- Skalierung,
|
||||
- Deadlocks,
|
||||
- Komplexität (vor allem in verteilten Systemen).
|
||||
|
||||
Wenn du möchtest, können wir als nächsten Schritt:
|
||||
- konkrete SQL-Beispiele (z.B. in PostgreSQL oder MySQL) durchgehen, oder
|
||||
- tiefer in Isolation Levels (READ COMMITTED, REPEATABLE READ, SERIALIZABLE) einsteigen.
|
||||
@@ -0,0 +1,549 @@
|
||||
#datenbank
|
||||
Kontext: Datenbanken
|
||||
|
||||
= ==Common Table Expression==
|
||||
|
||||
>`WITH`-Block in SQL-Abfrage
|
||||
|
||||
➡️[[#Zusammenfassung]]
|
||||
|
||||
---
|
||||
Im Folgenden bekommst du eine umfassende, aber einsteigerfreundliche Einführung in CTEs („Common Table Expressions“) im Kontext von Datenbanken – mit Definition, Abgrenzung, Nutzen, Herausforderungen und praxisnahen Beispielen.
|
||||
|
||||
---
|
||||
|
||||
## 1. Grundidee: Was ist eine CTE?
|
||||
|
||||
**CTE** steht für **Common Table Expression**.
|
||||
Vereinfacht gesagt ist eine CTE:
|
||||
|
||||
> Eine **temporäre, benannte Ergebnismenge**, die du in einer SQL-Abfrage definierst und im Anschluss in derselben Abfrage wie eine Tabelle verwenden kannst.
|
||||
|
||||
Man kann sich das vorstellen wie:
|
||||
|
||||
- „Ich speichere mir ein Zwischenergebnis unter einem Namen…“
|
||||
- „…und benutze dieses Zwischenergebnis dann in der eigentlichen Anfrage.“
|
||||
|
||||
Grundform:
|
||||
|
||||
```sql
|
||||
WITH name_der_cte AS (
|
||||
-- irgendeine SELECT-Abfrage
|
||||
SELECT ...
|
||||
FROM ...
|
||||
WHERE ...
|
||||
)
|
||||
SELECT *
|
||||
FROM name_der_cte;
|
||||
```
|
||||
|
||||
Wichtig:
|
||||
|
||||
- Die CTE existiert **nur für diese eine Abfrage**.
|
||||
- Sie wird **innerhalb der Abfrage** definiert und direkt **danach** genutzt.
|
||||
- Man kann CTEs **mehrfach referenzieren**, als wären es echte Tabellen.
|
||||
|
||||
---
|
||||
|
||||
## 2. Ein einfaches Beispiel
|
||||
|
||||
Stell dir vor, du hast eine Tabelle `bestellungen`:
|
||||
|
||||
- `id`
|
||||
- `kunde_id`
|
||||
- `betrag`
|
||||
- `datum`
|
||||
|
||||
Du möchtest:
|
||||
|
||||
1. Alle Bestellungen des Jahres 2024 herausfiltern.
|
||||
2. Dann auf Basis dieser gefilterten Daten den Gesamtumsatz pro Kunde berechnen.
|
||||
|
||||
Ohne CTE könnte man das mit einer verschachtelten Abfrage lösen; mit CTE sieht es lesbarer aus:
|
||||
|
||||
```sql
|
||||
WITH bestellungen_2024 AS (
|
||||
SELECT *
|
||||
FROM bestellungen
|
||||
WHERE datum >= '2024-01-01'
|
||||
AND datum < '2025-01-01'
|
||||
)
|
||||
SELECT
|
||||
kunde_id,
|
||||
SUM(betrag) AS umsatz_2024
|
||||
FROM bestellungen_2024
|
||||
GROUP BY kunde_id;
|
||||
```
|
||||
|
||||
Die CTE `bestellungen_2024` ist hier:
|
||||
„Alle Bestellungen aus 2024“, und wird anschließend in der Hauptabfrage verwendet.
|
||||
|
||||
---
|
||||
|
||||
## 3. Abgrenzung zu ähnlichen oder verwandten Begriffen
|
||||
|
||||
### 3.1 CTE vs. Unterabfrage (Subquery / Derived Table)
|
||||
|
||||
**Unterabfrage**: Eine Abfrage innerhalb einer anderen Abfrage, z. B.:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
kunde_id,
|
||||
SUM(betrag) AS umsatz_2024
|
||||
FROM (
|
||||
SELECT *
|
||||
FROM bestellungen
|
||||
WHERE datum >= '2024-01-01'
|
||||
AND datum < '2025-01-01'
|
||||
) AS b2024
|
||||
GROUP BY kunde_id;
|
||||
```
|
||||
|
||||
Unterschiede:
|
||||
|
||||
- **CTE**: Wird am Anfang mit `WITH` definiert, trägt einen Namen und kann **mehrfach** benutzt werden.
|
||||
- **Subquery**: Steht direkt im `FROM` oder `WHERE`, ist meist **anonymer** und schwerer zu lesen, vor allem bei komplexen Konstrukten.
|
||||
|
||||
Funktional können CTEs und Subqueries oft das Gleiche – CTEs verbessern eher **Struktur und Lesbarkeit**.
|
||||
|
||||
---
|
||||
|
||||
### 3.2 CTE vs. View (Sicht)
|
||||
|
||||
**View** (Sicht) ist wie eine **gespeicherte Abfrage** in der Datenbank:
|
||||
|
||||
```sql
|
||||
CREATE VIEW bestellungen_2024 AS
|
||||
SELECT *
|
||||
FROM bestellungen
|
||||
WHERE datum >= '2024-01-01'
|
||||
AND datum < '2025-01-01';
|
||||
```
|
||||
|
||||
Dann kannst du schreiben:
|
||||
|
||||
```sql
|
||||
SELECT kunde_id, SUM(betrag)
|
||||
FROM bestellungen_2024
|
||||
GROUP BY kunde_id;
|
||||
```
|
||||
|
||||
Unterschiede:
|
||||
|
||||
- **View**:
|
||||
- Wird **dauerhaft** in der Datenbank definiert.
|
||||
- Hat einen Namen, kann von **vielen Abfragen** und auch anderen Nutzer:innen verwendet werden.
|
||||
- Ändert sich nur über `CREATE OR REPLACE VIEW` / `ALTER VIEW`.
|
||||
- **CTE**:
|
||||
- Gilt **nur für eine einzige Abfrage**.
|
||||
- Ist daher ideal für **einmalige** oder **sehr spezialisierte** Zwischenschritte.
|
||||
- Erfordert keine Rechte zum Erstellen von Objekten in der Datenbank (kein `CREATE VIEW`).
|
||||
|
||||
Kurz:
|
||||
Views = dauerhafte, wiederverwendbare Bausteine.
|
||||
CTEs = temporäre, einmalige Bausteine innerhalb einer Abfrage.
|
||||
|
||||
---
|
||||
|
||||
### 3.3 CTE vs. temporäre Tabelle
|
||||
|
||||
Viele Datenbanken kennen **temporäre Tabellen**, z. B. `#temp_tab` in SQL Server oder `CREATE TEMP TABLE` in PostgreSQL:
|
||||
|
||||
```sql
|
||||
CREATE TEMP TABLE bestellungen_2024 AS
|
||||
SELECT *
|
||||
FROM bestellungen
|
||||
WHERE datum >= '2024-01-01'
|
||||
AND datum < '2025-01-01';
|
||||
|
||||
SELECT kunde_id, SUM(betrag)
|
||||
FROM bestellungen_2024
|
||||
GROUP BY kunde_id;
|
||||
```
|
||||
|
||||
Unterschiede:
|
||||
|
||||
- **Temporäre Tabelle**:
|
||||
- Wird physisch (zumindest logisch) in der Datenbank angelegt.
|
||||
- Existiert für die Dauer einer Session oder Transaktion.
|
||||
- Kann **indiziert** werden (Index hinzufügen) und so bei großen Datenmengen Performancevorteile bringen.
|
||||
- **CTE**:
|
||||
- Keine echte Tabelle, eher eine „logische Abfragekomponente“.
|
||||
- Keine eigenen Indexe.
|
||||
- Gilt nur innerhalb einer Abfrage.
|
||||
|
||||
---
|
||||
|
||||
### 3.4 CTE vs. Stored Procedure / Function
|
||||
|
||||
**Stored Procedures** und **Functions** sind Programmierbausteine auf Datenbankseite, z. B.:
|
||||
|
||||
- Prozeduren: führen mehrere Schritte, ggf. mit Kontrollstrukturen aus (IF, WHILE,…).
|
||||
- Funktionen: geben einen Wert oder eine Tabelle zurück.
|
||||
|
||||
Unterschiede:
|
||||
|
||||
- CTE ist **Teil einer einzelnen SQL-Select/Insert/Update/Delete-Abfrage**.
|
||||
- Stored Proc / Function ist **Code**, den man **speichert, versioniert und immer wieder aufrufen** kann.
|
||||
|
||||
CTEs können innerhalb von Stored Procedures verwendet werden – sie sind eher Bausteine **auf Abfrage-Ebene**, nicht auf Programm-Ebene.
|
||||
|
||||
---
|
||||
|
||||
## 4. Welche Probleme werden durch CTEs gelöst?
|
||||
|
||||
### 4.1 Bessere Lesbarkeit und Struktur
|
||||
|
||||
Statt eine riesige, komplexe Abfrage mit vielen verschachtelten Unterabfragen zu schreiben, kann man sich die Abfrage in **logische Schritte** zerlegen:
|
||||
|
||||
1. `WITH schritt1 AS (...)`
|
||||
2. `, schritt2 AS (...)`
|
||||
3. `SELECT ... FROM schritt2 ...`
|
||||
|
||||
Beispiel:
|
||||
Du möchtest erst „aktive Kunden“ bestimmen und dann nur deren Bestellungen summieren:
|
||||
|
||||
```sql
|
||||
WITH aktive_kunden AS (
|
||||
SELECT id, name
|
||||
FROM kunden
|
||||
WHERE status = 'aktiv'
|
||||
),
|
||||
bestellungen_aktive_kunden AS (
|
||||
SELECT
|
||||
b.kunde_id,
|
||||
SUM(b.betrag) AS umsatz
|
||||
FROM bestellungen b
|
||||
JOIN aktive_kunden k ON k.id = b.kunde_id
|
||||
GROUP BY b.kunde_id
|
||||
)
|
||||
SELECT
|
||||
k.name,
|
||||
bak.umsatz
|
||||
FROM bestellungen_aktive_kunden bak
|
||||
JOIN aktive_kunden k ON k.id = bak.kunde_id;
|
||||
```
|
||||
|
||||
Jeder CTE beschreibt einen **klaren Teilschritt**, das erleichtert Verstehen und Warten des Codes.
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Wiederverwendung innerhalb einer Abfrage
|
||||
|
||||
Oft brauchst du ein bestimmtes Zwischenergebnis **mehrmals in derselben Abfrage**.
|
||||
Ohne CTE müsstest du:
|
||||
|
||||
- Dieselbe Unterabfrage mehrfach schreiben (redundant, fehleranfällig)
|
||||
- Oder du machst Temp-Tabellen / Views, die aber mehr Setup erfordern.
|
||||
|
||||
Mit CTE definierst du das einmal und verwendest es mehrfach:
|
||||
|
||||
```sql
|
||||
WITH bestellungen_2024 AS (
|
||||
SELECT *
|
||||
FROM bestellungen
|
||||
WHERE datum >= '2024-01-01'
|
||||
AND datum < '2025-01-01'
|
||||
)
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM bestellungen_2024) AS anzahl_gesamt,
|
||||
(SELECT SUM(betrag) FROM bestellungen_2024) AS umsatz_gesamt;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Schrittweise Transformationen (ETL-artige Abläufe)
|
||||
|
||||
Man kann komplexe Datenverarbeitungen in **mehrere CTE-Schritte** aufteilen, z. B.:
|
||||
|
||||
1. Rohdaten bereinigen (ungültige Werte rausfiltern)
|
||||
2. Daten anreichern (Join mit Lookup-Tabellen)
|
||||
3. Aggregationen berechnen
|
||||
4. Ergebnis selektieren
|
||||
|
||||
Schema:
|
||||
|
||||
```sql
|
||||
WITH raw_data AS (
|
||||
SELECT * FROM import_tabelle
|
||||
),
|
||||
cleaned_data AS (
|
||||
SELECT *
|
||||
FROM raw_data
|
||||
WHERE wert IS NOT NULL
|
||||
),
|
||||
enriched_data AS (
|
||||
SELECT
|
||||
c.*,
|
||||
l.beschreibung
|
||||
FROM cleaned_data c
|
||||
LEFT JOIN lookup l ON c.code = l.code
|
||||
),
|
||||
aggregated AS (
|
||||
SELECT
|
||||
beschreibung,
|
||||
COUNT(*) AS anzahl,
|
||||
AVG(wert) AS durchschnitt
|
||||
FROM enriched_data
|
||||
GROUP BY beschreibung
|
||||
)
|
||||
SELECT *
|
||||
FROM aggregated
|
||||
ORDER BY anzahl DESC;
|
||||
```
|
||||
|
||||
Solche „linearen“ CTE-Ketten sind sehr hilfreich, um komplexe ETL-Schritte innerhalb einer SQL-Abfrage klar zu definieren.
|
||||
|
||||
---
|
||||
|
||||
### 4.4 Rekursive Abfragen (Hierarchien, Bäume, Graphen)
|
||||
|
||||
Ein **besonderer Typ** von CTE ist die **rekursive CTE**. Damit kann man Strukturen abfragen, die **hierarchisch** sind, z. B.:
|
||||
|
||||
- Mitarbeiter und ihre Vorgesetzten (Organigramm)
|
||||
- Kategorien und Unterkategorien (Baumstrukturen)
|
||||
- Stücklisten (Bauteil besteht aus Unterteilen, die wiederum Unterteile haben)
|
||||
- Graphen/Netzwerke mit Verbindungen
|
||||
|
||||
Grundidee:
|
||||
|
||||
- Es gibt eine **Anker-Abfrage** (Startpunkt).
|
||||
- Und eine **rekursive Abfrage**, die sich immer wieder selbst referenziert.
|
||||
|
||||
Beispiel: Mitarbeiter-Hierarchie
|
||||
Tabelle `mitarbeiter`:
|
||||
|
||||
- `id`
|
||||
- `name`
|
||||
- `chef_id` (Verweis auf `id` in derselben Tabelle, NULL für Chef ganz oben)
|
||||
|
||||
```sql
|
||||
WITH RECURSIVE hierarchie AS (
|
||||
-- 1) Anker: Chef (oberste Ebene)
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
chef_id,
|
||||
0 AS ebene
|
||||
FROM mitarbeiter
|
||||
WHERE chef_id IS NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- 2) Rekursiver Teil: alle Mitarbeiter, die einem bereits gefundenen Mitarbeiter unterstellt sind
|
||||
SELECT
|
||||
m.id,
|
||||
m.name,
|
||||
m.chef_id,
|
||||
h.ebene + 1 AS ebene
|
||||
FROM mitarbeiter m
|
||||
JOIN hierarchie h ON m.chef_id = h.id
|
||||
)
|
||||
SELECT *
|
||||
FROM hierarchie
|
||||
ORDER BY ebene, id;
|
||||
```
|
||||
|
||||
Ergebnis:
|
||||
Alle Mitarbeiter mit einer Spalte `ebene`, die angibt, wie weit sie vom obersten Chef entfernt sind.
|
||||
|
||||
**Ohne rekursive CTEs** wäre das sehr umständlich oder gar nicht (standardkonform) direkt in SQL möglich. Manche Systeme haben dafür eigene Syntax (z. B. `CONNECT BY` in Oracle), rekursive CTEs sind der **SQL-Standard**-Weg.
|
||||
|
||||
---
|
||||
|
||||
## 5. Weitere praxisnahe Beispiele
|
||||
|
||||
### 5.1 Alltagsszenario: „Top-Produkte pro Monat“
|
||||
|
||||
Gegeben:
|
||||
|
||||
- `verkaeufe` mit Spalten:
|
||||
- `produkt_id`
|
||||
- `datum`
|
||||
- `menge`
|
||||
- `umsatz`
|
||||
|
||||
Ziel:
|
||||
Pro Monat die **Top 3 Produkte nach Umsatz** anzeigen.
|
||||
|
||||
Ein möglicher Weg mit CTE:
|
||||
|
||||
```sql
|
||||
WITH monatliche_umsaetze AS (
|
||||
SELECT
|
||||
DATE_TRUNC('month', datum) AS monat,
|
||||
produkt_id,
|
||||
SUM(umsatz) AS umsatz_monat
|
||||
FROM verkaeufe
|
||||
GROUP BY DATE_TRUNC('month', datum), produkt_id
|
||||
),
|
||||
ranking AS (
|
||||
SELECT
|
||||
monat,
|
||||
produkt_id,
|
||||
umsatz_monat,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY monat
|
||||
ORDER BY umsatz_monat DESC
|
||||
) AS rang_im_monat
|
||||
FROM monatliche_umsaetze
|
||||
)
|
||||
SELECT
|
||||
monat,
|
||||
produkt_id,
|
||||
umsatz_monat,
|
||||
rang_im_monat
|
||||
FROM ranking
|
||||
WHERE rang_im_monat <= 3
|
||||
ORDER BY monat, rang_im_monat;
|
||||
```
|
||||
|
||||
Die Logik wird nachvollziehbar in zwei Schritte aufgeteilt:
|
||||
|
||||
1. summieren pro Monat und Produkt
|
||||
2. pro Monat ranken und Top 3 auswählen
|
||||
|
||||
---
|
||||
|
||||
### 5.2 Kalender-CTE: fehlende Tage auffüllen
|
||||
|
||||
Manchmal hat man z. B. nur Daten für Tage, an denen Verkäufe stattfanden, möchte aber **jeden Tag im Zeitraum** sehen (auch wenn Umsatz 0 ist).
|
||||
Mit einer rekursiven CTE kann man einen Kalender erzeugen.
|
||||
|
||||
Beispiel (PostgreSQL-Syntax):
|
||||
|
||||
```sql
|
||||
WITH RECURSIVE kalender AS (
|
||||
SELECT DATE '2024-01-01' AS tag
|
||||
UNION ALL
|
||||
SELECT tag + INTERVAL '1 day'
|
||||
FROM kalender
|
||||
WHERE tag < DATE '2024-01-31'
|
||||
)
|
||||
SELECT
|
||||
k.tag,
|
||||
COALESCE(SUM(v.umsatz), 0) AS umsatz
|
||||
FROM kalender k
|
||||
LEFT JOIN verkaeufe v ON DATE(v.datum) = k.tag
|
||||
GROUP BY k.tag
|
||||
ORDER BY k.tag;
|
||||
```
|
||||
|
||||
So erhältst du für **jeden Tag im Januar 2024** einen Umsatzwert – bei Tagen ohne Verkäufe eben 0.
|
||||
|
||||
---
|
||||
|
||||
## 6. Welche Herausforderungen und Stolpersteine gibt es?
|
||||
|
||||
### 6.1 Performance (Leistungsfähigkeit)
|
||||
|
||||
CTEs sind primär ein **Lesbarkeits-Feature**, aber sie können die Performance beeinflussen – je nach Datenbank:
|
||||
|
||||
- Einige Datenbanken „materialisieren“ CTEs:
|
||||
- Das Zwischenergebnis wird wirklich berechnet und zwischengespeichert.
|
||||
- Mehrfachzugriff ist dann ggf. schneller, aber die Initialberechnung kann teurer sein.
|
||||
- Andere optimieren CTEs ähnlich wie Unterabfragen:
|
||||
- Sie werden im Optimizer „eingefaltet“, d. h. wie eine direkte Teilabfrage behandelt.
|
||||
|
||||
Typische Punkte:
|
||||
|
||||
- **Mehrfache Verwendung großer CTEs**:
|
||||
- Wenn eine CTE sehr groß ist und du mehrfach darauf zugreifst, kann das teuer werden.
|
||||
- In manchen Systemen ist dann eine temporäre Tabelle mit Index effizienter.
|
||||
- **Rekursive CTEs**:
|
||||
- Können viel CPU und Zeit brauchen, wenn die Hierarchie sehr tief oder stark verzweigt ist.
|
||||
- Es gibt oft eine maximale Rekursionstiefe (z. B. 100 oder 32767), die begrenzt, wie tief die Rekursion geht.
|
||||
|
||||
Praxis-Tipp:
|
||||
|
||||
- CTEs sind super für mittlere Komplexität und moderate Datenmengen.
|
||||
- Bei sehr großen Datenmengen und Performanceproblemen:
|
||||
- Ausführungsplan anschauen.
|
||||
- Mit Temp-Tabellen, Indizes oder Views experimentieren.
|
||||
|
||||
---
|
||||
|
||||
### 6.2 Übermäßige Verschachtelung
|
||||
|
||||
Wenn man CTEs zu exzessiv nutzt, z. B. 20 oder mehr CTEs in einer Abfrage, wird es:
|
||||
|
||||
- schwer zu lesen
|
||||
- schwierig zu debuggen
|
||||
- komplex zu warten
|
||||
|
||||
Hier gilt:
|
||||
**Abstraktion, wo sinnvoll, aber nicht übertreiben.**
|
||||
|
||||
Manchmal ist es besser:
|
||||
|
||||
- Teile der Logik in eine View zu legen,
|
||||
- oder in eine Stored Procedure / Function,
|
||||
- oder die Logik in mehrere kleinere Abfragen aufzuteilen.
|
||||
|
||||
---
|
||||
|
||||
### 6.3 Datenbankspezifische Unterschiede
|
||||
|
||||
Nicht jede Datenbank:
|
||||
|
||||
- unterstützt CTEs gleich
|
||||
- oder hat genau dieselbe Syntax.
|
||||
|
||||
Beispiele:
|
||||
|
||||
- **PostgreSQL**: Unterstützt `WITH` und `WITH RECURSIVE`.
|
||||
- **SQL Server**: Unterstützt CTEs, Rekursion ohne extra `RECURSIVE`-Keyword.
|
||||
- **MySQL**: Ab Version 8.0 gibt es CTEs, rekursiv mit `WITH RECURSIVE`.
|
||||
- **Oracle**: Unterstützt CTEs, hatte aber historisch eigene Hierarchie-Syntax (`CONNECT BY`).
|
||||
|
||||
Wenn du in mehreren Systemen arbeitest, lohnt ein Blick in die Dokumentation der jeweiligen Datenbank.
|
||||
|
||||
---
|
||||
|
||||
### 6.4 CTEs haben keine eigenen Indizes
|
||||
|
||||
Da CTEs keine physischen Tabellen sind, kannst du:
|
||||
|
||||
- keine Indexe darauf erstellen
|
||||
- keine Statistiken direkt auf CTE-Ebene pflegen
|
||||
|
||||
D. h.:
|
||||
|
||||
- Der Optimizer muss die Abfrage so gut wie möglich auf Grundlage der zugrundeliegenden Tabellen optimieren.
|
||||
- Bei Performanceproblemen kann eine echte (temporäre) Tabelle mit Index manchmal besser sein.
|
||||
|
||||
---
|
||||
|
||||
## 7. Wann sollte man CTEs einsetzen? – Orientierung
|
||||
|
||||
**Geeignet für CTEs:**
|
||||
|
||||
- Wenn eine Abfrage **schwer lesbar** ist, weil sie viele Unterabfragen enthält.
|
||||
- Wenn du **dasselbe Zwischenergebnis mehrfach** in einer Abfrage benötigst.
|
||||
- Wenn du **schrittweise Transformationen** ausdrücken möchtest („Step 1, Step 2, …“).
|
||||
- Wenn du **Hierarchien** oder rekursive Strukturen mit SQL abbilden willst.
|
||||
|
||||
**Eher nicht ideal:**
|
||||
|
||||
- Für dauerhafte, von vielen Personen genutzte Logik → eher **Views** oder **Stored Procedures**.
|
||||
- Für sehr große Zwischenergebnisse, die oft wiederverwendet werden → evtl. **temporäre Tabellen** mit Index.
|
||||
- Wenn Performance schon kritisch ist → CTE-Einsatz testen und Ausführungspläne prüfen.
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
- **Definition**: CTE = „Common Table Expression“ = eine **temporäre, benannte Ergebnismenge** innerhalb einer Abfrage, definiert mit `WITH`.
|
||||
- **Nutzen**:
|
||||
- Klarere Struktur und bessere Lesbarkeit komplexer SQL-Abfragen.
|
||||
- Wiederverwendbarkeit eines Zwischenergebnisses innerhalb derselben Abfrage.
|
||||
- Möglichkeit, komplexe Transformationen sequenziell auszudrücken.
|
||||
- Unterstützung rekursiver Abfragen (Hierarchien/Bäume).
|
||||
- **Abgrenzung**:
|
||||
- Gegenüber Subqueries: funktional ähnlich, aber meist lesbarer.
|
||||
- Gegenüber Views: CTEs sind **temporär**, Views **dauerhaft**.
|
||||
- Gegenüber Temp-Tabellen: CTEs sind logisch, Temp-Tabellen physisch und indexierbar.
|
||||
- **Herausforderungen**:
|
||||
- Performanceabhängig vom Datenbanksystem und vom Aufbau der Abfrage.
|
||||
- Übermäßiger Gebrauch kann Abfragen unübersichtlich machen.
|
||||
- Rekursive CTEs können komplex und teuer werden, brauchen sorgfältige Bedingungen.
|
||||
@@ -0,0 +1,384 @@
|
||||
#datenbank
|
||||
➡️[[#Zusammenfassung]]
|
||||
|
||||
---
|
||||
MVCC („==Multi-Version Concurrency Control==“) ist ein Verfahren, mit dem Datenbanken gleichzeitige Zugriffe (Concurrency) von vielen Benutzern/Programmen steuern, ohne dass sie sich ständig gegenseitig blockieren.
|
||||
|
||||
Ich gehe Schritt für Schritt vor:
|
||||
|
||||
1. Ausgangspunkt: Was ist das Problem überhaupt?
|
||||
2. Grunddefinition von MVCC (für Einsteiger)
|
||||
3. Wie funktioniert das intern grob?
|
||||
4. Abgrenzung zu verwandten Konzepten
|
||||
5. Welche Probleme löst MVCC konkret?
|
||||
6. Welche Herausforderungen/Nachteile bringt MVCC mit sich?
|
||||
7. Praxisnahe Beispiele
|
||||
|
||||
---
|
||||
|
||||
## 1. Ausgangspunkt: Wozu brauche ich so etwas?
|
||||
|
||||
In einer typischen Anwendung greifen viele Nutzer gleichzeitig auf dieselbe Datenbank zu:
|
||||
|
||||
- Nutzer A liest Daten (z.B. Produktliste im Shop)
|
||||
- Nutzer B aktualisiert Daten (z.B. ändert Preise)
|
||||
- Nutzer C führt einen großen Report aus (z.B. tägliche Umsatzauswertung)
|
||||
|
||||
Ziel:
|
||||
Alle sollen gleichzeitig arbeiten können, **ohne Fehler** wie:
|
||||
|
||||
- „Dirty Reads“ (jemand liest halbfertige Änderungen)
|
||||
- Inkonsistente Sicht auf die Daten
|
||||
- Datenverlust, weil gleichzeitige Änderungen sich überschreiben
|
||||
- Dauerhafte Sperren und Deadlocks, die das System verlangsamen oder blockieren
|
||||
|
||||
Dazu gibt es das Konzept der **Transaktionen** (ACID):
|
||||
|
||||
- **A**tomicity: Alles oder nichts.
|
||||
- **C**onsistency: Regeln der Datenbank bleiben erfüllt.
|
||||
- **I**solation: Transaktionen beeinflussen sich möglichst wenig.
|
||||
- **D**urability: Einmal bestätigte Daten gehen nicht verloren.
|
||||
|
||||
Die Isolation ist das Kernfeld, in dem MVCC ins Spiel kommt:
|
||||
Wie sorgt man dafür, dass viele Transaktionen **gleichzeitig** laufen können, ohne sich ständig zu blockieren?
|
||||
|
||||
---
|
||||
|
||||
## 2. Grunddefinition von MVCC (für Einsteiger)
|
||||
|
||||
**MVCC (Multi-Version Concurrency Control)** bedeutet:
|
||||
|
||||
- **Jede Zeile (Row) in einer Tabelle existiert in mehreren Versionen.**
|
||||
- Jede Transaktion sieht die Daten „so, wie sie zu einem bestimmten Zeitpunkt waren“ – sie arbeitet mit einem **Snapshot** (Momentaufnahme).
|
||||
- Wenn jemand etwas **ändert**, wird **nicht** die existierende Zeile überschrieben, sondern eine **neue Version** der Zeile angelegt.
|
||||
- Für jede Transaktion gibt es Regeln, **welche Version** einer Zeile sie sehen darf.
|
||||
|
||||
Anschaulich:
|
||||
|
||||
- Stell dir eine Tabelle als Tabelle in Excel vor.
|
||||
- MVCC macht von einigen Zeilen „Kopien“, wenn sie geändert werden.
|
||||
- Du (deine Transaktion) siehst eine konsistente Version des Tabellenblatts zu dem Zeitpunkt, als du angefangen hast – auch wenn andere währenddessen schon neue Versionen erzeugt haben.
|
||||
|
||||
---
|
||||
|
||||
## 3. Wie funktioniert MVCC grob intern?
|
||||
|
||||
Vereinfachtes Modell (z.B. ähnlich wie in PostgreSQL/InnoDB):
|
||||
|
||||
### 3.1. Versionen von Zeilen
|
||||
|
||||
Statt einer Zeile gibt es:
|
||||
|
||||
- Version 1: erstellt von Transaktion T1
|
||||
- Version 2: später erstellt von Transaktion T2 (z.B. nach einem UPDATE)
|
||||
- Version 3: später erstellt von T3, usw.
|
||||
|
||||
Jede Version hat Metadaten, z.B.:
|
||||
|
||||
- „ab Transaktion X gültig“
|
||||
- „bis Transaktion Y gültig“
|
||||
(oder „gelöscht ab Transaktion Y“)
|
||||
|
||||
### 3.2. Was sieht eine Transaktion?
|
||||
|
||||
Wenn eine neue Transaktion T liest:
|
||||
|
||||
- Sie merkt sich einen „Zeitpunkt“ (z.B. eine Transaktions-ID oder Timestamp).
|
||||
- Beim Lesen prüft sie pro Zeile:
|
||||
|
||||
- Wurde diese Version **vor** meinem Start erzeugt und war zu dem Zeitpunkt schon committed?
|
||||
- Wurde sie danach erzeugt? Dann darf ich sie nicht sehen.
|
||||
- Ist sie bereits gelöscht oder von einer laufenden Transaktion verändert, die noch nicht committed ist? Dann gilt sie für mich ggf. als nicht existent.
|
||||
|
||||
Das Ergebnis:
|
||||
Du bekommst einen **konsistenten Snapshot** der Daten – so, als ob alle Änderungen nach deinem Start noch nicht existieren.
|
||||
|
||||
### 3.3. Schreiben mit MVCC
|
||||
|
||||
Beim **UPDATE** einer Zeile:
|
||||
|
||||
1. Alte Version bleibt im Speicher (zunächst noch sichtbar für alte Transaktionen).
|
||||
2. Neue Version wird erzeugt, mit „gültig ab Transaktion T“.
|
||||
3. Für neu startende Transaktionen ist **nach Commit** nur noch die neue Version relevant.
|
||||
|
||||
Beim **DELETE**:
|
||||
|
||||
- Die Zeile bekommt sozusagen ein „Todes-Zeitstempel“ (gültig bis T).
|
||||
- Alte Transaktionen sehen sie noch, neue nicht mehr.
|
||||
|
||||
### 3.4. Aufräumen („Vacuum“, Garbage Collection)
|
||||
|
||||
Alte Versionen, die **keine** Transaktion mehr sehen kann, sind Müll:
|
||||
|
||||
- Es gibt Prozess(e), die diese alten Versionen von Zeit zu Zeit **physisch entfernen**.
|
||||
- Beispiel-Begriff: in PostgreSQL „VACUUM“.
|
||||
|
||||
---
|
||||
|
||||
## 4. Abgrenzung zu verwandten Konzepten
|
||||
|
||||
### 4.1. MVCC vs. „klassische“ Sperren (Locking)
|
||||
|
||||
Früher (oder in einfachen Systemen):
|
||||
|
||||
- Jede Transaktion, die liest oder schreibt, setzt **Sperren** (Locks) auf Zeilen oder ganze Tabellen.
|
||||
- Wenn du eine Zeile liest, kann ein strenges System diese Zeile sperren, sodass andere sie nicht ändern dürfen, bis du fertig bist.
|
||||
- Das kann zu Blockaden und Deadlocks führen.
|
||||
|
||||
**MVCC-Ansatz:**
|
||||
|
||||
- Leser blockieren Schreiber **nicht** (oder nur minimal).
|
||||
- Schreiber blockieren Leser **nicht** (solange es nur um das Lesen bereits existierender, „alter“ Versionen geht).
|
||||
- Es gibt zwar weiterhin Sperren, aber sie sind oft nur für Konfliktfälle relevant (z.B. zwei Transaktionen, die **dieselbe** Zeile gleichzeitig ändern wollen).
|
||||
|
||||
MVCC ersetzt also nicht jede Form von Locking, sondern **reduziert** deren Notwendigkeit für Lesezugriffe stark.
|
||||
|
||||
### 4.2. MVCC vs. Snapshot Isolation
|
||||
|
||||
- **MVCC** ist ein **Mechanismus**: mehrere Versionen von Zeilen, Snapshots, Sichtbarkeitsregeln.
|
||||
- **Snapshot Isolation** ist eine **Isolationsebene** (ein bestimmter Garantiesatz für Transaktionen), die **auf MVCC aufbaut**.
|
||||
|
||||
Viele Systeme verwenden MVCC, um Snapshot Isolation umzusetzen:
|
||||
|
||||
- Jede Transaktion sieht den Datenbestand so, wie er beim Start (oder beim Start des Statements) war.
|
||||
- Aber: Snapshot Isolation ist oft **nicht vollständig serialisierbar** (siehe Write Skew Problem weiter unten).
|
||||
|
||||
Kurz:
|
||||
MVCC = Wie die Datenbank Versionen verwaltet.
|
||||
Snapshot Isolation = Welche Sicht und Garantien Transaktionen bekommen.
|
||||
|
||||
### 4.3. MVCC vs. Optimistic Concurrency Control (OCC)
|
||||
|
||||
- **Optimistic Concurrency Control** geht davon aus: Konflikte sind selten, man lässt Transaktionen frei operieren und prüft erst beim Commit, ob es Konflikte gab.
|
||||
- Viele MVCC-Systeme verwenden **Elemente von OCC**: Sie lassen Transaktionen auf einem Snapshot arbeiten und prüfen erst beim Commit, ob sie z.B. eine Zeile verändert haben, die inzwischen jemand anders schon geändert hat.
|
||||
|
||||
MVCC ist also kein direkter Gegensatz zu OCC, sondern eher eine Technik, mit der man OCC effizient implementieren kann.
|
||||
|
||||
---
|
||||
|
||||
## 5. Welche Probleme werden durch MVCC gelöst?
|
||||
|
||||
### 5.1. Leser blockieren Schreiber nicht (und umgekehrt)
|
||||
|
||||
**Problem ohne MVCC:**
|
||||
Großer Report läuft lange und sperrt viele Zeilen oder Tabellen. Schreibende Transaktionen können nicht mehr weiterarbeiten.
|
||||
|
||||
**Mit MVCC:**
|
||||
|
||||
- Der Report bekommt einen Snapshot.
|
||||
- Während der Report läuft:
|
||||
- Andere Transaktionen können neue Versionen schreiben.
|
||||
- Der Report liest weiterhin die alten Versionen, die für ihn gültig sind.
|
||||
- Ergebnis: lange lesende Transaktionen blockieren das System weit weniger.
|
||||
|
||||
**Praxisbeispiel:**
|
||||
|
||||
- Ein Online-Shop:
|
||||
- Ein Reporting-Job zählt alle Bestellungen des letzten Monats.
|
||||
- Gleichzeitig legen Kunden neue Bestellungen an.
|
||||
- Mit MVCC:
|
||||
- Reporting-Job sieht einen stabilen Stand („Stand 10:00 Uhr“).
|
||||
- Kunden können weiter bestellen; diese Bestellungen erscheinen evtl. nicht im aktuellen Report, aber im nächsten.
|
||||
|
||||
### 5.2. Konsistente Lese-Sichten
|
||||
|
||||
Lesende Transaktionen sehen eine **konsistente Momentaufnahme**, nicht einen Mix aus „halb alt, halb neu“.
|
||||
|
||||
Beispiel:
|
||||
|
||||
- Du willst sicherstellen, dass Summe aller Kontosalden = 0 (Bilanz).
|
||||
- Ohne MVCC könntest du beim Lesen ein Konto schon nach der neuen Buchung sehen, ein anderes aber noch vor der Buchung – Ergebnis: inkonsistente Summe.
|
||||
- Mit MVCC liest du alles so, wie es zu deinem Startzeitpunkt war.
|
||||
|
||||
### 5.3. Hohe Skalierbarkeit für viele Leser
|
||||
|
||||
MVCC ist besonders stark in Systemen mit:
|
||||
|
||||
- hohem Leseanteil (Reports, Dashboards, APIs)
|
||||
- vielen parallelen Nutzern
|
||||
|
||||
Weil Leser nicht permanent Schreiblocks blockieren, kann das System mehr parallele Anfragen verarbeiten.
|
||||
|
||||
### 5.4. Reduzierte Deadlocks
|
||||
|
||||
Deadlocks entstehen oft, wenn zwei Transaktionen sich gegenseitig Sperren wegnehmen wollen.
|
||||
Da MVCC Leser weitgehend von Locks entkoppelt, reduziert sich die Deadlock-Wahrscheinlichkeit, insbesondere bei reinen Lesezugriffen.
|
||||
|
||||
---
|
||||
|
||||
## 6. Welche Herausforderungen und Nachteile hat MVCC?
|
||||
|
||||
### 6.1. Speicher-Overhead durch viele Versionen
|
||||
|
||||
Da alte Versionen zunächst liegen bleiben, kann es passieren:
|
||||
|
||||
- Tabellen werden „aufgebläht“ (Bloat).
|
||||
- Disk-Verbrauch steigt, Indexe werden größer, Performance sinkt.
|
||||
|
||||
Darum braucht man:
|
||||
|
||||
- Mechanismen zur Bereinigung (VACUUM, Garbage Collection)
|
||||
- Tuning (z.B. wie aggressiv wird gereinigt?)
|
||||
|
||||
**Praxisproblem:**
|
||||
Ein System mit dauernd vielen Updates/Deletes, aber schlecht eingestelltem Vacuum, kann plötzlich deutlich langsamer werden, weil die Tabellen intern riesig sind.
|
||||
|
||||
### 6.2. Lang laufende Transaktionen halten alte Versionen fest
|
||||
|
||||
Wenn eine Transaktion sehr lange offen bleibt (z.B. ein Report, der 2 Stunden läuft):
|
||||
|
||||
- Alle Versionen, die **für diese Transaktion sichtbar sein könnten**, müssen erhalten bleiben.
|
||||
- Die Datenbank kann diese Versionen nicht löschen, weil sie noch gebraucht werden.
|
||||
- Folge: Bloat, mehr Speicherverbrauch, mehr I/O, langsamere Zugriffe.
|
||||
|
||||
Praxisnahe Konsequenz:
|
||||
|
||||
- Man versucht in produktiven Systemen:
|
||||
- Lange Transaktionen zu vermeiden oder
|
||||
- Sie auf Replikas/Read-Only-Kopien auszulagern.
|
||||
|
||||
### 6.3. Anomalien unter Snapshot Isolation (Write Skew)
|
||||
|
||||
Snapshot Isolation (häufig via MVCC umgesetzt) ist oft **nicht vollständig serialisierbar**.
|
||||
|
||||
Beispiel (vereinfacht):
|
||||
|
||||
- Zwei Ärzte müssen gleichzeitig im Krankenhaus Dienst haben.
|
||||
- Regel: Es dürfen nie **beide** Ärzte gleichzeitig „Off-Duty“ sein.
|
||||
- Tabelle `dienstplan` mit Einträgen pro Arzt.
|
||||
|
||||
Transaktion T1:
|
||||
|
||||
```sql
|
||||
BEGIN;
|
||||
SELECT COUNT(*) FROM dienstplan WHERE on_duty = TRUE;
|
||||
-- Ergebnis: 2 (Arzt A und B sind im Dienst)
|
||||
|
||||
-- T1 setzt Arzt A auf off-duty
|
||||
UPDATE dienstplan SET on_duty = FALSE WHERE arzt = 'A';
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
Transaktion T2 (parallel, mit eigenem Snapshot):
|
||||
|
||||
```sql
|
||||
BEGIN;
|
||||
SELECT COUNT(*) FROM dienstplan WHERE on_duty = TRUE;
|
||||
-- Ergebnis: 2 (sieht noch alten Stand, beide on duty)
|
||||
|
||||
-- T2 setzt Arzt B auf off-duty
|
||||
UPDATE dienstplan SET on_duty = FALSE WHERE arzt = 'B';
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
Beide Transaktionen sehen **denselben alten Stand** („2 Ärzte im Dienst“).
|
||||
Beide halten es für zulässig, je einen Arzt auf off-duty zu setzen.
|
||||
Ergebnis nach beiden Commits: **0 Ärzte im Dienst**, Regel verletzt.
|
||||
|
||||
Das ist ein typischer **Write Skew** – möglich unter Snapshot Isolation, obwohl MVCC sauber arbeitet.
|
||||
|
||||
Lösung:
|
||||
|
||||
- Strengere Isolationsebene: **SERIALIZABLE** (die DB verhindert durch zusätzliche Checks solche Fälle),
|
||||
oder
|
||||
- Explizite Sperren / zusätzliche Constraints durch den Entwickler.
|
||||
|
||||
### 6.4. Komplexere Implementierung und Tuning
|
||||
|
||||
MVCC ist intern relativ komplex:
|
||||
|
||||
- Verwaltung von Versionen
|
||||
- Sichtbarkeitslogik
|
||||
- Garbage Collection
|
||||
- Umgang mit Hot-Spot-Tabellen (sehr häufig aktualisierte Zeilen)
|
||||
|
||||
Für Administratoren/Entwickler bedeutet das:
|
||||
|
||||
- Man muss verstehen, wie die eigene DB MVCC implementiert (PostgreSQL, MySQL InnoDB, Oracle, SQL Server etc. machen es leicht unterschiedlich).
|
||||
- Man muss Parameter fürs Aufräumen und für Isolationsebenen sinnvoll setzen.
|
||||
|
||||
---
|
||||
|
||||
## 7. Praxisnahe Beispiele
|
||||
|
||||
### Beispiel 1: Webshop – Produktpreise ändern
|
||||
|
||||
Situation:
|
||||
|
||||
- Tabelle `produkte` mit Spalte `preis`.
|
||||
- Viele Nutzer sehen gleichzeitig das Produkt im Shop.
|
||||
- Ein Admin ändert den Preis.
|
||||
|
||||
Ohne MVCC (vereinfachte, pessimistische Sperrung):
|
||||
|
||||
- Admin setzt Lock auf Zeile/Produkt.
|
||||
- Solange Admin noch nicht committed hat:
|
||||
- Kunden könnten blockiert werden oder
|
||||
- sie sehen unklare Zwischenzustände.
|
||||
|
||||
Mit MVCC:
|
||||
|
||||
- Admin-Transaktion erzeugt eine **neue Version** der Produktzeile mit neuem Preis.
|
||||
- Kunden, die vor dem Commit lesen:
|
||||
- sehen die **alte Version** (alten Preis).
|
||||
- Kunden, die nach dem Commit lesen:
|
||||
- sehen die **neue Version** (neuen Preis).
|
||||
- Es gibt keine Lese-Blockade während der Preisänderung.
|
||||
|
||||
### Beispiel 2: Reporting vs. Online-Transaktionen
|
||||
|
||||
- Ein Finance-Report soll **Stand Tagesende** alle Buchungen auswerten.
|
||||
- Parallel buchen die Nutzer weiter.
|
||||
|
||||
Mit MVCC:
|
||||
|
||||
- Report bekommt einen Snapshot (z.B. Stand 23:59).
|
||||
- Er arbeitet vielleicht 30 Minuten oder länger.
|
||||
- Währenddessen können Buchungen von 00:00–00:30 weiter eingehen.
|
||||
- Report sieht einen stabilen Zustand; neue Buchungen tauchen erst im nächsten Report auf.
|
||||
|
||||
Das ist ideal für:
|
||||
|
||||
- Data Warehouse Light
|
||||
- tägliche Berichte
|
||||
- Audits (reproduzierbare Sicht)
|
||||
|
||||
### Beispiel 3: Problemfall lange Transaktion
|
||||
|
||||
- Ein Entwickler startet in der Entwicklungsumgebung eine Transaktion:
|
||||
|
||||
```sql
|
||||
BEGIN;
|
||||
SELECT * FROM grosse_tabelle; -- dauert lange
|
||||
-- Entwickler vergisst COMMIT oder ROLLBACK
|
||||
```
|
||||
|
||||
- Diese Transaktion bleibt offen.
|
||||
- Alle Versionen, die seit Beginn dieser Transaktion geändert wurden, müssen aufgehoben werden.
|
||||
- Vacuum/Garbage Collector kann viele alte Zeilen nicht löschen.
|
||||
- Nach Stunden/Tagen:
|
||||
- Tabellen aufgebläht,
|
||||
- Performance sinkt,
|
||||
- Admin muss offene Sessions identifizieren und beenden.
|
||||
|
||||
Lernpunkt:
|
||||
Mit MVCC muss man auf **offene, vergessene Transaktionen** achten.
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
- **MVCC** bedeutet: Zeilen werden nicht einfach überschrieben, sondern es gibt mehrere Versionen derselben Zeile.
|
||||
- Leser und Schreiber können gleichzeitig arbeiten:
|
||||
- Leser sehen einen konsistenten Snapshot,
|
||||
- Schreiber erzeugen neue Versionen, ohne Leser direkt zu blockieren.
|
||||
- MVCC ist der Mechanismus, auf dem Isolationsebenen wie **Snapshot Isolation** aufbauen.
|
||||
- Vorteile:
|
||||
- Hohe Parallelität,
|
||||
- Weniger Sperrkonflikte,
|
||||
- Konsistente Sichten für Reports.
|
||||
- Nachteile/Herausforderungen:
|
||||
- Mehr Speicherbedarf durch alte Versionen,
|
||||
- Notwendigkeit von Aufräumprozessen,
|
||||
- Vorsicht bei langen Transaktionen,
|
||||
- bestimmte Anomalien (z.B. Write Skew) bei Snapshot Isolation.
|
||||
@@ -0,0 +1,360 @@
|
||||
#datenbank
|
||||
|
||||
➡️ [[#Zusammenfassung]]
|
||||
|
||||
---
|
||||
### 1. Grundidee: Was ist eine Materialized View?
|
||||
|
||||
Eine **Materialized View** (materialisierte Sicht) ist vereinfacht gesagt:
|
||||
|
||||
> **Eine gespeicherte Ergebnistabelle einer Abfrage**, die regelmäßig aktualisiert wird.
|
||||
|
||||
Im Unterschied zu einer „normalen“ View, die bei jedem Zugriff die zugrunde liegende Abfrage neu ausführt, werden bei einer Materialized View:
|
||||
|
||||
- die Daten der Abfrage **physisch gespeichert** (wie in einer Tabelle),
|
||||
- und später **wiederverwendet**, ohne jedes Mal die komplette Abfrage neu berechnen zu müssen.
|
||||
|
||||
Du kannst dir das vorstellen wie einen **vorgeberechneten Bericht**, der als echte Tabelle im System liegt, aber technisch aus einer oder mehreren anderen Tabellen abgeleitet ist.
|
||||
|
||||
Typische Verwendung:
|
||||
- Große Tabellen
|
||||
- Aufwendige Joins
|
||||
- Aggregationen (SUM, COUNT, AVG, …)
|
||||
- Reporting & Analytics
|
||||
|
||||
---
|
||||
|
||||
### 2. Abgrenzung zu verwandten Begriffen
|
||||
|
||||
#### 2.1 Normale View vs. Materialized View
|
||||
|
||||
**View (logische Sicht)**:
|
||||
- Ist nur eine **gespeicherte Abfrage**.
|
||||
- Speichert selbst **keine Daten**.
|
||||
- Beim SELECT auf die View wird die zugrunde liegende Abfrage jedes Mal **neu ausgeführt**.
|
||||
- Vorteil: Immer **aktuell**, kein Speicheraufwand.
|
||||
- Nachteil: Kann bei komplexen Abfragen **langsam** sein.
|
||||
|
||||
**Materialized View**:
|
||||
- Ist eine **physisch gespeicherte Tabelle**, die aus einer Abfrage berechnet wurde.
|
||||
- Beim SELECT werden die **vorgehaltenen Daten** gelesen (schnell).
|
||||
- Muss **explizit aktualisiert** („refreshed“) werden.
|
||||
- Vorteil: Schnelle Abfragen, vor allem bei komplexen Berechnungen.
|
||||
- Nachteil: Daten können **veraltet** sein, zusätzlicher Speicher & Wartung.
|
||||
|
||||
#### 2.2 Materialized View vs. Tabelle
|
||||
|
||||
**Normale Tabelle**:
|
||||
- Daten werden direkt in diese Tabelle geschrieben (INSERT, UPDATE, DELETE).
|
||||
- Struktur (Schema) ist unabhängig; die Tabelle „gehört sich selbst“.
|
||||
|
||||
**Materialized View**:
|
||||
- Wird **aus anderen Tabellen abgeleitet** (Definition durch SELECT).
|
||||
- Man schreibt normalerweise **nicht direkt** hinein, sondern nur in die Basistabellen.
|
||||
- Die Materialized View wird über einen **Refresh-Mechanismus** aktualisiert.
|
||||
|
||||
#### 2.3 Materialized View vs. Cache
|
||||
|
||||
Ein **Cache** (z. B. im Application Server):
|
||||
- Hält Daten im Speicher, typischerweise kurzfristig.
|
||||
- Wird meist von der Anwendung gesteuert.
|
||||
- Ist oft flüchtig (z. B. bei Neustart weg).
|
||||
|
||||
**Materialized View**:
|
||||
- Ist Teil der Datenbank, persistent auf Disk.
|
||||
- Wird durch die Datenbank verwaltet.
|
||||
- Kann komplexere Konsistenz- und Refresh-Strategien nutzen.
|
||||
|
||||
#### 2.4 Materialized View vs. Index
|
||||
|
||||
**Index**:
|
||||
- Beschleunigt den Zugriff auf bestehende Daten einer Tabelle.
|
||||
- Speichert typischerweise Schlüsselwerte und Zeiger auf die Tabelle.
|
||||
- Berechnet **keine neuen Inhalte**, sondern hilft, vorhandene schneller zu finden.
|
||||
|
||||
**Materialized View**:
|
||||
- Enthält **eigene Daten**, die Ergebnis einer Abfrage sind (z. B. Summen, Gruppierungen).
|
||||
- Kann zusätzlich selbst Indexe haben.
|
||||
|
||||
#### 2.5 Datenbanken und Begriffe
|
||||
|
||||
Verschiedene Systeme nutzen leicht unterschiedliche Begriffe:
|
||||
|
||||
- **PostgreSQL**: `MATERIALIZED VIEW`
|
||||
- **Oracle**: `MATERIALIZED VIEW` (sehr ausgereifte Funktionen)
|
||||
- **SQL Server**: Kein direkter Begriff, aber **Indexed Views** sind sehr ähnlich
|
||||
- **MySQL**: Keine echte Materialized View, aber man kann das Verhalten nachbauen (z. B. mit Triggern, geplanten Jobs, Tabellen)
|
||||
|
||||
---
|
||||
|
||||
### 3. Welche Probleme werden durch Materialized Views gelöst?
|
||||
|
||||
#### 3.1 Performance bei komplexen Abfragen
|
||||
|
||||
Wenn du häufig dieselbe **komplexe Abfrage** ausführst, kostet das jedes Mal viel Rechenzeit:
|
||||
|
||||
- Viele Joins über große Tabellen
|
||||
- Aggregationen (SUM, COUNT, MAX, …)
|
||||
- Filter auf komplizierten Kombinationen
|
||||
|
||||
Eine Materialized View **berechnet diese Abfrage einmal** (oder in festen Intervallen) und speichert das Ergebnis. Spätere Abfragen greifen nur noch auf die **fertige Ergebnismenge** zu.
|
||||
|
||||
**Beispiel:**
|
||||
|
||||
Du hast einen Online-Shop mit Tabellen:
|
||||
|
||||
- `orders` (Bestellungen)
|
||||
- `order_items` (Bestellpositionen)
|
||||
- `products` (Produkte)
|
||||
- `customers` (Kunden)
|
||||
|
||||
Du willst regelmäßig wissen: **Wie viel Umsatz pro Kunde pro Monat?**
|
||||
|
||||
Ohne Materialized View:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
c.customer_id,
|
||||
date_trunc('month', o.order_date) AS month,
|
||||
SUM(oi.quantity * oi.unit_price) AS revenue
|
||||
FROM customers c
|
||||
JOIN orders o ON o.customer_id = c.customer_id
|
||||
JOIN order_items oi ON oi.order_id = o.order_id
|
||||
GROUP BY c.customer_id, date_trunc('month', o.order_date);
|
||||
```
|
||||
|
||||
Diese Abfrage kann bei Millionen Zeilen **sehr langsam** sein, besonders wenn sie viele Nutzer gleichzeitig ausführen.
|
||||
|
||||
Mit Materialized View:
|
||||
|
||||
```sql
|
||||
CREATE MATERIALIZED VIEW mv_customer_monthly_revenue AS
|
||||
SELECT
|
||||
c.customer_id,
|
||||
date_trunc('month', o.order_date) AS month,
|
||||
SUM(oi.quantity * oi.unit_price) AS revenue
|
||||
FROM customers c
|
||||
JOIN orders o ON o.customer_id = c.customer_id
|
||||
JOIN order_items oi ON oi.order_id = o.order_id
|
||||
GROUP BY c.customer_id, date_trunc('month', o.order_date);
|
||||
```
|
||||
|
||||
Einfache Abfrage darauf:
|
||||
|
||||
```sql
|
||||
SELECT * FROM mv_customer_monthly_revenue
|
||||
WHERE month = '2025-01-01';
|
||||
```
|
||||
|
||||
Das ist meist **sehr schnell**, da nur noch fertige Zeilen gelesen werden.
|
||||
|
||||
#### 3.2 Entlastung der Primärtables
|
||||
|
||||
Wenn viele Nutzer komplexe Analysen auf den operativen Tabellen ausführen, kann das:
|
||||
|
||||
- den Datenbankserver stark belasten,
|
||||
- Transaktionen verlangsamen,
|
||||
- das Online-System (z. B. Shop) spürbar ausbremsen.
|
||||
|
||||
Materialized Views dienen hier als eine Art **vorgefertigtes Reporting-Layer**, das:
|
||||
|
||||
- weniger Schreiboperationen hat,
|
||||
- stark komprimiert / aggregiert sein kann,
|
||||
- unabhängig indexiert werden kann.
|
||||
|
||||
#### 3.3 Zugriff auf entfernte Daten (z. B. Data Warehouse)
|
||||
|
||||
In manchen Systemen kann eine Materialized View Daten aus **anderen Datenbanken** oder **externen Quellen** einbinden. Damit können z. B.:
|
||||
|
||||
- Daten aus mehreren Systemen
|
||||
- in einer lokal gespeicherten, performanten Sicht
|
||||
zusammengefasst werden.
|
||||
|
||||
---
|
||||
|
||||
### 4. Welche Herausforderungen und Nachteile gibt es?
|
||||
|
||||
#### 4.1 Datenaktualität (Freshness)
|
||||
|
||||
Materialized Views sind **nicht automatisch immer aktuell**.
|
||||
|
||||
Typische Arten des Refresh:
|
||||
|
||||
1. **Maneller Refresh**
|
||||
Du rufst selbst etwas auf wie:
|
||||
|
||||
```sql
|
||||
REFRESH MATERIALIZED VIEW mv_customer_monthly_revenue;
|
||||
```
|
||||
|
||||
2. **Geplanter Refresh (z. B. jede Stunde/Tageswechsel)**
|
||||
Über Scheduler/Jobs: „Führe jede Nacht um 3 Uhr einen Refresh aus“.
|
||||
|
||||
3. **On Commit / nahezu in Echtzeit** (je nach DB)
|
||||
Bei bestimmten Systemen können Materialized Views nach Änderungen an den Basistabellen automatisch aktualisiert werden.
|
||||
|
||||
**Konsequenz:**
|
||||
Es gibt immer einen Trade-off zwischen:
|
||||
|
||||
- **Aktualität** (häufig refresht → näher an „Echtzeit“)
|
||||
- **Performance/Belastung** (jeder Refresh ist teils teuer)
|
||||
|
||||
#### 4.2 Konsistenz und Komplexität
|
||||
|
||||
Bei vielen Materialized Views, die sich überlappen oder von anderen Views abhängen, kann es kompliziert werden:
|
||||
|
||||
- In welcher Reihenfolge refresht man?
|
||||
- Was passiert, wenn eine Quelle fehlerhaft ist?
|
||||
- Wie geht man mit teilweisen Refreshes um?
|
||||
|
||||
#### 4.3 Speicherbedarf
|
||||
|
||||
Materialized Views brauchen **zusätzlichen Speicher**, da sie Daten duplizieren:
|
||||
|
||||
- Daten sind in den Basistabellen vorhanden
|
||||
- plus noch einmal in den Materialized Views
|
||||
|
||||
Je nach Anzahl und Detailgrad kann das signifikant sein.
|
||||
|
||||
#### 4.4 Schreibaufwand auf Basistabellen (indirekt)
|
||||
|
||||
Wenn Materialized Views sehr häufig aktualisiert werden:
|
||||
|
||||
- können Inserts/Updates auf Basistabellen indirekt langsamer werden,
|
||||
- weil der Refresh-Prozess Ressourcen frisst (z. B. CPU, I/O, Locks).
|
||||
|
||||
Bei Systemen mit „Refresh on Commit“ müssen evtl. zusätzliche Metadaten gepflegt werden, um Änderungen nachvollziehbar zu machen.
|
||||
|
||||
#### 4.5 Komplexität im Design und Betrieb
|
||||
|
||||
- Man muss Entscheidungen treffen:
|
||||
- Welche Abfragen lohnen sich als Materialized View?
|
||||
- Wie oft sollten sie aktualisiert werden?
|
||||
- Wer ist verantwortlich für Monitoring und Fehlerbehandlung?
|
||||
|
||||
- Bei Änderungen am Schema (z. B. Spalten hinzufügen) muss oft:
|
||||
- die Materialized View angepasst
|
||||
- oder neu aufgebaut werden.
|
||||
|
||||
---
|
||||
|
||||
### 5. Praxisnahe Beispiele
|
||||
|
||||
#### 5.1 Reporting im E-Commerce
|
||||
|
||||
Stell dir einen Online-Shop vor, der täglich Tausende Bestellungen hat. Das Management möchte im Dashboard sehen:
|
||||
|
||||
- Umsatz pro Tag
|
||||
- Top-10-Produkte pro Woche
|
||||
- Anzahl neuer Kunden pro Monat
|
||||
|
||||
Ohne Materialized Views würden diese Reports bei jedem Aufruf heavy Queries auf großen Tabellen ausführen.
|
||||
|
||||
Mit Materialized Views:
|
||||
|
||||
1. `mv_daily_revenue`
|
||||
Umsätze pro Tag
|
||||
|
||||
```sql
|
||||
CREATE MATERIALIZED VIEW mv_daily_revenue AS
|
||||
SELECT
|
||||
date_trunc('day', o.order_date) AS day,
|
||||
SUM(oi.quantity * oi.unit_price) AS revenue
|
||||
FROM orders o
|
||||
JOIN order_items oi ON oi.order_id = o.order_id
|
||||
GROUP BY date_trunc('day', o.order_date);
|
||||
```
|
||||
|
||||
2. `mv_weekly_top_products`
|
||||
Top-Produkte je Woche
|
||||
|
||||
```sql
|
||||
CREATE MATERIALIZED VIEW mv_weekly_top_products AS
|
||||
SELECT
|
||||
date_trunc('week', o.order_date) AS week,
|
||||
oi.product_id,
|
||||
SUM(oi.quantity) AS total_quantity
|
||||
FROM orders o
|
||||
JOIN order_items oi ON oi.order_id = o.order_id
|
||||
GROUP BY date_trunc('week', o.order_date), oi.product_id;
|
||||
```
|
||||
|
||||
3. `mv_monthly_new_customers`
|
||||
Neue Kunden pro Monat
|
||||
|
||||
```sql
|
||||
CREATE MATERIALIZED VIEW mv_monthly_new_customers AS
|
||||
SELECT
|
||||
date_trunc('month', c.created_at) AS month,
|
||||
COUNT(*) AS new_customers
|
||||
FROM customers c
|
||||
GROUP BY date_trunc('month', c.created_at);
|
||||
```
|
||||
|
||||
Diese Views kannst du z. B. **jede Nacht** aktualisieren, da sich historische Daten nicht mehr ändern.
|
||||
|
||||
#### 5.2 Data Warehouse / BI
|
||||
|
||||
In einem Data Warehouse gibt es oft:
|
||||
|
||||
- große „Faktentabellen“ (z. B. `fact_sales` mit hunderten Millionen Zeilen),
|
||||
- Dimensionstabellen (Kunde, Produkt, Region).
|
||||
|
||||
Materialized Views können hier als **„Summary Tables“** dienen, z. B.:
|
||||
|
||||
- Verkäufe pro Region und Monat
|
||||
- Verkäufe pro Produktkategorie und Quartal
|
||||
|
||||
Analysten müssen dann nicht mehr auf die komplette Faktentabelle zugreifen, sondern nur noch auf relativ kleine, aggregierte Materialized Views.
|
||||
|
||||
#### 5.3 Teilweise Aktualisierung (Incremental Refresh)
|
||||
|
||||
Fortgeschrittene Systeme (z. B. Oracle) können Materialized Views **inkrementell** aktualisieren: Es werden nur die Änderungen seit dem letzten Stand eingerechnet, statt alles komplett neu zu berechnen. Das spart enorm Zeit und Ressourcen, ist aber vom Setup her komplexer.
|
||||
|
||||
---
|
||||
|
||||
### 6. Typische Strategien und Best Practices
|
||||
|
||||
1. **Nur für „schwere“ Abfragen nutzen**
|
||||
Materialized Views lohnen sich insbesondere, wenn:
|
||||
- die Abfrage sehr teuer ist,
|
||||
- die Daten sich nicht „jede Sekunde“ ändern,
|
||||
- die Ergebnisse häufig angefragt werden.
|
||||
|
||||
2. **Refresh-Frequenz bewusst wählen**
|
||||
- Nahe Echtzeit notwendig? → Häufigerer Refresh, mehr Last
|
||||
- Reine Reports (z. B. täglich) → nächtlicher Refresh reicht oft
|
||||
|
||||
3. **Materialized Views benennen und dokumentieren**
|
||||
- Sinnvolle Namen (`mv_...`)
|
||||
- Dokumentation: Welche Abfrage? Wie wird refresht? Wer nutzt sie?
|
||||
|
||||
4. **Indexe auf Materialized Views setzen**
|
||||
- Genau wie bei normalen Tabellen können Indexe Abfragen weiter beschleunigen:
|
||||
```sql
|
||||
CREATE INDEX idx_mv_daily_revenue_day
|
||||
ON mv_daily_revenue (day);
|
||||
```
|
||||
|
||||
5. **Monitoring**
|
||||
- Refresh-Zeiten messen
|
||||
- Fehler beim Refresh protokollieren
|
||||
- Speicherverbrauch im Blick behalten
|
||||
|
||||
---
|
||||
|
||||
### Zusammenfassung
|
||||
|
||||
- **Materialized Views** sind gespeicherte (materialisierte) Ergebnisse von Abfragen, die wie Tabellen genutzt werden können.
|
||||
- Sie dienen vor allem zur **Performance-Steigerung** bei komplexen oder häufig ausgeführten Abfragen und zur **Entlastung** der operativen Tabellen.
|
||||
- Gegenüber normalen Views:
|
||||
- **schneller** beim Lesen,
|
||||
- aber **nicht automatisch aktuell** – sie müssen refresht werden.
|
||||
- Herausforderungen:
|
||||
- Datenaktualität (wie oft refresht man?),
|
||||
- zusätzlicher Speicherverbrauch,
|
||||
- Komplexität in Design und Wartung.
|
||||
- Typische Einsatzgebiete:
|
||||
- Reporting, Analytics, Dashboards,
|
||||
- Data Warehousing,
|
||||
- Aggregationen über große Datenmengen.
|
||||
Reference in New Issue
Block a user