IT-Kompaktkurs. Datenbanken Skript zur Folge 4. Prof. Dr. Manfred Gruber Fachhochschule München

Größe: px
Ab Seite anzeigen:

Download "IT-Kompaktkurs. Datenbanken Skript zur Folge 4. Prof. Dr. Manfred Gruber Fachhochschule München"

Transkript

1 Fachhochschule München Munich University of Applied Sciences IT-Kompaktkurs Skript zur Folge 4 Prof. Dr. Manfred Gruber Fachhochschule München manfred.gruber@informatik.fh-muenchen.de Nov 1, 2000

2 Transaktions-Konzept, Mehrbenutzer-Synchronisation, Fehlerbehandlung Geschäftsdaten sind nirgendwo besser aufgehoben sind als in Datenbanksystemen, denn auch deren Verarbeitung geschieht nirgendwo besser. Aufbewahren ist das eine, zuverlässiges Verwalten das andere. Beides leisten Datenbanksysteme. Man nennt sie deshalb manchmal auch Datenbank-Management-Systeme. Im folgenden wird erläutert, was in diesen Systemen geschieht. Dabei zeigt sich: Datenbanksysteme haben eine vielschichtige Architektur. Man findet fast alles wieder, was an Informatik- Wissen "gut und teuer" ist. 1 Transaktions-Konzept Ein wichtiges Grundkonzept ist das Transaktions-Konzept. Was sind Transaktionen genau? Die Umbuchung von DM 8000,- von einem Konto 1 auf ein Konto 2 aus der Sicht des Datenbanksystems: Aus dieser Sicht ist eine Transaktion einfach eine Folge von Lese- und Schreibzugriffen auf die Datenbasis. Die angeführten Operationen machen nur Sinn, wenn sie alle und in der richtigen Reihenfolge durchgeführt werden. Was zwischen "begin of transaction'' (BOT) und commit (festschreiben) steht, ist eine unteilbare Einheit. Nach einer nur teilweisen Durchführung wäre die Datenbasis inkonsistent, d.h. in einem inkorrekten Zustand, weil möglicherweise DM 8000 verschwunden wären! (siehe Folie 1, Transaktions-Konzept 1) Was soll man dem Beispiel entnehmen? Dass Datenbanksystem-Architekturen von der Idee "Transaktion'' ausgehen: Eine Transaktion ist eine unteilbare, atomare Einheit von reads und writes, "eingeklammert'' von einer Anfangs- und einer Endemarke. Transaktionen sind in sich konsistent und hinterlassen die Datenbank in konsistentem Zustand. Es kann aber übrigens trotzdem vorkommen, dass eine erst teilweise ausgeführte Transaktion abgebrochen werden muss, hierfür gibt es die Operation abort (abbrechen). Dabei müssen sämtliche Spuren einer abgebrochenen Transaktion wieder beseitigt werden. In Datenbanksystemen geht es also hauptsächlich um Transaktionsverarbeitung! (siehe Folie 2, Transaktions-Konzept 2) Große Datenbanksysteme müssen laufend Transaktionen verarbeiten, und zwar viele "gleichzeitig'' und so schnell wie möglich. Nun muss man wissen: Transaktionen beschäftigen das System auf verschiedene Weise: Es müssen Daten von Platte in den Hauptspeicher gelesen, oder in umgekehrter Richtung zurückgeschrieben werden, und es wird Rechenleistung benötigt, um die Daten zu verarbeiten. (siehe Folie 3, "gleichzeitige'' Ausführung von Tas) Seite 2

3 Plattenzugriffe sind deutlich langsamer als das Rechnen in der CPU. Die Zugriffe auf die Datenbasis müssen also beschleunigt werden. Das geschieht durch "verzahnte'' (im Fachjargon: nebenläufige) Verarbeitung der Operationen verschiedener(!) Transaktionen. Aber: Dies ist nicht in zufälliger Reihenfolge möglich - man kann nicht beliebig "verzahnen'', die Nebenläufigkeit birgt einige Gefahren, wie folgende Beispiele anschaulich zeigen: Beispiel 1: Die Transaktion 1 (Umbuchung) kennen wir schon. Die Transaktion 2 bucht 4 % Zins zu jedem Kontostand. Aber: Die beiden Transaktionen laufen so ab, dass am Ende unterm Strich der Zins für DM 8000 fehlt! Der Grund ist: T 2 liest den Kontostand K 1, den T 1 gerade überschrieben hat. Genau in diesem Moment ist die Datenbasis in einem inkonsistenten Zustand; diese Konstellation hat den treffenden Namen "dirty read''. (siehe Folie 4, typische Synchronisationsprobleme 1) Beispiel 2: Hier trifft unsere Umbuchungs-Transaktion auf eine Transaktion T 2, die die Summe aller Konten ermittelt. aber: So wie das hier abläuft, wird die Transaktion T 2 getäuscht: T 2 bringt DM 8000 zu viel heraus! Der Grund ist: T 2 liest einen Kontostand K 1, der noch vor dem Ende von T 2 durch T 1 verändert wird, würde T 2 danach K 1 noch einmal lesen, würde sich ein anderer Wert ergeben (daher der Name "unrepeatable read'') T 2 hat also ein "Phantom'' gesehen. Tatsächlich sagt man auch "Phantomproblem'' hierzu (siehe Folie 5, typische Synchronisations-Probleme 2) Beispiel 3: Die eine Transaktion bucht von Konto K 1 DM 8000 ab, die andere DM 8000 drauf, es müsste sich eigentlich wieder der ursprüngliche Kontostand ergeben Aber: Es sind am Ende DM 8000 mehr auf dem Konto! Der Grund ist: Die eine Transaktion überschreibt das noch nicht festgeschriebene Resultat der anderen Transaktion, "lost update'' genannt (siehe Folie 6, typische Synchronisations-Probleme 3) Das waren drei typische Konfliktsituationen, die beim "Verzahnen'' von Transaktionen vermieden werden müssen. Wir werden noch sehen, wie. Zusammenfassung: Grundlegend für alle Überlegungen ist der Begriff der Transaktion. Für Transaktionen und ihre Verarbeitung gelten folgende Regeln: 1) Sie sind atomar, werden also ganz oder gar nicht ausgeführt. 2) Sie stellen in sich konsistente Verarbeitungseinheiten dar und lassen eine Datenbasis in einem konsistenten Zustand zurück. 3) Sie laufen quasi isoliert ab, d.h. sie bekommen die Wirkung paralleler Transaktionen nicht zu Gesicht. 4) Dauerhaftigkeit: Erfolgreiche, durch commit abgeschlossene Transaktionen werden durchgesetzt, d.h. auf dem Plattenspeicher "verewigt'' (darauf kommen wir noch zu sprechen). Die Anfangsbuchstaben der vier Regeln ergeben im Englischen das Merkwort: "ACID''. (siehe Folie 7, ACID-Paradigma) Langsam wird deutlich, dass ein Datenbanksystem vielschichtig sein muss, es hat einige Aufgaben zu lösen. Wie wird das "gemanaged''? Seite 3

