Bedingung | Beschreibung |
---|---|
Atomicity | - Transaktion ist kleinste, atomare Ausführungseinheit - Entweder alle Änderungen durch Transaktion in der Datenbasis werden wirksam oder gar keine |
Consistency | - Transaktion überführt Datenbank von konsistentem Zustand in anderen konsistenten Zustand - (Inkonsistenzen innerhalb Transaktionen erlaubt) |
Isolation | - Transaktion ist gegenüber anderen Transaktionen isoliert - Ergebnis einer Transaktion kann nicht direkt durch andere Transaktionen beeinflusst werden - Jede Transaktion wird logisch so ausgeführt, als gäbe es keine andere Transaktion |
Durability | - Wirkung einer Transaktion auf Datenbasis bleibt nach erfolgreichem Abschluss dauerhaft erhalten - auch bei Systemfehlern |
Operation | Beschreibung |
---|---|
.bot() | Initialisierung (wird oft nicht explizit genutzt) |
.r(a) .read(a, var_A) | Lesen von Objekt a und Übergabe an Programmvariable A |
.w(b) .write(b, var_B) | Schreiben von Programmvariable B in Objekt b |
.a() .abort() | Abbruch der Transaktion (alle Änderungen werden unwirksam) |
.c() .commit() | Erfolgreiche Beendigung der Transaktion (Änderungen werden geschrieben) |
mit Ordnungsrelation H:
auch innerhalb einzelner Transaktionen:
Das Gehalt ist zwar eingegangen, aber der neue Kontostand mit Gehalt ist zwischenzeitlich verloren gegangen.
Veränderungen können zwischenzeitlich Inkonsistenzen erzeugen, die jedoch bereinigt werden. Ohne entsprechende Isolation werden jedoch andere Transaktionen beeinflusst.
Für zwei Konten und
Transaktion T1 gleicht mit aus
Transaktion T2 zahlt 100€ von aus
Das zwischenzeitlich erzeugte Guthaben von T1 konnte T2 nutzen, um 100€ auszuzahlen und die Datenbank in einen inkonsistenten Zustand zu bringen.
Die Summenberechnung in T1 berücksichtigt also das n+1-te Element (= Phantomobjekt) nicht.
Sei eine Menge von Transaktionen mit den Ausführungsplänen G und H.
H und G sind äquivalent, falls ihre Konflikte identisch sind.
Für alle Transaktionen :
Ein Ausführungsplan ist serialisierbar, wenn es einen äquivalenten sequentiellen Ausführungsplan gibt.
Beispiel: (sequentieller Ausführungsplan)
Ein Serialisierbarkeitsgraph sei ein gerichteter Graph mit Knotenmenge und Kantenmenge zur Historie .
Ausführungsplan H ist genau dann serialisierbar, falls zyklenfrei ist.
Konflikte:
Der Graph ist zyklenfrei und damit serialisierbar mit äquivalenten sequentiellen Ausführungsformen:
Es kann nicht mit T2 begonnen werden, da sonst die Konflikte nicht entstünden und der Ausführungsplan damit per Definition nicht äquivalent wäre.
= präventive Verfahren zur Verhinderung nicht-serialisierbarer Ausführungspläne
Es werden dadurch nur serialisierbare Ausführungspläne erzeugt.
Es können aber Verklemmungen auftreten.
Problem: kaskadierendes Zurücksetzten
Zeitpunkt | T1 | T2 | Bemerkung |
---|---|---|---|
1 | BOT | ||
2 | BOT | ||
3 | lock(A) | ||
4 | write(A) | ||
5 | lock(B) | ||
6 | read(B) | ||
7 | lock(B) | T1 muss auf T2 warten | |
8 | lock(A) | T2 muss auf T1 warten | |
9 | ... | ... | Deadlock |
(bisher keine Unterscheidung für Schreibe- oder Leseoperationen)
Zwei Sperrmodi:
R-Sperre: Objekt darf nur gelesen werden
X-Sperre: Exklusiver Lese- und Schreibzugriff auf Objekt
= Fehlerbehandlung, um ACID-Bedingungen zu bewahren
Bei Auftreten eines Fehlers müssen alle Änderungen der abgebrochenen Transaktion rückgängig gemacht werden. Logging
Transaktionsfehler
Systemfehler
Speicherfehler
= Protokollierung aller vorgenommenen Änderungen
physisches Logging
logisches Logging
Protokollierung von Änderungsoperationen und zugehörigen inversen Operationen
Aufbau
Beispiel:
Logische Protokolldatei |
---|
... |
<#42, T04, begin TA, 0> |
<#43, T04, P1, A = A+10, A A-10, #42> |
<#44, T04, P1, B = B*2, B = B/2, #43> |
<#45, T03, P2, D = D+1, A = D-1, #40> |
<#46, T03, commit, #45> |
... |
physiologisches Logging
Protokolleinträge werden im Voraus geschrieben.
Analysephase
REDO-Phase
UNDO-Phase
Durchlaufe alle Einträge vom Ende bis zum Anfang
Führe UNDO-Operation für alle Loser-Transaktionen durch
Schreibe Kompensationseinträge für alle UNDO-Operationen in Log-Datei
Aufbau:
Sequenznummer
Transaktionsnummer
Seitenkennung
REDO-Operation
(UNDO-Operation des jeweiligen zu kompensierenden Eintrags)
leeres UNDO-Feld
Zeiger auf zuletzt ausgeführte UNDO-Operation
Zeitpunkt | Logische Protokolldatei |
---|---|
... | ... |
71 | <#71, T08, P1, B = B+4, B = B-4, #69> |
72 | <#72, T09, begin_TA, 0> |
73 | <#73, T09, P1, A = A-1, A = A+1, #72> |
74 | <#74, T08, P2, D = D-3, D = D+3, #71> |
75 | <#75, T08, commit, #74> |
76 | <#76, T09, P1, A = A*4, A = A/4, #73> |
CRASH | |
77 | <#77, T09, P1, A=A/4, , #73> |
78 | <#78, T09, P1, A=A+1, , #72> |
Winner-Transaktion: T08 (wegen commit zu Zeitpunkt 75)
Loser-Transaktion: T09 (weil kein commit)
Problem/Motivation: Log-Dateien können sehr groß werden
Wiederanlauf startet bei Sicherungspunkt
Puffer werden nach WAL-Prinzip geschrieben. Bei Erzeugung des Sicherungspunktes wird eine Liste mit aktiven Transaktionen und die kleinste LSN (MIN_LSN) aller noch aktiven Transaktionen gespeichert.
Recovery-Ablauf:
Zustand nach REDO-Phase:
Zustand nach UNDO-Phase:
Isolationslevel | dirty reads | dirty writes | non-repeatable reads | Phantome |
---|---|---|---|---|
read uncommited | möglich | möglich (wenn schreibend) | möglich | möglich |
read commited | nein | möglich (wenn schreibend) | möglich | möglich |
repeatable reads | nein | nein | nein | möglich |
serializable | nein | nein | nein | nein |
Leseoperationen in Transaktionen ignorieren die Sperren anderer Transaktionen
dirty-reads erlaubt
Transaktion setzt Schreibsperre auf geändertes Tupel und hält diese bis commit / rollback
Leseoperationen halten Sperren nur kurz (nicht bis zum Ende der Transaktion)
non-repeatable reads sind möglich
Lesesperren und Schreibsperren werden bis zum Ende der Transaktion gehalten
Transaktionen sehen immer den gleichen Zustand eines Tupels in der Datenbank
Phantome sind möglich
Relation M
MId | Gehalt |
---|---|
M1 | 1000 |
M2 | 2000 |
Transaktion 1 (wird im Isolationslevel SERIALIZABLE ausgeführt)
x
begin transaction;
update M set Gehalt = Gehalt * 2 where MId = 'M1';
update M set Gehalt = Gehalt + 100 where MId = 'M1';
commit;
Transaktion 2
x
begin transaction;
select sum (Gehalt) as G1 from M;
select sum ( Gehalt) as G2 from M;
commit;
Werte von G1, G2 mit unterschiedlichen Isolationsleveln für T2:
Ausführungsreihenfolge | G1 | G2 |
---|---|---|
T1 < T2 | 4100 | 4100 |
T2 < T1 | 3000 | 3000 |
Ausführungsreihenfolge | G1 | G2 |
---|---|---|
T1 < T2 | 4100 | 4100 |
T2 < T1 | 3000 | 3000 |
T2 < T1 < T2 | 3000 | 4100 |
Ausführungsreihenfolge | G1 | G2 |
---|---|---|
T1 < T2 | 4100 | 4100 |
T2 < T1 | 3000 | 3000 |
T2 < T1 < T2 | 3000 | 4100 |
T2 < T1 < T2 < T1 | 3000 | 4000 |
T1 < T2 < T1 < T2 | 4000 | 4100 |
Ziele:
*=verifizierendes (optimistisches) Verfahren**
(2-Phasen-Sperrprotokoll ist präventives Verfahren)
Historie:
initiale Werte:
Zeitpunkt | Tupel | Version (k) | Wert | read-ts | write-ts |
---|---|---|---|---|---|
2 | z | 2 (weil bei Schreiben neue Version und initial 1) | 2 | 1 (wird mit write gesetzt) | 1 (weil BOT zu Zeitpunkt 1) |
4 | x | 1 (weil initial 1 und keine Änderung bei Lesen) | 1 | 3 (weil BOT zu Zeitpunkt 3) | 0 (weil initial 0 und keine Änderung bei Lesen) |
7 | y | 2 (weil bei Schreiben neue Version und initial 1) | 5 | 6 (wird mit write gesetzt) | 6 (weil BOT zu Zeitpunkt 6) |
10 | y | 3 (weil bei Schreiben neue Version und letzte Version von T2 = 2) | 4 | 9 (wird mit write gesetzt) | 9 (weil BOT zu Zeitpunkt 9) |
12 | x | 2 (weil bei Schreiben neue Version und initial 1) | 8 | 11 (wird mit write gesetzt) | 11 (weil BOT zu Zeitpunkt 11) |
13 | y | 4 (weil bei Schreiben neue Version und letzte Version von T3 = 3) | 1 | 11 (wird mit write gesetzt) | 11 (weil BOT zu Zeitpunkt 11) |
14 | y | 1 (weil BOT von T5 als erstes und somit kein Update bekannt) | 3 (weil initial 3 und T5 Stand von Zeitpunkt 1 hat) | 1 (weil BOT zu Zeitpunkt 1) | 0 (weil initial 0 und keine Änderung bei Lesen) |