Performance läßt sich durch Verlagerungsstrategie steigern (Teil 1):

Transaktionen lauten im Betriebssystem ab

01.05.1987

Transaktionsorientierte Systeme erfordern insbesondere in einem verteilten DV-System enorme Rechnerkapazitäten und dementsprechend hohe Performance-Werte. Um die Transaktionen zu beschleunigen, wird vielerorts versucht, sie aus der Anwendungsebene auf die des Betriebssystems oder des Mikrocode zu verlagern. Edgar Nett*) beschreibt die Voraussetzungen, Gründe und bisherigen Ergebnisse solcher Bemühungen.

Für Transaktionen gilt, ebenso wie für viele andere Konzepte im Bereich der Datenverarbeitung, daß sie Analogien zu entsprechenden Vorgehensweisen in der Organisation gesellschaftlichen Zusammenlebens enthalten. Solche Analogien können nützlich sein, um spezifische Problemfelder sowie mögliche Lösungsansätze allgemeinverständlich darlegen zu können. Deshalb soll auch hier ein solches Beispiel am Beginn der Ausführungen stehen:

Bevor ein Brautpaar sich das Ja-Wort gibt, erfolgen meistens viele Aktivitäten, in die auch andere Personen involviert sein können. Der Übergang in den Stand der Ehe geschieht, formal gesehen, in zwei Schritten. In der ersten Phase holt der Standesbeamte von jedem der beiden die Zustimmung ein. Erst dann vollzieht er die Ehe durch eine entsprechende Mitteilung. Es ist jedoch nicht auszuschließen, daß ein Beteiligter vor dem endgültigen Ja noch abspringt. Dann müssen alle bisher schon unternommenen Aktivitäten wieder rückgängig gemacht werden. Das Paar würde im Zustand ,ledig' verbleiben. Im positiven Fall werden beide durch den Standesbeamten endgültig und definitiv verheiratet. Dieser Zustand könnte somit nur durch eine kompensierende Aktion (Scheidung) wieder rückgängig gemacht werden.

An diesem Beispiel kann man alle Eigenschaften und deren Motivation erläutern, welche eine Transaktion in der Datenverarbeitung auszeichnen. Eine Transaktion bewirkt durch die in ihr ablaufenden Aktivitäten eine Zustandstransformation. An diesen Aktivitäten sind unter Umständen mehrere Teilhaber beteiligt. In einer verteilten Systemumgebung können mehrere Transaktionen parallel ablaufen. Die Konsistenz wird dadurch gewahrt, daß das Auftreten von lnterferenzen verhindert wird. Dies bedeutet, daß jede Transaktion gegenüber der Außenwelt als unteilbar erscheint. Entweder sie wird erfolgreich abgeschlossen und die damit verbundene Zustandstransformation ist für alle sichtbar vollzogen, oder es bleibt voll und ganz beim alten Zustand, und nichts im System hat sich geändert. Es müssen also Mechanismen bereitgestellt werden, die dafür sorgen, daß im Fehlerfall das System wieder auf den Zustand bei Beginn der betroffenen Transaktion zurückgesetzt werden kann.

Transaktionen wurden eingeführt auf der Ebene der Applikationssoftware, und zwar speziell im Anwendungsbereich Datenbanksysteme. Dies hatte mit der Tatsache zu tun, daß hier zuallererst die Notwendigkeit verteilter Datenverarbeitung auftrat (zum Beispiel im Bankgewerbe und bei den Luftverkehrsgesellschaften).

Deshalb soll an zwei Beispielen aus dem Bankenbereich veranschaulicht werden, wie das Interferieren von zwei parallel ablaufenden Transaktionen zu Inkonsistenzen führen kann (siehe Abbildung 1 und 2).

Der einfachste Weg, solche Inkonsistenzen zu vermeiden, wäre, dafür Sorge zu tragen, daß alle Transaktionen nur seriell, das heißt nacheinander ausgeführt werden. Offensichtlich würde dies aber gerade einem Hauptzweck verteilter Datenverarbeitung, nämlich Leistungssteigerung durch Ausnutzung von Parallelität, widersprechen. Also verlangt man lediglich Serialisierbarkeit.

Eine parallele Ausführung von Transaktionen ist serialisierbar, wenn die gleichen Resultate beziehungsweise Zustandsänderungen in der Datenbank auch durch irgendeine serielle Ausführung dieser Transaktionen erzielt werden können. Es ist leicht zu überprüfen, daß diese Bedingung in den angeführten Beispielen nicht erfüllt ist. Ursache dafür ist, daß dort Transaktionen (Y in Abbildung 1; B in Abbildung 2) auf nicht mehr gültige beziehungsweise inkonsistente Werte von Daten zugreifen, welche vorübergehend während der Ausführung einer parallel ablaufenden Transaktion erzeugt wurden. Um Serialisierbarkeit zu erreichen, muß also dieses Sichtbarwerden von Zwischenzuständen einer Datenbank während des Ablaufs einer Transaktion verhindert werden.

In einer Transaktion benötigte Daten sind so lange für den Zugriff anderer durch das Setzen sogenannter Locks zu sperren, bis die Transaktion beendet ist. Theoretisch wurde sogar bewiesen, daß es möglich wäre, die zugegriffenen Daten schon zu dem Zeitpunkt - Lock-Punkt genannt - freizugeben, ab dem innerhalb der betreffenden Transaktion auf keine weiteren Daten mehr zugegriffen wird. Von dieser Möglichkeit wird jedoch aus zwei Gründen kein Gebrauch gemacht: Da Transaktionen auf Datenbanken im allgemeinen von sehr kurzer Dauer sind, fällt die theoretische Effizienzsteigerung gegenüber dem erhöhten Aufwand zur Bestimmung des Lock-Punktes nicht ins Gewicht. Außerdem wird durch das Festhalten der Daten bis zum Ende einer Transaktion ein möglicher "Abort" wesentlich erleichtert. Denn keine andere Transaktion ist von dieser Maßnahme betroffen.

