Performance läßt sich verbessern

Die relationale DB-Technik ist noch nicht ausgeschöpft

09.10.1992

Kosteneinsparungen durch die Minimierung des Verbrauchs von Ressourcen sind zu einem wesentlichen Ziel der Anwendungsentwicklung geworden. Das gilt auch für den Bereich relationaler Datenbanksysteme, Harald Huber schildert, wie bei der Entwicklung relationaler Anwendungen der Ressourcenverbrauch verringert werden kann.

Galt früher die Performance von DV-Anwendungen als ein Kennzeichen für ihre Anwendbarkeit und Qualität, so steht der Begriff heute für die Optimierung des Ressourceneinsatzes. Die DV steckt in einer Legitimationskrise, das zeigt schon die Outsourcing-Diskussion. Probleme entstehen nicht zuletzt dann, wenn die Investition in die Optimierung einer Anwendung so hoch ist, daß sich eine Kosteneinsparung kaum noch realisieren läßt.

Anwender sollten deshalb prüfen, welche Maßnahmen getroffen werden müssen, um

- die wahrscheinliche Performance und den Ressourcenverbrauch abzuschätzen,

- eine akzeptable Performance zu erreichen und

- von Anfang an Dynamik zu ermöglichen, zum Beispiel bei steigender Benutzerzahl oder Datenmenge.

Informationen über das genaue Mengengerüst sowie exakte Daten über das Anwenderverhalten, die Systemauslastungen und die tageszeitliche Verteilung der Last sind häufig erst während des Einsatzes eines Systems verfügbar. So ist es schwer, von Anfang an die Ressourcenminimierung mit einem wirtschaftlich sinnvollen DV-Einsatz zu betreiben. Deshalb sollten in der Anfangsphase nur die Weichen richtig gestellt werden, die konkrete und detaillierte Umsetzung ist erst in der Realisierungsphase möglich. Es muß die Option bestehen, die Tuning-Maßnahmen nachträglich in der Wartungsphase durchzuführen.

Relationale Datenbanken dienen als ideale Vehikel

Relationale Datenbanken, denen oftmals eine schlechte Performance und ein hoher Ressourcenverbrauch nachgesagt wird, sind für eine solche Vorgehensweise ein ideales Vehikel. Das hohe Maß an physischer Datenunabhängigkeit und die heute häufig unzutreffende hohe Optimizer-Intelligenz erlauben die Erstellung von Anwendungen, die mit anderen Mitteln nicht zu realisieren wären.

Deshalb verringern relationale Datenbanksysteme trotz größerem Ressourcenbedarf den Gesamtaufwand. Die Bilanz ist dann insgesamt positiv, wenn die Intelligenz dieser Systeme bei der Realisierung von Anwendungen eingeplant und eingesetzt wird. Wir haben mit diesem Ziel einen Ablaufplan entwickelt und in mehreren Projekten erfolgreich verwendet. Die einzelnen Schritte sollen hier vorgestellt werden.

Das Know-how ist zu ermitteln

Für den ersten Schritt einer relationalen Anwendungsentwicklung sollte man eine detaillierte Kenntnis der Funktionsweise der relationalen Datenbank haben. Wichtig sind dabei zunächst die verfügbaren Zugriffspfade. Relationale Datenbanken ermöglichen eine Vielzahl von Zugriffsverfahren, von denen Relational Scan und Index-Zugriff wesentliche Bestandteile sind. Hashing oder aufwendige Index-Suchverfahren, komplexe Leseverfahren über Parallelverarbeitung oder Kombinationen kommen aber ebenso zum Einsatz. Der Benutzer muß dies bei seiner Anwendung einplanen.

Von Bedeutung ist auch die Speicherverwaltung. Da die Daten physisch unabhängig abgelegt werden, sind Anwender geneigt, den physischen Aufbau der Daten zu vernachlässigen. Dies macht auch Sinn, solange nur die Anwendungslogik betroffen ist. Die Zugriffspfade der Datenbank werden jedoch sehr stark durch die Art der Datenspeicherung bestimmt. Zudem lassen sich Performance und Ressourceneinsatz nur bei Kenntnis der physischen Speicherform abschätzen.

Optimizer sind heute in relationalen Datenbanken recht weit verbreitet. In wenigen Fällen sind sie rein heuristisch, meistens verwenden sie statistische Informationen über die Daten, manchmal auch Angaben über die Hardware, auf der sie zum Einsatz kommen. Der Anwendungsentwickler sollte diese Methoden einigermaßen genau kennen und vor allem über Schwächen des Optimizers informiert sein. Verfügt die Datenbank über eine Möglichkeit, den Zugriff zu analysieren, muß der Entwickler diese Auswertung verstehen und interpretieren können.

