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

Transaktionen laufen im Betriebssystem ab

15.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.

Die Implementierung von Konsistenzerhaltungsmechanismen zieht unvermeidlich eine Einschränkung der Nebenläufigkeit und damit der Systemleistung nach sich. Für Maßnahmen zur Wiederherstellung (Recovery und Rekonfiguration) wird nach heute gängiger Praxis die potentiell erreichbare Nebenläufigkeit noch einmal über das für die Konsistenzerhaltung notwendige Maß hinaus reduziert (striktes Sperren). Außerdem werden zusätzliche Ressourcen gebunden. Die Bipolarität zwischen Systemleistung einerseits und Fehlertoleranz, Verläßlichkeit oder Sicherheit andererseits zieht sich durch alle Designebenen. Der "Erfolg" eines Systems hängt auch davon ab, wie dieses Problem gelöst wird. Dazu ist auch eine entsprechende Unterstützung von seiten der Hardwarearchitektur erforderlich. Um letztendlich zu auf Messungen und Bewertungen beruhenden verläßlichen Aussagen zu kommen, müssen Prototypen beziehungsweise Labormodelle mit entsprechenden Testumgebungen implementiert werden.

Bei Betrachtung der in der letzten Folge erörterten notwendigen Änderungen der bisher erfolgten Transaktionsimplementierungen im Datenbankbereich wird deutlich, daß dort überwiegend Fragen nach der Struktur von Transaktionen (Forderung nach Möglichkeit der Schächtelung) sowie nach der Implementierung der Konsistenzerhaltung im Vordergrund standen (siehe CW Nr. 19 vom 8. Mai 1987, Seite 14). Das ergab sich zwangsläufig aus der Tatsache, daß die Lösung des Konsistenzproblems weit mehr anwendungsspezifische Aspekte enthält als dies bei Fragen der Recovery der Fall ist. Es liegt deshalb nahe, die Implementierung auf der Systemebene in Form von einander überlagernden Schichten vorzunehmen (siehe Abbildung 5).

Die oberste Schicht 3 repräsentiert die Schnittstelle zu den Applikationen, die das Transaktionskonzept benutzen. Bei Schachtelung kann sich prinzipiell jede Transaktion zu einem Transaktionsbaum beliebiger Tiefe ausweiten. Die Wurzel eines solchen Baumes wird entsprechend als Wurzeltransaktion bezeichnet. Jede Wurzel eines Unterbaumes ist gleichzeitig Vater der von ihr erzeugten Subtransaktionen als auch Sohn der Vatertransaktion, die sie generiert hat. Während der Abwicklung der Subtransaktionen sind die Väter inaktiv. Nach ihrer Beendigung geht die Kontrolle wieder an die Väter zurück. Die Synchronisation der obersten Wurzeltransaktionen vollzieht sich analog zu der in traditionellen Transaktionssystemen. Im Falle der Schachtelung werden auch parallel ablaufende "Bruder"-Transaktionen nach dem gleichen Schema synchronisiert, so als ob die Söhne selbst oberste Wurzel in ihrer eigenen Transaktionswelt wären.

Prinzipiell bringt die Schachtelung zwei wesentliche Vorteile mit sich. Einmal erlaubt sie die Einführung von Parallelität innerhalb einer Transaktion ohne die Gefahr einer Konsistenzverletzung bei parallelem Zugriff auf gemeinsame Daten. Zum anderen wird im Fehlerfall ein Rücksetzen in kleineren Einheiten möglich. Dies wird dadurch erreicht, daß jede Subtransaktion beim ersten Zugriff auf ein Objekt einen eigenen Recovery-Punkt (Checkpoint) anlegt. So ist zunächst nur die entsprechende Subtransaktion von einem Abbruch betroffen. Der Vater hat dann die Möglichkeit der Initiierung eines Neustarts beziehungsweise der Wahrnehmung von Alternativen im Rahmen eines "Exception Handlings". Damit werden auch die Voraussetzungen für die Anwendung der Standardsoftware-Fehlertoleranzverfahren (Recovery-Blöcke, Multiversionsprogrammierung) geschaffen.

Allerdings kann eine Subtransaktion nicht durch Übergang in den Zustand "committed" beendet werden, da ein späterer Abbruch der Vatertransaktion nicht auszuschließen ist. Folglich wird ein zusätzlicher, vorläufiger Endzustand "completed" eingeführt, welcher auch einen späteren Widerruf nicht ausschließt. Das Zustandsübergangsdiagramm danach ist in Abbildung 6 dargestellt.