Recovery-Fähigkeit bringt zweifachen Nutzen

Damit kommt man zur Recovery-Fähigkeit von Transaktionen. Der mit dieser Eigenschaft verbundene Zweck ist zweifacher Art. Erstens soll eine Funktion bereitgestellt werden, die das Zurückfallen auf den Zustand bei Beginn der Transaktion realisiert (der entsprechende Ausdruck in Datenbanksystemen heißt UNDO). Die zweite Aufgabe besteht darin, die Daten gegen Fehlfunktionen und Rechnerausfälle zu schützen.

Die meistgenützte Art zur Implementierung der Rücksetzbarkeit ist das sogenannte "logging": Bei jeder Datenbankoperation werden hauptsächlich der Name der betreffenden Transaktion sowie der alte und neue Wert des benutzten Datums oder Objektes als Eintrag in einem Log (analog einem Eintrag in ein Logbuch) festgehalten. Der alte und der neue Wert können jeweils komplette Kopien oder aber nur die geänderten Teile des Objektes enthalten. Beispielsweise werden bei einer Operation auf einem Datenfeld innerhalb eines Files natürlich nur die entsprechenden Werte des betroffenen Datenfeldes und nicht die des gesamten Files abgespeichert. Im Falle des Abbruchs einer Transaktion wird dann das Log auf alle diesbezüglichen Einträge abgesucht, und die betroffenen Objekte werden auf ihren alten Wert zurückgesetzt. Zu Schutzzwecken sollte das Log auf einem oder mehreren nichtflüchtigen Speichermedien (zum Beispiel Spiegelplatten) aufbewahrt werden.

Die erfolgreiche Beendigung (Commit) einer Transaktion wird durch einen entsprechenden Commit-Eintrag im Log gekennzeichnet. In einer verteilten Umgebung muß man im allgemeinen jedoch davon ausgehen, daß der Effekt einer Transaktion sich in mehreren Logs niederschlägt. In solch einem Fall muß dafür gesorgt werden, daß der Commit-Eintrag entweder in allen oder in keinem der in Frage kommenden Logs erscheint (entsprechend der Alles-oder-Nichts-Eigenschaft von Transaktionen).

Das sogenannte Zwei-Phasen-Commit-Protokoll wird üblicherweise zu diesem Zweck implementiert. Im Protokoll übernimmt ein Koordinationsprozeß die Hauptverantwortung für dessen Durchführung. Grob skizziert ist der Verlauf, untergliedert in die beiden Phasen, wie folgt:

Jeder Teilhaber muß die Session abbrechen können

Zu Beginn der ersten Phase sendet der Koordinator einer Transaktion an alle Teilhaber eine Nachricht, sich auf das Commit vorzubereiten (Prepare-Nachricht). Im positiven Fall werden alle Teilhaber innerhalb einer vorbestimmten Zeit diesen Wunsch bestätigen (Prepared-Nachricht). Sendet aber auch nur einer der Teilnehmer eine negative Antwort (Abort-Nachricht) oder läßt die vorgesehene Zeit verstreichen (Time-out-Bedingung), leitet der Koordinator das UNDO ein. Dazu notiert er einen Abort-Eintrag für die betreffende Transaktion in seinem Log und sendet eine entsprechende Nachricht an alle Teilhaber. Diese verfahren dann ihrerseits entsprechend.

Im positiven Fall beginnt nun die zweite Phase: Der Koordinator schreibt einen Commit-Eintrag in sein Log. Nun gilt die Transaktion als erfolgreich abgeschlossen.

Anschließend sendet der Koordinator eine entsprechende Nachricht an alle Teilhaber. Diese wiederum nehmen den Commit-Eintrag in ihrem Log vor, heben die Sperren auf die von ihnen zugegriffenen Daten auf und senden eine Vollzugsnachricht an den Koordinator zurück; dieser vermerkt den erfolgreichen Abschluß des Protokolls im Log.

Im negativen Fall, das heißt bei einer Abort-Nachricht an alle Teilhaber, müssen diese jeweils ihren Log durchkämmen und bei entsprechenden Einträgen die betroffenen Daten auf den dort vermerkten alten Wert zurücksetzen. Anschließend werden auch die Sperren aufgehoben und eine Vollzugsnachricht an den Koordinator zurückgesandt. Damit ist das Protokoll beendet.

Zu erwähnen bleibt noch, daß es wünschenswert ist, jedem Teilhaber die Möglichkeit zum einseitigen Abbruch einer Transaktion einzuräumen. In diesem Fall muß innerhalb des dann einzuleitenden Protokolls der Abort allseitig durchgeführt werden. Das Zeitintervall, in dem diese Möglichkeit nicht gegeben ist, wird im Zwei-Phasen-Commit-Protokoll auf die zweite Phase beschränkt. Von dem Zeitpunkt an, wo ein Teilhaber seine Prepared-Nachricht an den Koordinator absendet, hat er selbst keinen Einfluß mehr auf den weiteren Verlauf.

Dr. Edgar Nett ist Projektleiter bei der Gesellschaft für Mathematik und Datenverarbeitung (GMD), Sankt Augustin.