Neben der reinen I/O-Tätigkeit führen Datenbanken noch weitere Aktionen durch. Wesentlich ist die Art der Kommunikation mit anderen Systemen, die Verarbeitung logischer Operatoren und die Methoden, mit denen Funktionen wie Substring oder der Aufbau von Views ausgeführt werden.

Abschließend muß Informationsmaterial über die vorhandene Hardwarebasis vorliegen: Dabei sind unter anderem Plattengeschwindigkeit, CPU und Kanalwerk von Bedeutung. Auch dem Sperrverfahren kommt große Bedeutung zu:: Neben den bisher angesprochenen Punkten, die das Laufzeitverhalten beeinflussen, sollte Klarheit darüber bestehen, wie die Datenbank Datensätze vor konkurrierenden Zugriffen schützt und wie sich diese Verfahren auswirken.

Zumindest ungefähr sollte die Anzahl von Datensätzen und ihrer internen Struktur bekannt sein. Das heißt, es sind Fragen zu beantworten, die sich auch der Optimizer stellt: Wie viele Mitarbeiter gehören im Durchschnitt zu einer Kostenstelle? Wie sieht die Streuung dieses Wertes aus? Existieren Abhängigkeiten zwischen der Kostenstelle und deren Ort? Kann das Gruppieren oder Sortieren auf einer Spalte auch andere Spalten in eine Reihenfolge bringen?

Normalerweise sollte das funktionale Design einer Anwendung bekannt sein. Die Praxis zeigt jedoch, daß die Funktionen häufig nicht in letzter Konsequenz durchdacht sind. Zudem haben oftmals die Mitarbeiter nicht ausreichend über miteinander kommunizierende Funktionen gesprochen.

Während der Anzahl der Benutzer Bedeutung zukommt, kann das Benutzerverhalten meistens nicht berücksichtigt werden, Schwierigkeiten dürfte es etwa dann geben, wenn alle Benutzer am gleichen Wochentag arbeiten, die Abschätzung aber auf die Woche bezogen wurde.

Beurteilung der Gesamtanwendung

Werden die hier genannten Punkte berücksichtigt, dürfte die Funktionsweise des relationalen Datenbanksystems bekannt sein. Erst danach kann die Gesamtanwendung mit Blick auf die Frage beurteilt werden, wie kritisch die Software im Hinblick auf den Ressourcenverbrauch ist. Normalerweise wird das Verhalten einer Anwendung durch eine Analyse der wesentlichen Engpässe ermittelt, denn so läßt sich auch die Gesamtproblematik der Anwendung beurteilen.

Abhängig von den Eigenschaften der Anwendungen finden sich meistens folgende Engpässe:

1. Anwendungen, die aufgrund der jeweils verarbeiteten Datenmenge kritisch sind. Große Datenbestände können zu einem hohen Ressourcenverbrauch führen. Normalerweise erlauben die Suchverfahren der Datenbanken durchaus das sehr schnelle Auffinden einzelner Datensätze - auch bei sehr großen Datenmengen. Grundsätzlich wirkt sich also die Datenmenge kaum auf die Zugriffszeiten aus. Werden die großen Datenmengen jedoch in einem Schritt verarbeitet, muß mit Schwierigkeiten gerechnet werden.

Andererseits kann auch die Verarbeitung kleinerer Datenmengen zu Problemen führen, nämlich dann, wenn die Dynamik der Daten oder die Vielfalt der Abfragearten eine komplette Ausstattung mit Suchmechanismen verbietet. Kritisch wirken sich große Datenmengen möglicherweise auch dann aus, wenn mit hohen Transaktionsleistungen gearbeitet werden muß. Dann summieren sich die im Einzelfall nur geringfügigen zeitlichen Verzögerungen zu einer beträchtlichen Wartezeit für das Gesamtsystem.

2. Anwendungen mit hoher Dynamik der Daten: Werden in Anwendungen terminabhängige Bewegungsdaten verarbeitet, so ist allgemein mit Problemen zu rechnen. Das kann zum Beispiel der Fall sein, wenn jeden Monat neue Datensätze zu laden und die Datensätze des zurückliegenden Monats zu löschen sind. Wird in der physischen Speicherung der Daten eine Reihenfolge (Clustering) verlangt, so geht diese im monatlichen Rhythmus wieder verloren.