4 Managen heißt deligieren; für jeden Zweck gibt es daher eine Komponente: Für Konsistenz und Isolation sorgt die Mehrbenutzer-Synchronisation (engl. Concurrency Control). für Atomarität und Dauerhaftigkeit sind Protokoll- und Wiederanlauf-Einheit (engl. Logging & Recovery) zuständig. (siehe Folie 8, Aufgabenverteilung) 2 Mehrbenutzer-Synchronisation (Concurrency Control) Wo setzt man bei der Mehrbenutzer-Synchronisation an? Dabei dreht sich alles um Schedules. Unter einem Schedule versteht man die Verzahnung von Transaktionen. Gegeben sein ein Schedule S mit zwei Transaktionen. Der Index (1 oder 2) sagt, von welcher Transaktion die Operation stammt. r bzw. w stehen für "lesen'' bzw. "schreiben''. A, B, C sind verschiedene Datenbasis-Objekte Der Schedule gibt die zeitliche Reihenfolge an (siehe Folie 9, Schedules) Aber: Wir haben vorhin an Beispielen gesehen, dass nicht jeder Schedule konsistenzerhaltend ist. Konsistenz aber ist nach dem ACID-Paradigma unverzichtbar. Die Frage ist nur, woran man "vernünftige'', konsistenzerhaltende Schedules erkennt. Serielle Schedules sind eigentlich ideal, denn wenn die Transaktionen unverzahnt nacheinander ablaufen, sind Isolation und Konsistenz natürlich garantiert. Jedoch sind siezu langsam. Man könnte auch jeden Schedule, der sich gerade ergibt, untersuchen, ob seine Wirkung irgendeiner seriellen Abarbeitung der beteiligten Transaktionen entspricht; man hätte dann einen serialisierbaren Schedule. Leider ist diese Frage, auch für den Computer, nur mit sehr großem Zeitaufwand entscheidbar - und deshalb nicht praktikabel Es gibt aber ein Kriterium, das nicht allzu restriktiv ist, Serialisierbarkeit garantiert und praktikabel ist: Konflikt-Serialisierbarkeit (siehe Folie 10, "vernünftige" Schedules) Wie ist Konflikt-Serialisierbarkeit definiert? Man kann sie anhand des Konfliktgraphen verständlich machen: Im Konfliktgraph eines Schedules stellen die beteiligten Transaktionen die Knoten dar. Ein Pfeil führt von einer Transaktion T i zu einer Transaktion T j, wenn beide Transaktionen auf dasselbe Datenbank-Objekt zugreifen - und zwar T i vor T j -, wobei mindestens eine der Operationen eine Schreiboperation ist. Das Kriterium lautet: enthält der Konfliktgraph keine Zyklen, so ist der Schedule konfliktserialisierbar (und damit auch serialisierbar). Das System kommt natürlich ohne Bild aus und benutzt stattdessen einen geeigneten Algorithmus. (siehe Folie 11, Konflikt-Serialisierbarkeit 1) T 1,T 2,T 3 bilden den Schedule S Seite 4

