add synced notes on IT know how

This commit is contained in:
Mathias Schneider
2026-03-17 19:01:50 +01:00
parent b8797d5ca8
commit fc3aef4da8
31 changed files with 13162 additions and 1 deletions
@@ -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.
+549
View File
@@ -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.
+384
View File
@@ -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:0000: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.