Zwar ist in diesem Fall die Performance der Anwendung kein wirkliches Problem, zumal sich nachts oder am Wochen. ende das Clustering der Daten wiederherstellen läßt, doch sollten die Kosten für eine solche Maßnahme Berücksichtigung finden. Anwender, die solche Programme benutzen, dürften über die Kosten, die häufig erst im Rahmen von Outsourcing-Projekten wirklich verrechnet werden, sehr überrascht sein.

Ist ein 24-Stunden-Betrieb an sieben Tagen in der Woche die Regel, so verbietet sich dieses vorgehen ohnehin. Der permanente Zugriff schließt die Wartung der Daten in regelmäßigen Abständen aus. Als Lösung bietet sich die Spiegelung von Datenbeständen an. Es muß wohl nicht erläutert werden, daß diese vorgehensweise wiederum sehr hohe Kosten verursacht.

3. Anwendungen mit erwartet hoher Transaktionsleistung: Bei Anwendungen, die im Bereich der Transaktionsleistung kritisch sind, ist der Spielraum für Tuning-Maßnahmen sehr begrenzt. Die Abhängigkeit der einzelnen Parameter erschwert eine Optimierung. Häufig bleibt in solchen Fällen nur die Erhöhung der Ressourcen.

Performance so wichtig wie Logik

Üblicherweise treten in solchen Anwendungen wenige Funktionen häufig auf und eine Vielzahl von Funktionen vergleichsweise selten. Deshalb versteht sich vorn selbst, daß das Design der häufig verwendeten Funktionen einer exakten Planung unterliegen muß und daß Zugeständnisse an logisches sowie physisches Design notwendig sein können.

Allerdings besteht das Risiko, die Performance der Logik unterzuordnen, ohne sicher zu wissen, ob überhaupt ein Problem besteht. Auch hier sollte vor allem eine Weiterentwicklung ermöglicht werden, beispielsweise der Einsatz von Views, die exakte Dokumentation von Queries oder die Kennzeichnung der eventuell aus Performance. Gründen zu ändernden Datenstrukturen.

4. Anwendungen, die aufgrund des Sperrverhaltens kritisch sind: Diese Anwendungen können auch zu den ersten beiden Gruppen gehören. Normalerweise nimmt die Beeinflussung durch das Sperrverhalten bei wachsender Datenmenge ab und wird deshalb bei Anwendungen mit großen Datenmengen häufig vernachlässigt. Aber auch Programme mit mehr als zehn Millionen Datensätzen können Arbeitstabellen mit wenigen hundert Datensätzen haben. Möglich ist auch, daß sich Anwender um 1000 bis 2000 Sätze innerhalb einer Tabelle von Millionen Datensätzen "streiten". Diese Situation ist vor allem darin zu erwarten, wenn große Datenmengen fortlaufend eingegeben werden.

Ein Beispiel wäre eine Vorgangsnummer, die als Primärschlüssel dient und auch die physische Einfügungsreihenfolge der Daten bestimmt. Neue Datensätze erhalten dann immer eine um eins erhöhte Nummer. Werden Säue eingefügt, so leidet zum einen die Organisation der Daten und Zugriffsmechanismen darunter, zum anderen ist eine nachfolgende Veränderung der eben eingefügten Daten nicht gerade unwahrscheinlich. Dann könnten aber im selben physischen Bereich Sperren durch neue Inserts zu erwarten sein.

Das Beispiel zeigt, wie wichtig Performance-Planungen sind. Oft wird versucht, Sperrgröße und -verfahren oder auch die physische Struktur der Daten zu modifizieren beziehungsweise andere systemtechnische Möglichkeiten zu nutzen.

Dagegen könnte das Ändern der Einfügungsreihenfolge die meisten Probleme lösen. Es bringt zwar ein schlechteres Clustering der Daten mit sich, ermöglicht aber eine bessere Verteilung der geänderten Daten.

Oft ist die Performance eine Domäne von Technikern, die die Logik der Anwendung im Griff zu haben glauben und deshalb die Anwendungsfunktion vernachlässigen.

Ihnen ist es meistens nicht möglich, derartige von der Logik der Anwendung bestimmte Lösungsmöglichkeiten zu, wählen.