5 Dem Schedule entnimmt man: - T 1 liest A, bevor T 2 A schreibt: Pfeil von T 1 nach T 2 - T 2 schreibt A, bevor T 3 A liest: Pfeil von T 2 nach T 3 - T 3 liest B, bevor T 1 B schreibt: Pfeil von T 3 nach T 1 Der Konfliktgraph ist zyklisch, also ist der Schedule nicht konfliktserialisierbar! (siehe Folie 12, Konflikt-Serialisierbarkeit 2) Wenn wir nun die letzten beiden Operationen von S vertauschen, dreht sich der Pfeil zwischen T 3 und T 1 um dann hat der Konfliktgraph keinen Zyklus! Damit ist der zugehörige Schedule konfliktserialisierbar, also konsistenzerhaltend (siehe Folie 13, Konflikt-Serialisierbarkeit 3) Wie setzt man nun diese Erkenntnis in die Praxis um? Man wendet eine Methode an, die bei laufendem Betrieb automatisch konfliktserialisierbare Schedules erzeugt, z.b. ein sperrbasiertes Protokoll. Es beruht auf dem Sperren von Datenbankobjekten. Der Scheduler (eine System-Komponente) setzt temporäre Sperren; es gibt zwei Modi: S-Sperren ("S'' für "shared'') und X-Sperren ("X'' für "exclusive'') Eine Transaktion darf ein Datenbankobjekt lesen, wenn sie eine S-Sperre darauf erhalten hat und schreiben, wenn sie eine X-Sperre darauf erhalten hat Dabei gilt: S-Sperren sind nur erhältlich auf Objekte, die noch nicht X-gesperrt sind, X-Sperren sind nur erhältlich auf Objekte, die weder S- noch X-gesperrt sind (das besagt die "Verträglichkeits-Matrix''). (siehe Folie 14, Sperrbasierte Synchronisation 1) Das strenge 2-Phasen-Sperrprotokoll: Es funktioniert so, wie eben beschrieben. Zusätzlich gilt: Jede Transaktion behält die Sperren, die sie in ihrem Verlauf bekommen hat, bis zu ihrem Ende. Die Transaktion macht sich zwar dadurch "breit'' und bremst andere Transaktionen aus, aber dafür sorgt das strenge 2-Phasen-Sperrprotokoll für Serialisierbarkeit und zusätzlich für die Rücksetzbarkeit abzubrechender Transaktionen, was auch eine wichtige Eigenschaft ist. Bei diesem Protokoll kann es aber vorkommen, dass eine Transaktion eine angeforderte Sperre im Moment nicht erhalten kann, dann kommt sie in eine Warteschlange und muss dort auf den Erhalt "ihrer'' Sperren warten (siehe Folie 15, Sprerrbasierte Synchronisation 2) Aber: Damit ist noch nicht alles geregelt, denn auch beim strengen 2-Phasen-Sperrprotokoll ist man noch nicht alle Probleme los, wie folgendes Beispiel zeigt: Wir notieren jetzt zusätzlich mit S bzw. X die Anforderung von S- bzw. X-Sperren Man sieht: T 1 hält eine X-Sperre auf A und T 2 eine X-Sperre auf B. Diese Sperren halten die Transaktionen auch durch ("streng''!) T 1 braucht aber noch eine S-Sperre auf B und T 2 eine S-Sperre auf A, die bekommen sie natürlich jetzt nicht sie blockieren sich gegenseitig Müssen sie nun "ewig'' warten? eigentlich schon, man nennt diese Situation Verklemmung (eng. deadlock) (siehe Folie 16, Sperrbasierte Synchronisation 3) Seite 5