Die Synchronisation erfolgt nach dem bereits beschriebenen Prinzip des Sperrens mit Hilfe des sogenannten Zwei-Phasen-Lock-Protokolls, nicht zuletzt deswegen, weil nach bisherigen Erkenntnissen andere, optimistischere Verfahren weniger günstige Ergebnisse gerade bei langen Transaktionen zeitigen. Bei diesem Protokoll darf das Recht des Zugriffs auf ein Objekt ("Lock" genannt) erst dann wieder zurückgegeben werden, wenn innerhalb der Transaktion keine weiteren Locks mehr erworben werden. Dadurch wird die Transaktion in zwei Phasen aufgeteilt, wobei der Zeitpunkt des Phasenwechsels als Lock-Punkt bezeichnet wird. Dabei erben Väter die Locks ihrer Söhne. Allerdings geschieht dies bisher erst, wie schon beschrieben, nach Beendigung der Subtransaktion. In dem hier beschriebenen Ansatz können die Locks "hochgereicht" werden, sobald die entsprechenden Objekte nicht mehr benötigt werden, vorausgesetzt, der Lock-Punkt ist erreicht. Auf der obersten Ebene bedeutet dies letztendlich, daß Objekte schon vor der Beendigung der Transaktion freigegeben werden können.

Bei manchen Anwendungen, wie zum Beispiel langen Designtransaktionen, ist es oftmals durchaus angebracht, auf Serialisierbarkeit zumindestens teilweise zu verzichten. Deshalb ist es möglich, Subtransaktionen ausschließlich zu Recovery-Zwecken zu definieren; das heißt die Konsistenz wird auf dieser Ebene systemseitig nicht garantiert.

Der Preis für die in der obersten Schicht vorgenommenen Modifikationen zugunsten verbesserter Effizienz und größerer Anwendungsbreite besteht in einem größeren Aufwand zur Implementierung der zweiten Haupteigenschaft von Transaktionen, nämlich ihrer Recovery-Fähigkeit. Die entscheidende Frage lautet also: Wie können die beschriebenen positiven Auswirkungen erhalten bleiben, ohne durch den in Kauf zu nehmenden höheren Aufwand bei der Recovery wieder aufgehoben oder sogar ins Negative verkehrt zu werden? Die Antwort darauf kann in drei Punkten zusammengefaßt werden:

Die den verteilten Systemen innewohnende Parallelität kann auch zur Generierung spezieller Recovery-Prozesse, die gleichzeitig zu den eigentlichen Anwenderprozessen ablaufen, ausgenutzt werden. Ebenso läßt sich der Zustand "completed" zur effizienteren Abwicklung des aufwendigen Zwei-Phasen-Commit-Protokolls einsetzen, indem man mehrere Transaktionen zusammenfaßt. Eine objektorientierte Hardware-Architektur insbesondere auf der Ebene des lokalen Recovery-Managements bietet dabei Unterstützung.

Die Zulassung einer vorzeitigen Freigabe von Objekten hat zur Konsequenz, daß sich Maßnahmen zur Recovery auch über Transaktionsgrenzen hinweg erstrecken. Dies wird ermöglicht durch die Implementierung eines Recoverygraphen sowie durch die Abwicklung von sogenannten Chasing-Protokollen. Der Recoverygraph ist eine systemweit verteilte Datenstruktur. Darin werden die Berechnungsaktivitäten einer Transaktion in einem Knoten zusammengefaßt repräsentiert. Durch Kanten dargestellte Abhängigkeiten entstehen, wenn auf Objekte zugegriffen wird, die durch eine sich noch nicht im Zustand "committed" befindende Transaktion verändert wurden. Der lokale Abbruch einer Transaktion wird durch das Chasing-Protokoll im Gesamtsystem bekanntgemacht, so daß andere davon betroffene Transaktionen ermittelt und ebenfalls rückgängig gemacht werden können.

Es sei noch einmal darauf hingewiesen, daß selbst bei einer Kette von Abbrüchen, verursacht durch das Chasing, keine Verschlechterung gegenüber der bisherigen Vorgehensweise eintritt, da alle zusätzlich vom Abbruch betroffenen Transaktionen dort überhaupt noch nicht hätten ausgeführt werden können. Im übrigen kann natürlich eine allzu große Abbruchlawine durch die rechtzeitige Beantragung eines "Commit" seitens der Teilhaber einer Transaktion oder des Systems selber verhindert werden.

Messungen haben gezeigt, daß das aufwendige Zwei-Phasen-Commit-Protokoll sich sehr negativ auf den Durchsatz auswirkt. Nun wird es möglich, bei der Abwicklung dieses Protokolls gleich mehrere Transaktionen auf einmal zu berücksichtigen. Im Falle einer Commit-Anforderung muß dann lediglich die Menge der davon betroffenen Transaktionen, wiederum mit. Hilfe des Recoverygraphen, ermittelt werden. Die Verwaltung des Recoverygraphen sowie die Durchführung der Protokolle obliegt den jeweiligen Recovery-Managern und erfolgt parallel zur Ausführung der Transaktionen.

Die hier beschriebene Vorgehensweise wird durch jüngst veröffentlichte Leistungsanalysen bestätigt. Danach ist die bisherige pessimistische Form des Sperrens bis zum Erreichen des Commitzustandes in den meisten Fällen weniger effizient als der optimistische Ansatz, den Zugriff schon auf noch nicht "gesicherte" Daten zu erlauben, auch wenn hierbei die Gefahr einer Kette von Abbrüchen einkalkuliert wird.