5. Anwendungen, die durch hohe CPU-Belastung kritisch werden: In der Beurteilung von Anwendungen gibt es die Schlagwörter "CPU-Bound" und "I/O-Bound". Gemeint sind damit die Flaschenhälse I/O- und CPU-Belastung.

Letztlich kann jede Anwendung zu einer sehr hohen CPU-Belastung führen. Die interne Verarbeitung sowie die Verarbeitung von Zugriffsmechanismen, logischen Operatoren und Funktionen muß bekannt sein, um den Arbeitsaufwand für die CPU zu schätzen. Außerdem sind auch Erfahrungswerte aus anderen Anwendungen wichtig. Trotz genauer Kenntnis der Funktion kann es hier zu Fehleinschätzungen kommen.

Bewerten von Einzelfunktionen

Stellt sich nach dieser Analyse der Anwendung heraus, daß die Gesamtfunktion eher unkritisch ist, was meist schon nach kurzer Zeit geklärt werden kann, so bringt die Realisierung keine allzu großen Probleme mit sich. Große Bedeutung für die Beurteilung einer Funktion hat nicht nur die Technik, sondern auch das Know-how der Entwickler. Die erste relationale Anwendung eines Teams ist immer kritisch.

Sind die problematischen Funktionen erst einmal bekannt, so wird man versuchen, diese über ein Redesign zu optimieren. Auch hier empfiehlt es sich nicht, die Funktion grundsätzlich immer allein in bezug auf die Performance zu optimieren. Möglichst lange sollte die Logik der Anwendung unberührt bleiben. Gerade in diesem Punkt ist die Intelligenz der Datenbank hinzuzuziehen.

Bei einem gegebenen Datendesign fällt es dem Anwendungsprogrammierer oft recht schwer, selbst den optimalen Zugriffspfad zu finden. Das gilt insbesondere dann, wenn die Query oder Funktion sehr komplex ist. Läßt man den Optimizer aber den aus seiner Sicht optimalen Pfad finden, so fällt es meist leichter zu überlegen, an welchen Stellen dieses Zugriffs optimiert werden kann. Ist ein solcher Punkt gefunden, so läßt sich dieses neue Umfeld dem Optimizer anbieten und seine Reaktion beobachten.

Je nach Optimizer gibt es allerdings Fälle, in denen die Entscheidung einfach falsch ist. Dann wird in der Regel versucht, die Syntax der Query zu ändern. Es empfiehlt sich dabei, auch die umliegenden Funktionen der Programme mit in die Überlegungen einzubeziehen, gilt es doch zu ermitteln, wo eine Logik kostengünstiger zu befriedigen ist: in der Programmiersprache oder in der Datenbank.

Wichtig sind Datenaufbau und die Zugriffspfade

Liegen große Datenmengen vor, sollte das physische Design der Daten an die jeweils kritischsten Funktionen angepaßt werden. Wichtig sind hier der physische Aufbau der Daten und die verfügbaren Zugriffspfade. Auch eine eventuelle Denormalisierung läßt sich in Betracht ziehen. Das heißt, daß das Zusammenfassen mehrerer Tabellen vorteilhaft sein kann, um der Join-Problematik zu begegnen - ein Verstoß gegen die erste Normalform, daher "Denormalisierung". Dies sollte allerdings nur in Ausnahmefällen geschehen, da die Join-Problematik nicht so schwerwiegend ist, wie häufig dargestellt.

Häufiger als das Zusammenfassen von Tabellen kommt der Einsatz von Aggregationstabellen vor - Tabellen, die Durchschnittswerte oder Summen für darunterliegende Dateitabellen enthalten. Die Wahl dieser Tabellen ist direkt abhängig von dem Mengengerüst und solle so variabel wie möglich gestaltet sein.

Das Festhalten von transitiv abhängigen Werten in Tabellen (dritte Normalform) ist dazu geeignet, die CPU-Belastung in manchen Fällen zu senken. Der Einsatz dieser Technik sollte allerdings genau bedacht und in seiner Auswirkung gründlich ausgetestet werden.

Plant man das logische und physische Design für große Datenmengen, so sollten die RZ-Funktionen ebenfalls geplant werden. Häufig ist ein reorganisationsloser Betrieb möglich, in anderen Fällen ist genau zu prüfen, welcher Aufwand beim Erstellen von Tabellen entsteht. Eine große Rolle spielt auch, die RZ-Funktionen auf die kritischen Funktionen auszurichten. Allerdings darf nicht vergessen werden, daß nun ein unternehmensweites Denken erforderlich ist.