6 Was kann man gegen diese Verklemmung tun? Es gibt einen pessimistischen und einen optimistischen Ansatz Pessimistisch ist, durch einen Algorithmus dafür zu sorgen, dass Verklemmungen gar nicht erst entstehen Aber: Das beschäftigt das System zusätzlich! Der optimistische Ansatz geht davon aus, dass solche Situationen selten sind und lässt es "drauf ankommen'', es wird aber ein Wartegraph aufgebaut und laufend kontrolliert Dieser sieht folgendermaßen aus: - Die Knoten bestehen aus den Transaktionen des Schedules S. - Ein Pfeil führt von T i nach T j, wenn T i von T j im vorher erläuterten Sinn von T j blokkiert wird. - Hat der Wartegraph einen Zyklus, liegt eine Verklemmung vor. Dann wird eine der am Zyklus beteiligten Transaktionen abgeschossen, dadurch wird der Zyklus aufgebrochen und es geht weiter. Die abgeschossene Transaktion kann noch einmal gestartet werden, denn "Ganz oder gar nicht'' war ja die Devise. (siehe Folie 17, Deadlock-Erkennung und -Beseitigung) 3 Fehlerbehandlung (Logging & Recovery) Da auch ein Datenbanksystem vor Unfällen nicht sicher ist unterscheidet man: lokale Fehler Dieser liegt vor, wenn eine Transaktion zurückgesetzt werden muss (mögliche Gründe: ein gezielter benutzerseitiger oder systemseitiger Abbruch oder ein fehlerhaftes Anwendungsprogramm) Fehler mit Hauptspeicherverlust z.b. durch Stromausfall Fehler mit Hintergrundspeicherverlust z.b. durch einen "head crash'' (siehe Folie 18, Fehlerklassifikation) Fehler mit Hauptspeicherverlust treffen ein laufendes System ziemlich hart. Das versteht man besser, wenn man sich klar macht, wie Datenbanksysteme Daten von der Festplatte holen und dorthin zurückkopieren, nämlich in Form gleich großer Datenseiten: Für ein Datenbank- Objekt ist dies mindestens eine Seite, manchmal mehrere. Nach der Bearbeitung der Seiten müssen diese wieder auf Platte zurückkopiert werden. Im Sinne des ACID-Paradigmas würde man folgende Seiten-Ersetzungs-Strategien bevorzugen: Strategie "force'' Jede eingelesene Seite wird nach einer Veränderung durch eine Transaktion sofort zurückkopiert. Dadurch würde das Gebot der Dauerhaftigkeit von Veränderungen ideal eingehalten, aber das häufige Hin- und Herkopieren der Seiten würde das System auch bremsen. Strategie "no steal'' Jede eingelesene Seite wird nicht zurückkopiert, solange noch irgendeine aktive Transaktion auf ihr etwas zu schreiben hat. Das wäre zwar ein Beitrag zur Atomarität von Transaktionen, aber es würde dann im Hauptspeicher zu Engpässen kommen, ebenfalls mit negativer Auswirkung auf den Durchsatz. (siehe Folie 19, Seiten-Ersetzungs-Strategien) Um die Systemleistung hoch zu halten, werden also die beiden obigen Verfahren nicht angewendet. Im Gegenteil: Man wendet die Strategie "steal & no force'' an. Seite 6