Das Recovery-Punktmanagement erfolgt in der untersten Schicht und kann stellenlokal vorgenommen werden. Hier kann eine zugrundeliegende objektorientierte Hardware-Architektur einen wesentlichen Beitrag zur Leistungssteigerung bringen.

Auch im Hinblick auf die Eindämmung der Fehlerausbreitung sowie der Fehlererkennung - beides unverzichtbare Elemente in einer optimistischen Recovery-Strategie wird der Objektansatz verfolgt. Der Zugriff zu gemeinsam benutzten Objekten wird durch eine Reihe definierter Einschränkungen reguliert. Da deren Einhaltung überprüft werden muß, sind diese Mechanismen auch vorzüglich zur Fehlererkennung geeignet.

Typ-Inkompatibilität wird erkannt

Die genau spezifizierte Schnittstelle von Objekten, die sowohl die Datenstruktur als auch die gültigen typspezifischen Operationen festlegt, erlaubt die Erkennung von Typ-Inkompatibilitäten. Durch einen implementierten Capability-Mechanismus können unstatthafte Zugriffe (verursacht durch Fehler oder mit bewußter Absicht), die spezifizierte Zugriffsrechte verletzen, erkannt und vermieden werden. Durch diese Maßnahmen wird die Ausbreitung der entsprechenden Fehler verhindert. Gleichzeitig wird durch einen solchermaßen implementierten "Trusted Kernel" die Basis für darauf aufbauende Maßnahmen zur Restriktionssicherheit geschaffen. Da die Überprüfungen alle an der Objektschnittstelle vorgenommen werden, wird der Schaden isoliert.

Aufbauend auf einem systemweit eindeutigen Objektnamen wurde ein einheitlicher Adressierungsmechanismus implementiert ("Single Level Store"). Ziel dabei ist es, die Adressierung vom virtuellen Speicher, File-System oder E/A-Geräten transparent zu gestalten. Die unterschiedlichen Adressierungsmechanismen für File-System und virtuellen Speicher, welche es nicht ermöglichen, Files in den Adreßraum von Prozessen abzubilden, sind zum Beispiel eine Schwachstelle von Unix.

Um auf ein Objekt zugreifen zu können, muß es sich im aktiven Speicher befinden. Zur Sicherstellung der Permanenz werden die von einer Commit-Aktion betroffenen Objekte in den Permanentspeicher verlagert. Während der ersten Phase des Zwei-Phasen-Commit-Protokolls werden diese Objekte zunächst im Logspeicher zwischengelagert. Dies ist notwendig, um auch bei einem Stellenausfall das Commit erfolgreich zu beenden (entspricht dem Redo im Datenbankbereich). Von entscheidender Bedeutung ist jedoch die Existenz des Recovery-Speichers. Dieser Teil des virtuellen Speichers wird von seiten der Speicherverwaltung ausschließlich dem System für das Anlegen von Recovery-Punkten zur Verfügung gestellt. Damit entfällt der sonst notwendige Zugriff über das File-System.

Diese Effizienzsteigerung wirkt sich insbesondere bei der Anwendung geschachtelter Transaktionen aus, da dort, zwecks Minimierung der im Fehlerfall zu stornierenden Aktivitäten, jede Subtransaktion ihre eigenen Recovery-Punkte anlegt. Die Verwaltung des objektspeichers sowie die Aufgaben der untersten Schicht übernimmt ein eigens dafür in der Entwicklung befindlicher Koprozessor. Der große Vorteil des Einsatzes von Koprozessoren bisher vor allem in der Gleitkommaarithmetik eingesetzt - besteht darin, daß sie an auf dem Markt befindliche, leistungsstarke Mikroprozessoren angehängt werden können. Dadurch wird eine effiziente Hardware-Unterstützung für bestimmte Anwendungen erreicht, ohne daß die übrige Anwendungssoftware davon berührt wird.

Eine erste lauffähige Version des hier vorgestellten Transaktionskonzepts ist bereits fertiggestellt. Sie wurde, zunächst zu Tist- und Meßzwecken, oberhalb der Systemaufrufschnittstelle von Unix 4.2 bsd in einer Systemumgebung, bestehend aus durch Ethernet verbundenen, "Sun"-ähnlichen Workstations (Integrated Solutions), implementiert. Der Umfang der in "C" geschriebenen Software beträgt zirka 90000 Zeilen. Allerdings ist diese Möglichkeit, Transaktionen in verteilten DV-Systemen möglichst effizient zu handhaben, keineswegs der einzige gangbare Weg.

Das Fazit: Transaktionen sind kein neues Konzept. Die Idee von Konsistenten und dauerhaften Datenzustands-Transformationen ist so einfach wie einleuchtend und deshalb vor allem im Datenbankbereich schon Quasistandard. Allerdings verschließen sich die bisherigen Implementierungen einem breiteren, immer stärker in den Vordergrund tretenden Anwendungsspektrum. Nicht zuletzt bildet ein diesen Bedürfnissen angepaßtes Transaktionskonzept eine gute Basis für die Implementierung verteilter Systeme, da es die darin auftretenden Synchronisations- und Fehlerprobleme in einer sauberen und machbaren Weise einkapselt.

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