An dieser Stelle sei noch ein. mal darauf hingewiesen, wie wichtig es ist, kritische Funktionen von Anwendungen zu dokumentieren. Man kann sich leicht vorstellen, welche Schwierigkeiten bei der Neueinführung einer Software bei großen Datenmengen entstehen, auf die schon andere Anwendungen zugreifen.

Ist das Sperrverhalten des Programms kritisch, so wird man sich über eine Optimierung in diesem Bereich Gedanken machen müssen. Möglichkeiten sind hier die Wahl des Zugriffspfades, der Einsatz von Dummy-Daten oder die systemtechnische Beeinflussung von Sperrgrößen und Sperrdauer. Manche Datenbanken erlauben es, unter verschiedenen Sperrverfahren zu wählen. Existiert diese Möglichkeit nicht, so muß man unter Umständen selbst ein Sperrverfahren codieren.

Ein wesentliches Problem beim Sperrverfahren liegt dann vor, wenn eine parallele Online- und Batch-Verarbeitung durchgeführt werden soll. Dann ist es wichtig, auch Batch-Funktionen genau zu designen, und zwar nicht nur im Bereich der Sperrverfahren und Zugriffswege.

Die logischen Arbeitseinheiten, die durch die Datenbank gesperrt werden müssen, führen nach ihrer Beendigung nicht nur zur Freigabe der Daten, sondern zu einem Eintrag in das Recover-Log, das die Datenbank führt. Für Batch-Verfahren ist daher das Restart-Recovery-Verhalten ein besonders wichtiger Punkt, der in Abhängigkeit von der Wahl der Sperrverfahren gestaltet werden muß. Wichtig ist die terminliche Planung, um Überschneidungen zu vermeiden.

Ist die Anwendung kritisch durch die hohe Dynamik der Daten, hilft es, die Update-Funktionen und die RZ-Abläufe zu optimieren. Die Möglichkeiten hier sind sehr stark von der Datenbank bestimmt. Wichtig wird eine genaue Kenntnis der physischen Datenverwaltung. Das Spiegeln von Datenbeständen, die Segmentierung von Dateien, die Wahl von Nummernkreisen, die anstatt eines Updates gelöscht und wieder belegt werden, oder auch das Verteilen der Daten selbst auf mehrere Tabellen bieten sich hier an. In vielen Datenbanken ist das komplette Löschen einer Tabelle weit weniger aufwendig als das Löschen eines Teils der Tabelle.

Systemparameter haben Einfluß auf die Leistung

Wird die Anwendung durch eine hohe Transaktionsleistung kritisch, so ist eine genaue Kenntnis der Systemvorgänge notwendig. Man muß sich in den meisten Fällen mit kleinen Optimierungsergebnissen zufriedengeben. Wichtig ist es, frühzeitig Performance-Tests durchzuführen. Die meisten Datenbanken bieten Möglichkeiten, die Funktionen zu "tracen". Ziel ist dann, die Punkte zu finden, in denen der größte Teil der Ressourcen verbraucht wird.

Einen großen Einfluß auf die Transaktionsleistung des Systems hat die Wahl der Systemparameter. Die ausreichende Zuteilung wesentlicher Ressourcen kann unter Umständen die gesamte Performance nachhaltig beeinflussen. Entscheidend sind die Sperralgorithmen und deren notwendige Ressourcen, die Funktionen für Erstellung und Löschung von Verbindungen zwischen Benutzern und Datenbank, die Restart-Recovery-Funktionen (Logging), die I/O-optimierenden Funktionen (Buffer-Verwaltung) und die internen Funktionen der Datenbank.

Optimierung als iterativer Vorgang

Sind die einzelnen kritischen Funktionen ausreichend optimiert, sollte mit der Realisierung begonnen werden, wobei der Gesamtaufwand zur Erreichung einer hohen Performance nicht die Vorteile und Einsparungen einer solchen Vorgehensweise aufzehren darf. Daher empfiehlt es sich, mit den kritischen Funktionen zu beginnen. Noch während der weitergehenden Realisierung sollten sie getestet werden, speziell auch unter Produktionsbedingungen.

Damit beginnt ein iterativer Vorgang, der über die Lebenszeit der Anwendung besteht. Reicht die Performance von Funktionen nicht aus oder scheinen die Kosten so hoch zu sein, daß sich eine Optimierung lohnt, wird man wieder in die Phase des Designs zurückgehen, um die Funktion neu zu gestalten.