7 "steal'' (stehlen) bedeutet, dass eine Seite zurückkopiert werden kann, obwohl es noch eine Transaktion geben mag, die ein Objekt auf dieser sperrt. Ein abbrechen dieser Transaktion kann nur dann gut gehen, wenn in einer Protokoll- Datei (Log-Datei) steht, wie die Seite vorher ausgesehen hat, damit ihr ursprünglicher Zustand wieder hergestellt werden kann. "no force'' bedeutet, geänderte Seiten nicht sofort zurückzuschreiben, obwohl die ändernde Transaktion abgeschlossen ist. Ein Absturz des Systems kann dann nur gut gehen, wenn in der Log-Datei die Veränderungen schon festgehalten sind, damit sie später nachgeholt werden können. Alles steht und fällt also mit der Log-Datei bzw. mit einem geeigneten Logging! (siehe Folie 20, steal & no force) Für jeden Transaktionsschritt wird ein Eintrag in die Log-Datei gemacht; diese Einträge kommen in einen Pufferbereich im Hauptspeicher und von dort in einen separaten permanenten Speicher, der so ausfallsicher wie möglich ist. Jeder Eintrag enthält die Information, welche Transaktion welches Datum auf welcher Seite wie verändert hat (die sogenannte redo- und undo-information). (siehe Folie 21, Logging) Besonders wichtig - tatsächlich "überlebensnotwendig'' - beim steal-&-no-force-ansatz ist, dass dies alles rechtzeitig geschieht, wenn man bedenkt, dass jederzeit "das Licht ausgehen'' kann; das geeignete Protokollverfahren ist das Write-Ahead-Logging. Es schreibt vor: 1) Alle Log-Sätze, die zu einer Datenbank-Seite gehören, müssen permanent (z.b. auf Band) gespeichert sein, bevor die Seite selbst zurückkopiert wird. So können nach jedem Fehler die Spuren nicht abgeschlossener Transaktionen auf der Seite wieder entfernt werden. 2) Alle Log-Sätze, die zu einer Transaktion gehören, müssen vor deren Abschluss geschrieben sein. So können nach einem Fehler die Veränderungen abgeschlossener Transaktionen nachgeholt werden, die noch nicht auf dem permanenten Speicher stehen (siehe Folie 22, Write-Ahead-Logging) Die komplexeste aller System-Komponenten des Datenbanksystems, der Recovery Manager, besorgt diese "Reparaturen'': Spuren nicht abgeschlossener Transaktionen beseitigen (undo) und Veränderungen abgeschlossener Transaktionen durchsetzen (redo). Er stellt nach jedem Fehlerfall wieder einen möglichst aktuellen konsistenter Zustand der Datenbasis her. Im schlimmsten Fall (bei Verlust von Hauptspeicher und Hintergrundspeicher) zieht er Archiv-Stände der Datenbasis und der Protokoll-Datei heran, um die Datenbasis durch das "Nachfahren'' aller protokollierten Transaktionen wieder herzustellen. solche Archivstände (Sicherungen) werden selbstverständlich auch immer angefertigt. (siehe Folie 23, Recovery-Manager) Seite 7