Zusammenfassung_Date..

Werbung
Embedded SQL (checked).......................................................................................................... 2
JDBC (checked) ......................................................................................................................... 4
Objekt-Relationale Datenbanken am Beispiel von Oracle (checked) ........................................ 6
Optimierung (checked) ............................................................................................................... 7
Trigger (checked) ....................................................................................................................... 9
Deadlock (checked) .................................................................................................................. 10
Hash-Verfahren (checked) ....................................................................................................... 11
B-Tree-Verfahren (checked) .................................................................................................... 12
Optimizer (checked) ................................................................................................................. 13
Replikation (checked) .............................................................................................................. 14
ANSI/SPARC-Architektur (checked) ...................................................................................... 15
Das hierarchische Modell (checked) ........................................................................................ 16
DRDA (checked) ...................................................................................................................... 16
Datenbank-Entwurf (checked) ................................................................................................. 17
Objektorientierte Datenbanken (checked) ................................................................................ 19
Embedded SQL (checked)
Um SQL-Anweisungen in einen Quelltext einzubetten, ich möchte hier als Beispiel PL/1
entsprechend den Vorlesungsfolien verwenden, bietet sich die Nutzung von Embedded SQL
an.
Hier werden die SQL-Anweisungen direkt in den Quelltext eingebettet, vor der jeweiligen
SQL-Anweisung steht noch das Schlüsselwort EXEC SQL welches der Precompiler später
benötigt um die eingebettete Anweisung zu erkennen.
Beispielsweise wäre so die folgende Programmierung möglich:
EXEC SQL
UPDATE KUNDEN
SET NAME=MUELLER
WHERE KUNDEN-NR=56789;
Alternativ können auch Hostvariablen verwendet werden, diese beginnen immer mit einem :
(in der SQL-Anweisung), also z.B. :Kundenname. Hostvariablen sind Variablen, die in dem
PL/I-Programm (der Hostsprache) UND in dem eingebetteten SQL verwendet werden
können.
Zu Beginn eines Programms, welches Hostvariablen benötigt, müssen diese deklariert werden
(damit der Precompiler dieser als Hostvariablen erkennt):
EXEC SQL
BEGIN DECLARE SECTION
DCL sqlstate CHAR(5);
DCL kundennummer CHAR(15);
...
EXEC SQL
END DECLARE SECTION;
Ausnahmebedingungen können ebenfalls abgefangen werden, z.B. wenn eine SelectAnweisung die leere Menge ergab:
EXEC SQL
WHENEVER NOT FOUND GOTO LABEL;
Wobei “LABEL” eine beliebige Sprungmarke des Programms in der Hostsprache ist.
Der Precompiler hat die Aufgabe, die eingebetteten SQL-Anweisungen in Konstrukte der
jeweiligen Programmiersprache zu übersetzen (diese Konstrukte arbeiten dann mit
Bibliotheken, die die Verbindung zur Datenbank herstellen). Er arbeitet vor der
Übersetzungszeit das Programm sequentiell ab. Zusammen mit dem jeweiligen DBMS
überprüft er, ob die Syntax der SQL-Anweisungen korrekt ist, ob die Referenzen stimmen und
die nötigen Rechte vorhanden sind.
Bei statischen Anwendungen kann außerdem noch der optimale Zugriffspfad bestimmt und
gespeichert werden. Bei dynamischen SQL-Anweisungen, die erst zur Laufzeit ermittelt
werden ist dies natürlich von Hause aus nicht möglich.
Um dynamische Anweisungen mit wiederholtem Zugriff zu beschleunigen gibt es auch in
Embedded SQL die Möglichkeit der Prepared Statements.
Beispielsweise:
EINSTRING = “INSERT INTO STUDENTEN VALUES (?,?);
EXEC SQL
PREPARE PREP_ANWEISUNG FROM :EINSTRING;
EXEC SQL
EXECUTE PREP_ANWEISUNG USER :VARIABLE1; :VARIABLE2;
Dynamische SELECT-Anweisung mit Deskriptor:
EXEC SQL BEGIN DECLARE SECTION;
material
char(10);
anzahl
Int;
regal
Int;
prep
char(18);
descriptorOut
char(18);
descriptorIn
char(18);
c1
char(18);
satz
char(512);
EXEC SQL END DECLARE SECTION;
…
satz = "SELECT material, anzahl, regal,
FROM demo
where material = ?";
EXEC SQL PREPARE prep FROM :satz;
EXEC SQL DECLARE c1 CURSOR FOR prep;
//Anlegen Descriptoren
EXEC SQL ALLOCATE DESCRIPTOR :descriptorOut;
EXEC SQL ALLOCATE DESCRIPTOR :descriptorIn
//Initialisieren Descriptoren
EXEC SQL DESCRIBE OUTPUT prep USING SQL DESCRIPTOR :descriptorOut;
EXEC SQL DESCRIBE INPUT prep USING SQL DESCRIPTOR :descriptorIn;
//die Eingabewerte in Descriptor Area füllen
material = "Schrauben";
EXEC SQL SET DESCRIPTOR :descriptorIn VALUE 1 DATA = :material;
//Cursor öffnen und einlesen
EXEC SQL OPEN c1 USING SQL DESCRIPTOR descriptorIn
//Lese und Verarbeitungsschleife
EXEC SQL FETCH c1 INTO SQL DESCRIPTOR
EXEC SQL GET DESCRIPTOR descriptorOut
EXEC SQL GET DESCRIPTOR descriptorOut
EXEC SQL GET DESCRIPTOR descriptorOut
...
//Lese und Verarbeitungsschleife Ende
EXEC
EXEC
EXEC
EXEC
SQL
SQL
SQL
SQL
descriptorOut;
VALUE 1 :material = DATA,
VALUE 2 :anzahl
= DATA,
VALUE 3 :regal
= DATA,
CLOSE c1;
DEALLOCATE PREPARE : prep;
DEALLOCATE DESCRIPTOR descriptorOut;
DEALLOCATE DESCRIPTOR descriptorIn;
JDBC (checked)
JDBC ist eine normierte Schnittstelle von einem JAVA-Programm zum
Datenbankmanagement-System. Dabei beruht JDBC auf CLI und SQL/92 Entry-Level. Es hat
sich in letzter Zeit auch eine Erweiterung zum herkömmlichen JDBC, nämlich JDBC 2.0
etabliert, mit vielen nützlichen Erweiterungen, wie änderbaren Ergebnismengen und anderem.
Die meisten DBMS Hersteller bieten mittlerweile Klassen mittels derer man über JDBC
direkt auf das jeweilige DBMS zugreifen kann, alternativ kann auch noch eine JDBC-ODBCBridge oder ein Pure-JAVA-Treiber verwendet werden.
Ich möchte nun direkt vorstellen, wie man eine Verbindung zu einem DBMS in einem JAVAProgramm initialisiert und wie man SQL-Befehle ausführt.
try {
Class.forName(“jdbc.jconnector-mysql“); //Laden eines Treibers um über JDBC auf
//MySQL zuzugreifen
//Vom Treibermanager, bei dem der MySQL-Treiber mittels dem obigen Befehl automatisch
//registriert wurde eine Verbindung anfordern:
Connection conn = DriverManager.getConnection(“jdbc:heubes.de”, “root”, “geheim”);
} //Exceptions abfangen…
Generell kann man über die Klasse Statement Statements anlagen, die man dann als Queryoder Update-Anweisung an das DBMS übertragen kann.
Hierzu ein kurzes Beispiel:
int count;
Statement einStmnt = conn.createStatement();
Und dann für ein Update:
count = einStmnt.executeUpdate(“CREATE TABLE TESTTABLE (NUMMER INT, TEST
VARCHAR(20))”);
Oder im Falle einer Select-Anweisung:
ResultSet = einStmnt.executeQuery(“select * from tab”);
Hier erhalten wir als Ergebnis ein ResultSet, selbiges entspricht dem Cursor-Konzept und
wird auch ähnlich behandelt, nur eben „objektorientiert”, auf diesen Aspekt komme ich später
noch detaillierter zu sprechen.
Soll eine Select-Anweisung , z.B. „SELECT * FROM Kunden WHERE Ort = ?“ mit
wechselnden Parametern ausgeführt werden, so kann dies die Klasse Statement nicht
gewährleisten, hier wird das PreparedStatement-Interface benötigt.
Die Verarbeitung sieht dann folgendermaßen aus:
PreparedStatement prepStmnt = conn.prepareStatement(“SELECT * FROM Kunden WHERE
Ort = ?“);
Bevor diese Anwendung ausgeführt werden kann, soll das ? mit einem Wert, der sich ändern
kann, wiederholt ausgeführt werden.
Dieses Besetzen wird über die Operation setXXX(a,b) realisiert. „setString“ wird es
anzunehmender Weise in unserem Fall sein:
prepStmt.setString(1, „Dortmund“);
Dieser Befehl richtet sich nach folgender Syntax:
setXXX(a,b)
a = Position des Platzhalter, bei uns wird nur ein Fragezeichen, also ein Platzhalter verwendet,
entsprechend benötigen wird den 1. Platzhalter, daher muss hier eine 1 eingesetzt werden.
Das b ist der entsprechende Wert, oben wird der Ort auf „Dortmund“ gesetzt.
Die volle SQL-Anweisung würde also für diese Iteration so aussehen:
SELECT * FROM Kunden WHERE Ort = „Dortmund“.
ResultSet rs = prepStmnt.executeQuery() liefert uns also einen ResultSet für den jeweils
eingesetzten Platzhalter.
Vorteil dieser Methode gegenüber normalen Statements mit zusammengesetzten Strings:
Wenn viele Durchläufe nötig sind, kann die SQL-Anweisung zur Analyse und
„Vorübersetzung“ an das DBMS geschickt werden. Der Optimizer muss also weniger häufig
bemüht werden, wodurch diese Vorgehensweise als deutlich performanter als die mit
„normalen“ Statements bezeichnet werden kann.
Werden Stored Procedures benötigt, so kann auch das CallableStatement-Interface verwendet
werden.
Abschließend möchte ich noch die Weiterverarbeitung des ResultSets kurz erläutern:
Wir benötigen eine Schleife zum durchlaufen der einzelnen „Tupel“ des Sets:
while (rs.next())
{
String Temp = rs.getString(“Kundenname“);
//...
}
Hier würde aus dem aktuellen Tupel der Wert der Spalte „Kundenname“ ausgelesen, rs.next()
ist solange True, wie es ein Nachfolgetupel gibt.
Objekt-Relationale Datenbanken am Beispiel von Oracle (checked)
Der objekt-relationale Ansatz von Oracle baut auf SQL 92 auf, das bedeutet, er ist nicht von
Grund auf objektorientiert (wie z.B. im Fall von Poet oder Versant) sondern beinhaltet die
gesamte Relationentheorie nach SQL-Standard und wurde um objektorientierte ZUSÄTZE
erweitert.
Folgende Erweiterungen hinsichtlich eines objektorientierten Datenbankmanagementsystems
wurden bei Oracle vorgenommen:
Es wurde das Konzept der benutzerdefinierten abstrakten Datentypen implementiert. Des
weiteren werden Tupeltypen (row) zur Verfügung gestellt.
Basierend auf den abstrakten Datentypen wird das Subtypkonzept, mit Vererbung, ermöglicht.
Es ist somit auch möglich, Attribute und Funktionen zu redefinieren, es handelt sich hierbei
also um Erweiterungen die klar in Richtung Polymorphismus abzielen (auch wenn dies
natürlich ohne echte Vererbung nicht möglich ist – daher auch der Zusatz in Richtung).
Erstmalig sind durch die objektorientierten Erweiterungen von Oracle auch nicht atomare
Wertebereiche für Attribute (im Zusammenhang mit row) möglich, hierzu benötigt man dann
die Typkonstruktoren list, set und multiset.
Oracle 8 stellt in Erweiterung hierzu auch Objekt-Views zur Verfügung, Kollektionstypen
werden aber auf eine Tabelle begrenzt.
Nicht implementiert wurde hingegen eine echte Vererbungsstruktur mit echten Klassen usw.
Ich möchte nun noch auf ein paar Aspekte des Kompromiss „objekt-relational“ (Kompromiss
zwischen den alten und bewährten relationalen Aspekten und den „neu-modernen“
objektorierentierten Konzepten) eingehen.
Grade dieser Kompromiss hat nämlich in der Praxis eine herausragende Bedeutung, ist es
doch so, dass sich in der Softwareentwicklung die Objektorientierung weitestgehend
durchgesetzt hat, so kann sie im Bereich der Datenbanken mehr oder minder als Flopp
bezeichnet werden – Systeme wie poet finden keine breite Verwendung, vielmehr gewinnen
objekt-relationale Systeme wie Oracle, db2 oder auch der Microsoft SQL-Server immer mehr
an Bedeutung.
Für den Datenbankadministrator besonders erleichternd in Oracle 8 ist die Möglichkeit, User
Defined Datatypes zu verwenden. Im Klartext bedeutet dies, dass dem
Datenbankadministrator und oder Anwendungsentwickler nun auch direkt auf
Datenbankebene die Möglichkeit gegeben ist, eigene Objekt und Kollektionstypen anzulegen.
Objekttypen bestehen erwartungsgemäß aus dem Namen des Typs, der Menge der Attribute
und den Methoden, ich möchte hier ein kurzes Beispiel für das Anlegen eines Objekttyps
anführen:
Create type Typ_Kontakt as Object (
Tel_private varchar(20),
Tel_gesch varchar(20),
Fax varchar(20),
E-Mail varchar(20)
);
Dieser Typ kann nun in der gesamten Datenbank zur Speicherung der Kontaktdaten eingesetzt
werden, somit findet hier das Prinzip der Wiederverwendung Anwendung.
Ähnlichen funktioniert dies für Methoden.
Es existiert allerdings keine echte Kapselung, so dass auf get- und set-Operationen verzichtet
wurde.
Weitere nützliche Erweiterungen von Oracle sind, dass Typen als Arrays angelegt werden
können:
Create type EinArray as
Varray(spalten) of number;
Ähnlich nützlich ist die Verwendung von nested tables, wo Tabellen in andere Tabellen
“eingenistet” werden können.
Ebenfalls noch nicht implementiert ist die Objektidentität, dies wird für kommende Releases
erwartet.
Optimierung (checked)
Oft kommt es in der Praxis vor, dass ein bestehendes Datenbanksystem unter längerer Last
von Tag zu Tag langsamer wird und der verantwortliche Datenbankadministrator sich fragen
muss, woran das liegen kann und entsprechende Strategien entwickeln muss um ein weiteres
Absinken der Transaktionsgeschwindigkeit zu verhindern.
Doch auch bei der Neuinstallation sollte von Anfang an auf eine weitergehende Optimierung
geachtet werden.
Wichtig bei einem Datenbanksystem ist zunächst einmal, dass die Festplatten, sei es SCSI,
RAID oder nur normales IDE nicht überlastet sind. Hierfür besitzen Server-Systeme meist
viele Festplattenschächte.
Auf Grund häufiger paralleler Zugriffe, sollten sich die folgenden Bereiche eines
Datenbanksystems möglichst auf verschiedenen Platten befinden:
1. Die DBMS-System-Dateien, je nach Betriebssystem Binaries, DLLs,
Installationsumgebungen und ähnliches, dies ist der Kern des Datenbanksystems.
2. Das Data Dictionary sollte sich ebenfalls auf einer gesonderten Platte, oder zumindest
Partition befinden, da in ihm Schemata und Meta-Informationen gespeichert werden.
3. Sämtliche Log-Files sollten möglichst auf einer eigenen Platte gespeichert werden, je nach
Konfiguration können in kürzester Zeit riesige Datenmengen an Log-Files entstehen.
4. Temporärer Speicherplatz ist auch für Datenbanken von besonderer Wichtigkeit, grade bei
großen Datenbanken sind hier immense Plattengrößen erforderlich (genau so wie beim
Arbeitsspeicher, 3 GB RAM keine Seltenheit) um die bei umfangreichen SELECT oder
SORT / ORDER Aufgaben nötigen Zwischenstände zwischenzuspeichern.
5. Jede große Datenbank (also das oder die Datenfile/s) sollte eine eigene Festplatte
zugewiesen bekommen.
6. Sollten viele Indexe eingesetzt werden, so kann sich auch für die Speicherung der IndexStrukturen ein eigener Festspeicher lohnen, ansonsten können sie auf einer separaten Partition
auf der Hauptsystemplatte gespeichert werden.
Soll die Plattenverteilung bei einem bestehenden System umgestellt werden, so ist dies meist
nur Nachts unter vorübergehender Außerbetriebnahme des Systems möglich!
Weniger aufwendig bei einem zu langsam gewordenem Datenbanksystem (natürlich auch bei
einem neu installiertem!) ist die gezielte Implementation von Indexen.
Einsatzmöglichkeiten von Indexen gibt es viele, zu den wesentlichsten gehört sicherlich die
Möglichkeit den direkten Zugriff auf bestimmte Datensätze zu haben, ohne einen kompletten
sequentiellen Durchlauf starten zu müssen. Über Indexe sind meist auch Bereichsanfragen an
bestimmte Tabellen oder gar ganze Datenbanken möglich.
Indexe sind vom Prinzip her in den meisten Fällen sortiert, so dass eine Vorsortierung
vorhanden ist und ein wiederholtes Sortieren entfällt.
Für Indexe eignen sich generell Schlüsselspalten oder Spalten die in Join, Having und Sort
Operationen enthalten sind.
Gegebenenfalls kann eine Partitionierung Zugriffe extrem beschleunigen. Beispielsweise
könnte man eine Tabelle, die Aufträge enthält horizontal partitionieren und jedes Jahr eine
andere Festplatte verwenden, dies verhindert, dass von Jahr zu Jahr das Gesamtsystem
langsamer wird.
Vertikale Partitionierungen sind dagegen wegen der zusätzlichen Schlüssel, die eingefügt
werden müssen weniger zu empfehlen.
Das Prinzip des Clusterns basiert auf den gleichen Grundsätzen, im Grunde ist die oben
beschriebene horizontale Partitionierung nichts anderes als Clustering, Daten die
zusammengehören werden zusammen gespeichert, genau das geschieht in diesem Beispiel,
wird in der Praxis sehr häufig angewendet!
Gegebenenfalls können Tabellen umstrukturiert werden, so dass NOT NULL-Spalten am
Anfang stehen, dann die mit fester Länge und am Ende schließlich die mit variabler Länge
stehen – dies wird in den meisten Fällen aber so oder so schon von vornherein geschehen sein.
In vielen Fällen, wo viele lesende Transaktionen stattfinden, kann eine Denormalisierung, die
teure JOINs vermeidet, die Anwendung extrem beschleunigen, daher sollte überdacht werden,
ob es im konkreten Fall Sinn macht.
Aber nicht nur die Datenbankinstallation kann optimiert werden, auch die auf das System
erfolgenden Zugriffe bzw. die Anwendungen die selbige erzeugen müssen optimiert werden.
So kann es deutliche Performance-Steigerungen bringen, wenn sämtliche die Datenbank
verwendenden Anwendungen bei SELECT-Abfrage statt dem * die wirklich benötigten
Spalten aufführen und nicht benötigte weglassen.
Auch sollten stets korrekte Datentypen verwendet werden, um unnötige Konvertierungen zu
vermeiden.
Ebenso ist der Index bei zu kleinen Datenbanken oder auf falsche Spalten als eine deutliche
Performance-Bremse zu sehen, da er bei ändernden Transaktionen stets mit aktualisiert
werden muss.
Die Benutzung von between, like, or und not sollte minimiert werden, um dem
Datenbanksystem Arbeit abzunehmen.
Prepared Anweisungen können den Optimizer und die gesamte Datenbank deutlich entlasten!
Gegebenenfalls kann das manuelle Setzen von Sperren und manuelle Commit-Anweisungen
die Datenbank deutlich beschleunigen, wenn das vom DBMS verwendete Sperrprinzip für
den jeweiligen Fall ungeeignet ist, auch die Nebenläufigkeit kann in bestimmten Fällen erhöht
werden.
Man sollte eventuell versuchen, nicht all zu viele JOINs zu verwenden, da diese viele
Ressourcen verbrauchen und somit „teuer“ sind.
Und wenn denn gar nichts mehr hilft und das System einfach zu umfangreich geworden ist,
dann muss über ein verteiltes System nachgedacht werden, Stichwort ist hierbei Replikation,
also die Verteilung der Daten auf mehrere Datenbankknoten.
Hierbei ist der limitierende Faktor immer die Datenübertragung. Deutliche Vorteile liegen in
der erhöhten Ausfallsicherheit und in der erhöhten Lesegeschwindigkeit, auch die CPUAuslastung und der Arbeitsspeicher werden auf den einzelnen Maschinen meist proportional
weniger verwendet.
Großes Problem bei der Replikation: Ändernde Transaktionen, denn hier müssen zeitnah alle
Replikate aktualisiert werden.
Trigger (checked)
Trigger sind immer dann von Bedeutung, wenn an einer Tabelle etwas geändert wird und vor
oder nach dieser Änderung etwas Bestimmtes passieren soll.
Warum werden nun Trigger unterstützt?
Fragen wir uns, wie wir die oben kurz angerissenen Funktionen von Triggern implementieren
würden, würde uns ein DBMS keine Trigger bieten (wie z.B. derzeit noch MySQL).
Wir müssten wohl oder übel alle Trigger-Funktionen durch die Anwendung realisieren, das
bedeutet, vor einem Update, nach deinem Update, vor dem Löschen, nach dem Löschen, vor
dem Einfügen, nach dem Einfügen usw. bestimmte Operationen aufrufen.
Das ist auch problemlos möglich, zumindest solange wir nur mit einem Client auf der
Datenbank arbeiten – gäbe es jeweils nur eine Anwendung, die auf eine Datenbank zugreift,
könnte man in der Tat relativ getrost auf Trigger verzichten – aber genau das ist in der Praxis
selbstredend nicht der Fall, viel mehr ist es so, dass Datenbanken grade dann benutzt werden,
wenn viele Anwendungen/Clients auf eine oder gar mehrere Datenbasen zugreifen müssen.
Im Falle einer Client-Server Architektur wäre die Implementation ohne Trigger schon
denkbar schwierig, denn woher soll ein Programm wissen, ob nicht vielleicht ein anderes grad
etwas ändert um dann entsprechend eines Triggers zu reagieren? Hierfür müsste die
Datenbank entsprechende Konstrukte zur Verfügung stellen und es müssten ein oder mehrere
Clients die Datenbank ständig auf Änderungen überwachen.
Dieser Misstand war den Machern von SQL bekannt und sie haben befunden, dass die
einfachste Lösung eine DMBS-Seitige ist, nämlich dass auf Seite der Datenbank direkt bevor
z.B. eine Einfügen-Transaktion gestartet wird, bestimmte Aktionen gestartet werden können,
und aus diesem Grund gibt es Trigger, sämtliche Eigenprogrammierungen sind in den meisten
Fällen unangebracht, da viel zu komplex, wie ich oben schilderte, folglich sind Trigger eine in
vielen Fällen unverzichtbare Vorrichtung für den Datenbankadministrator und
Anwendungsentwickler.
Ein typisches Anwendungsbeispiel für die Benutzung von Triggern ist die Protokollierung
bzw. das Logging der Änderungen an einer Tabelle, nach jeder Änderung soll protokolliert
werden, wann die Änderung von wem durchgeführt wurde.
Hierfür wird eine separate Tabelle benötigt, in der die Protokollierung eingefügt werden kann.
Nehmen wir an, wir wollen in einer Linux-Umgebung protokollieren, welche User auf einem
PROFTPD-Daemon einen Account haben, an dem in den letzten Wochen Änderungen an den
Rechten vorgenommen wurden. Wir möchten also konkret loggen, bei welchem User von
wem in letzter Zeit wann welche Änderungen an den User-Rechten vorgenommen wurden.
Der Trigger könnte hier wie folgt aussehen:
CREATE TRIGER PROTOKOLL_UPDATE
AFTER UPDATE ON FTPUSER
FOR EACH ROW
BEGIN
INSERT INTO FTPUSER_PROTOKOLL
VALUES (USER, CURRENT TIMESTAMP, “NEUE RECHTE”)
END;
Ein entsprechender Trigger könnte dann beispielsweise auch noch für insert und delete
Operationen vorgenommen werden.
Obiges Beispiel zeigt, dass bei jedem Update der Tabelle FTPUSER ein neues Tupel in die
Tabelle FTPUSER_PROTOKOLL eingetragen wird.
Grade im Bereich der Protokollierung gibt es viele Anwendungsmöglichkeiten.
Doch auch in einem Warenwirtschaftssystem sind Beispiele denkbar, z.B. kann bei jedem
Update der Bestandstabelle überprüft werden ob bestimmte Mindestbestände unterschritten
wurden und schließlich eine entsprechende Stored Procedure aufgerufen werden oder
Ähnliches.
Weitere Anwendungsbereiche sind in der Replikation zu finden  der Trigger sorgt zum
Beispiel bei Primary Copy dafür, dass die Änderungen an alle anderen Knoten weitergegeben
werden.
Auch im Datenschutz können Trigger verwendet werden, z.B. um bestimmte Informationen
automatisch zu entfernen.
Schließlich sind Trigger eigentlich immer dann ein Ausweg, wenn die „normale“
Funktionalität eines Datenbanksystems nicht ausreicht, z.B. bei Zusammenhängen die über
einfache Referenzen oder Fremdschlüssel hinausgehen.
Deadlock (checked)
Grundsätzlich treten Deadlocks meist bei der Verwendung von Sperrverfahren auf. Ein
Deadlock lässt sich beschreiben als eine Verklemmung die zwei oder mehr Transaktionen bei
Zugriff auf ein und dieselben Daten in der Datenbank verursachen.
Hier ein Beispiel:
Es gibt eine Transaktion 1, diese hat verschiedene Aufgaben zu erledigen und sperrt dabei
unter anderem Tabelle A. Eine weitere Transaktion 2 läuft parallel ab und sperrt dazu unter
anderem Tabelle B. Nun benötigt Transaktion 1 Zugriff auf Tabelle B und Transaktion 2
zeitgleich Zugriff auf Tabelle A. Es entsteht eine Verklemmung, in der beide Transaktionen
dauerhaft laufen und jeweils auf die Freigabe der von der anderen Transaktion gesperrten
Tabelle warten.
Werden nicht entsprechende Maßnahmen ergriffen werden beide Tabellen ewig gesperrt
bleiben und beide Transaktionen ewig laufen. Unter Umständen kann durch solch eine
Verklemmung das ganze System zum Erliegen kommen!
Solche Verklemmungen nennt man daher auch Deadlocks.
Im Rahmen des SQL-Standards stehen für die Vermeidung von Deadlocks in der DDL mit
SET TRANSACTION einige Möglichkeiten zur Verfügung.
So kann hier die volle Serialisierbarkeit mit SERIALIZABLE eingestellt werden. Wenn bei
mehrfachen Selects in einer Transaktion auch Datensätze angezeigt werden sollen, die
zwischenzeitlich von anderen Transaktionen geändert wurden, so kann mit SET
TRANSCATION schließlich in den REPEATABLE READ-Modus geschaltet werden.
Dürfen bei einem wiederholten gleichartigen Commit in einer Transaktion auch bereits
gelesene Zeilen von einer anderen Transaktion geändert werden, so kann der READ
COMITTED-Modus aktiviert werden, sollen darüber hinaus auch Zeilen gelesen werden
können, die von anderen Transaktionen noch nicht freigegeben wurden, so sollte der READ
UNCOMMITTED-Modus aktiviert werden.
Des weiteren gibt es laut SQL-Standard in der DCL die Möglichkeit mit COMMIT explizit
eine Transaktion zu beenden (erfolgreich) oder sie mit ROLLBACK zurückzusetzen, auch
können mit LOCK TABLE Tabellen explizit gesperrt werden.
In aktuellen DBMSen gibt es im wesentlichen 2 Lösungsansätze zum Umgehen von
Deadlocks, da wären zum einen verschiedene Sperrverfahren, für die jeweils Strategien zum
Umgehen von Deadlocks entwickelt werden müssen und zum anderen die optimistischen
Synchronisationsverfahren, bei denen generell keine Deadlocks auftreten können.
Als einfachster Vertreter der Sperrverfahren bleibt das Zwei-Phasen-Sperrprotokoll zu
nennen, hier werden in der ersten Phase die benötigten Sperren gesetzt, nach Durchführung
der Transaktion werden in der zweiten Phase alle Sperren wieder entfernt. Die
Deadlockgefahr ist relativ hoch.
Als Erweiterung wurde das Zeitstempelverfahren eingesetzt, wo jedem sperrbaren Objekt bei
einer Sperre ein Zeitstempel zugewiesen wird, kommt es zu einem Konflikt kann so z.B. eine
Transaktion bevorzugt werden, sofern sie jünger ist als die Transaktion die das betroffene
Objekt gesperrt hat, dies garantiert die Deadlockfreiheit!
Bei optimistischen Synchronisationsverfahren ist, wie oben bereits erwähnt die
Deadlockfreiheit garantiert, dieses Verfahren sollte nur angewendet, wenn nur selten
Transaktionskonflikte auftreten. Hier wird nicht in den Ablauf der Transaktion eingegriffen
sondern erst am Ende der selbigen überprüft ob ein Konflikt aufgetreten ist.
Hash-Verfahren (checked)
Das Hash-Verfahren ist eins von vielen, um einen Index bei einer Datenbank zu realisieren.
Ein Index wird auf bestimmte Spalten gelegt, um Zugriffe über diese Spalten auf die Tabelle
entscheidend zu beschleunigen, das macht entsprechend nur dann Sinn, wenn die Spalten
auch häufig aufgerufen werden und die Datenmengen entsprechend groß sind, ansonsten kann
der Einsatz eines Indexes mehr „kosten“ als die entsprechenden Operationen so
durchzuführen!
Es gilt nämlich zu beachten, dass ein Index auch bei Änderungen (also Einfügen, Ändern,
Löschen) mitgepflegt werden muss.
Das Hash-Verfahren zeichnet sich dadurch aus, dass die Verbindung zwischen Schlüssel und
der Tupel-ID (also dem „Link“ zum eigentlichen Datensatz) über eine Funktion (der HashFunktion) realisiert ist.
Entscheidender Vorteil dieser Methode ist, dass keine Lesezugriffen zum Finden der TID
nötig sind, also keine Plattenzugriffe den Vorgang ausbremsen.
Dies hat zur Folge, dass Hash-Verfahren besonders schnell sind.
Hash-Verfahren sind meistens auf mathematischen Funktionen und Gleichungen basiert, die
auf Grundlage mathematischen Grundeigenschaften in speziellen Algorithmen einem
Schlüssel ein Tupel durch Berechnung bereitstellen.
Ganz unproblematisch sind Hash-Verfahren aber dennoch nicht (sonst würde man nichts
anderes mehr einsetzen):
So kann es bei den Berechnungen passieren, dass einem Schlüssel zwei Tupel-IDs zugeordnet
werden können, da die Hash-Funktionen oft einen zu tolerierenden Ungenauigkeitsfaktor
haben können.
Für solche Probleme gibt es jedoch effektive Lösungsmöglichkeiten.
Problematischer wirkt es sich da schon aus, dass keine direkten Bereichsanfragen möglich
sind, da die Hash-Funktion immer nur ein Tupel direkt findet und nicht einen ganzen Bereich
(z.B. Rechnungen von 2002).
Des weiteren ist kein Zugriff in sortierter Reihenfolge möglich, wieder aus dem gleichen
Grund, weil die Funktion uns nur ein Tupel zurückliefert.
Für den Fall, dass der Index allerdings zunächst sortiert und aufgelistet wird, ist beides dann
doch möglich, wobei zu überlegen ist, ob dann nicht andere Verfahren, wie zum Beispiel der
binäre Baum vorzuziehen sind.
Auf Grund der oben genannten Probleme mit den Hash-Funktionen, ist die Auswahl
derselbigen oft ein Grund, woran die Nutzung dieses Verfahrens scheitert.
Trotz allem ist dieses Verfahren auf Grund der schnellen Zugriffsmöglichkeit nicht
wegzudenken und von unverzichtbarer Bedeutung.
B-Tree-Verfahren (checked)
Zunächst einmal ist zu sagen, dass ausgewogene k-Trees ein Spezialfall von indexsequentiellen Dateien sind.
Dies bedeutet, dass die meisten Vorzüge aber auch Nachteile von index-sequentiellen
Dateien auch für das Speicherverfahren des Indexes in Forum von binären Bäumen gilt.
Grundsätzlich gelten erstmal die folgenden Vorraussetzungen:
Die Speicherung erfolgt auf den Datenseiten sequentiell in Schlüsselreihenfolge. Im Index
wird dann für jede Daten-Seite der Schlüssel zusammen mit der Tupel-ID des letzten Records
jeder Datenseite gespeichert, dies ist dann jeweils der Schlüssel mit dem größten Wert.
Das k bezeichnet die für den Baum vereinbarte Schlüsselanzahl. Der Baum muss außerdem
zwingend sortiert sein und jeder Knoten enthält entweder k oder 2k Knoten, dadurch
resultiert, dass der Weg von der Wurzel bis zu jedem Blatt gleich lang ist.
Besonders positiv wirkt sich diese Struktur dann aus, wenn wir nun zu einem Schlüssel im
Baum eine Tupel-ID finden wollen, der erste Zugriff ermöglichst uns (bei k=100) bereits das
Auffinden von 100 TIDs, mit dem zweiten werden es dann schon 10.000!
Das bedeutet, dass die Anzahl der auffindbaren Schlüssel exponentiell steigt und mit extrem
wenigen Zugriffen extrem viele Schlüssel aufgefunden werden können.
Aus diesen sehr erfreulichen Eigenschaften resultiert sicherlich die weite Verbreitung von
binären Bäumen im Bereich der Indexe, der Zugriff ist so schnell wie bei kaum einem
anderen Verfahren – nicht nur bei Datenbanken, auch bei anderen Verfahren in der Informatik
haben sich binäre Bäume als hervorragende Speicherstrukturen erwiesen.
Es ist problemlos möglich in diesem Baum ab einem bestimmten Knoten Bereichsanfragen zu
starten, da vom Prinzip des Baumes her schon alles exakt sortiert ist, ist natürlich auch ein
sortierter Zugriff problemlos möglich.
Großes Problem der binären Bäume:
Reorganisation: Die Daten in der Datenbank verändert sich, der Index muss aktualisiert
werden, hierfür muss der komplette Baum neu aufgebaut werden, dies verursacht hohe Kosten
in Bezug auf Plattenzugriffe, Speicher- und Cacheauslastung, sowie natürlich der
Prozessorauslastung.
Dieses generelle Problem der Indizierung ist bei Bäumen besonders prägnant, da ein
balancierter Baum eigentlich schon bei kleineren Änderungen komplett neu aufgebaut werden
muss, damit er auch balanciert bleibt.
Je öfter der Index geändert wird, desto größer ist das Problem.
Relationale DBMSe benutzen dieses Verfahren trotz allem vorherrschend, zur Entschärfung
des Problems wird die Indizierung jedoch für besonders häufig geänderte Indexe auch mit
anderen Verfahren durchgeführt.
Da dies jedoch DBMS-interne Vorgänge sind, hat der Datenbankadministrator auf diese
Vorgänge kaum Einfluss.
Optimizer (checked)
Aufgaben des Optimizers eines Datenbankmanagementsystems ist die Ermittlung von
Zugriffspfaden. Darüber hinaus soll ein möglichst optimaler Zugriffspfad ermittelt werden.
Dieser günstige Pfad führt dann zu einer Beschleunigung der Ausführungsgeschwindigkeit.
Zur Ermittlung des günstigen Pfades gibt es im wesentlichen 3 Möglichkeiten:
Die erste ist auch gleich die trivialste, der Optimizer sucht einfach „drauf los“ und der nächst
kürzeste Pfade wird ausgewählt, ansonsten gibt es hierbei keine konkreten Kriterien zur
Pfadfindung.
Bei den zunächst eingesetzten regelbasierten Optimizern wurden bestimmte Regeln
eingesetzt, um die günstigsten Zugriffspfad zu finden.
Neuerdings schließlich, bei den kosten-basierten Optimizern werden die jeweils anfallenden
Kosten für Plattenzugriffe, Prozessorbelastung und den Arbeitsspeicher abgeschätzt und so
ein kosten- und leistungsoptimaler Zugriffspfad ausgewählt.
Im Relation zu den Optimizern, die unsystematisch und ohne Regeln vorgingen, waren die
regelbasierten Optimizer schon ein gewaltiger Fortschritt, während herkömmliche Optimizer
noch sequentiell meist die kompletten Tabellen auslesen mussten, erlauben regelbasierte
Optimizer meist direktere Zugriffspfade, die sich meist an der Reihenfolge der SELECTAnweisungen im aktuellen Query ausrichtet und danach auf die möglichst häufige
Ausnutzung von Indexen zur Erhöhung der Geschwindigkeit abzielt.
Nutzt man als Datenbankadministrator einen regelbasierten Optimizer, so kann man dessen
Ausführung meist dadurch beeinflussen, in dem man Indexe so setzt, dass sie für häufig
benutzt Abfragen dem Optimizer direkt zur Vefügung stehen. Unter Umständen wertet der
Optimizer auch die Reihenfolge der Tabellen in einer SELECT-Anweisung aus. Es kann
daher sinnvoll sein, die SELECT-Anweisungen so zu formulieren, dass der regelbasierte
Optimizer, entsprechend der verwendeten Regeln möglichst direkt über Indexe auf Daten
zugreifen kann und nicht sequentiell lesen muss.
Es wird deutlich, dass es keine gute Lösung ist, wenn der Optimizer an Hand von Regeln
vorgeht und die Kosten außer Acht lässt, dies wird häufig zu Systemüberlastungen führen,
und auch der Index und optimierte SELECT-Anweisungen werden hier wenig helfen.
Daher sind kosten-basierte Optimizer vorzuziehen, da diese zunächst keine starren Regeln
verwenden sondern alle Möglichkeiten in Betracht ziehen und an Hand der geschätzten
Kosten einen optimalen Zugriffspfad auswählen.
Eine Möglichkeit, den Optimizer nicht ständig aufzurufen wäre beispielsweise, die bereits
ermittelten Zugriffspfade abzuspeichern, denn häufig werden dieselben Anfragen gestellt.
So kann der Optimizer deutlich entlastet werden und nur bei „ganz neuen“ Anfragen wird er
noch benötigt. Grade auch kosten-basierte Optimizer, die die Systemlast reduzieren sollen,
verwenden solche Verfahren.
Replikation (checked)
Bei der Replikation von Datenbanken bzw. Datenbankknoten ist das Ziel durch das Kopieren
bzw. Duplizieren von Datenbanken gezielte Redundanz zu schaffen.
Mit der Replikation kann eine deutlich größere Verfügbarkeit der Daten geschaffen werden,
des weiteren kann ein Lastausgleich einzelner Knoten leicht implementiert werden, woraus
eine Verbesserung des Datendurchsatzes resultiert. Bei lesenden Transaktionen ist die
Geschwindigkeit durch lokal vorgehaltene Kopien der Datenbanken um ein vielfaches höher,
als würden die Daten über ein Netzwerk übertragen.
Als besonderes Goodie ist es bei verteilten Informationssystemen möglich, z.B.
Laptops/PDAs/Smartphones von Unterwegs per GPRS+GSM/UMTS in eine
Firmendatenbank einzuklinken!
Dem gegenüber steht natürlich ein erhöhter Systemaufwand und auch eine erhöhte
Kommunikation zum Beispiel per TCP/IP über ein IP-Netzwerk.
Für lesende Transaktionen ist die Replikation als völlig unproblematisch zu sehen, hier bietet
sie nahezu nur Vorteile, der kaum Nachteile gegenüber stehen, problematisch wird es
allerdings bei ändernden Transaktionen, da hier ALLE Replikate möglichst in Echtzeit
aktualisiert werden müssen, was einen hohen Kommunikationsaufwand bedeutet, bei
langsamen oder abgebrochenen Übertragungen kommt es zu empfindlichen
Konsistenzproblemen, da die Daten in verschiedener Aktualität auf verschiedenen Knoten
liegen.
Es wird deutlich, dass geeignete Verfahren der Replikation, aber grade auch in Bezug auf
ändernde Transaktionen geeignete Verfahren der Synchronisation, spricht
Transaktionskontrolle in verteilten Informationssystemen von Nöten sind.
Auf alle Fälle ist die Verteilung sorgfältig zu planen und auf Grund der angerissenen
Probleme gilt, dass man sie nur so viel wie nötig einsetzen sollte.
Sämtliche Verfahren der Replikation hier in Gänze aufzuführen, würde sicherlich den
Rahmen dieser Zusammenfassung sprengen, ich möchte daher kurz einige wesentliche
Aspekte skizzieren und dann schließlich auf deren konkrete Implementierung in konkreten
relationalen Datenbanksystemen zu sprechen kommen.
Verbreitet für die globale Transaktionsunterstützung ist das 2-Phasen-Commit-Protokoll, bei
selbigem eine Transaktion von einem Koordinatorknoten aus (der die Transaktion initiiert)
nur dann durchgeführt wird, wenn alle anderen Knoten melden, dass die für diese Transaktion
bereit sind.
Nun muss auf den jeweiligen Knoten noch die Synchronisation gewährleistet sein, dies
geschieht meist mit Sperrverfahren, wie dem Zwei-Phasen-Sperrprotokoll oder dem
Zeitstempelverfahren. Alternativ setzen Systeme wie Microsoft Navision DB auch
optimistische Synchronisationsverfahren ein, wodurch die Deadlockgefahr deutlich
herabgesetzt/eliminiert wird.
Bei den Verfahren der Replikation als solches gibt es zwei verschiedene Verfahren, die
semantischen und die syntaktischen. Ich möchte hier kurz die syntaktischen vorstellen, hier
wäre zunächst Primary Copy zu nennen, wo von einem Knoten, auf dem Änderungen
erfolgten, die Kopien an die anderen Knoten verteilt werden schließlich wäre im Bereich des
unstrukturierten Quorums das Majority Consensus Verfahren zu nennen, wobei hier eine
Abstimmung von dem Knoten der ursprünglichen Knoten durchgeführt wird, der die
Transaktion initiiert, ist die Mehrheit der Knoten für die Replikation bereit, so wird diese
durchgeführt. Ähnlich läuft es im strukturierten Quorum, konkret beim Tree Quorum ab, nur
dass hier die Mehrheit der Ebenen bereit sein muss.
Dynamische Quoren bieten den großen Nachteil der Netzpartitionierung, sind aber flexibler,
da sie die jeweils aktuelle Knotenanzahl in die Abstimmung mit einbeziehen.
Alternativ gibt es noch das ROWA Verfahren, bei dem eine Transaktion von jedem
beliebigen Knoten lesen kann, die Replikate aber auf allen Knoten ändern muss.
Abschließend möchte ich noch kurz auf die eingesetzten Verfahren in Oracle und DB2
eingehen.
Oracle unterstützt das Prinzip der Basic Replication, welches dem oben genannten Primary
Copy Prinzip entsprecht, des weiteren wird Advanced Replication untersützt. Hier gibt es eine
symmetrische Replikation, wobei alle Replikate gleichberechtigt sind und die Änderungen an
die übrigen Replikate asynchron weitergereicht werden. Der DELETE-Konflikt wird nicht
behandelt.
Änderbare Snapshots sind eine Kombination der beiden grade genannten Verfahren in Oracle,
ROWA ist hingegen nicht vorgesehen.
Auch in DB2 ist ROWA nicht vorgesehen. Wie auch in Oracle wurde aber das Primary CopyVerfahren implementiert.
Bei der asynchronen Aktualisierung und Einsatz einer Primärtabelle wird die Netzlast
verringert, weil Änderungen zusammengefasst und erst dann übertragen werden.
Beim Verfahren der asynchronen Aktualisierung unter Einsatz einer Primärtabelle mit
änderbarem Kopien übernimmt zwar die Primärtabelle die Verteilung der Änderungen, die
Replikate können jedoch trotzdem geändert werden und schicken dann bei einer Änderung
einer entsprechende Mitteilung an die Primärtabelle.
ANSI/SPARC-Architektur (checked)
Laut der ANSI/SPARC-Architektur gibt es drei Ebenen: Die erste Ebene ist die externe Sicht,
die Benutzersicht, die zweite ist konzeptionelle, die globale Sicht, die dritte ist die interne
Sicht, die Speichersicht.
Das hierarchische Modell (checked)
In diesem Modell nimmt der Benutzer die Daten in einer hierarchischen Form wahr und muss
durch die Daten navigieren.
Ein Vertreter solcher Datenbankmanagementsysteme ist IMS/DB.
Die Struktur der Datenbank befindet sich dabei in einer Assemblerdatei, der IMSStrukturdatei.
Sichten, z.B. für die Rechteverwaltung, lassen sich über PCB-Files realisieren.
In PL/I stehen entsprechende Funktionen zur Navigation durch eine hierarchische Datenbank
zur Verfügung!
DRDA (checked)
DRDA ist eine von IBM 1999 festgelegte Architekturbeschreibung, die eine Menge von
Regeln bzw. Protokollen umfasst, um plattformübergreifend auf verteilte Daten zugreifen zu
können.
Hierfür werden Methoden zur koordinierten Kommunikation zwischen verteilten relationalen
Datenbankmanagementsystemen definiert.
Für den Benutzer bzw. die Anwendung wirkt der Zugriff hingegen genau so als wäre es ein
lokaler Zugriff.
Verschiedene Hersteller bieten bereits APIs an, die sich an DRDA orientieren, wobei
Vorreiter hierbei verständlicher Weise das aus dem selben Hause stammende DB2 ist.
DRDA realisiert den verteilten Zugriff im wesentlichen über drei Funktionen:
Der Database Server ist hierbei der eigentliche Datenbankserver, miteinander in
Kommunikation stehen der Application Server, der auf dem selben Rechner wie der Database
Server läuft, und der Application Requester, der Anforderungen aus Anwendungen von
Client-Rechnern aus durch das Netz an den Application Server schickt.
Der Application Server seinerseits reicht die entsprechenden Anforderungen an den Database
Server weiter, der mit Hilfe des DBMS die gwünschten Transaktionen vornehmen kann.
DRDA bietet insgesamt 5 Ebenen für die Unterstützung der Verteilung.
Da wäre als erstes die User-Assisted Distribution zu nennen, hier verteilt der Benutzer selbst
die Daten, ihm stehen nur die beiden Funktionen Daten zu extrahieren und Daten einzuladen
zur Verfügung.
Beim DRDA-Remote-Request kann ein einziger Rechner, die so genannte Single Unit of
Work, eine einzelne SQL Anweisung an einen entfernten Datenbankserver stellen.
Der selbe Fall liegt vor, wenn mehrere SQL-Anweisungen in einer Transaktion an einen
Server geschickt werden, nur wird hier von der Remote Unit of Work gesprochen statt von
der Single Unit of Work, wobei jedoch pro SQL-Anweisung nur ein DBMS angesprochen
werden kann.
Im vierten Fall, der Distributed Unit of Work, können mehrere SQL-Anweisungen gegen
mehrere entfernte DBMS gestellt werden, wobei auch hier pro SQL-Anweisung nur ein
DBMS angesprochen werden kann, sollen mehrere DBMSe auf einmal in einer SQLAnweisung angesprochen werden, so muss die vollständige Verteilung mit Distributed
Request verwendet werden.
Data Warehouse (checked)
Der Ansatz des Data Warehouse beruht auf dem Wunsch einer präzisen
Entscheidungsfindung bei Geschäftsvorfällen, hier wird ein so genanntes Decision Support
System (kurz DSS) gewünscht.
Erreicht wird dies durch die interaktive Verbindung von Regeln sowie Intuition von Experten
mit dem Ziel einer ad hoc-Modellierung jeweils neuer Situationen unter Simulation eines
kleinen Ausschnitts der realen Welt.
Ein Decision Support System bietet dabei verschiedene Reports, testet Hypothesen an Hand
konkreter Zahlen, erzeugt Modelle an Hand historischer Zahlen und erkennt aus selbigen und
aktuellen Daten unbekannte neue Trends.
Die Analysen im Data Ware House greifen mit den so genannten OLAP-Anforderungen, den
Anfragen aus dem OnLine Analytical Processing auf die Datenbank zu, daraus ergibt sich für
die Datenbank, dass die Daten themenorientiert zusammengefasst sein müssen und die Daten
pro Abfrage relativ groß sind.
Da meist auf historische Daten zugegriffen wird, kann meist von fast ausschließlich lesenden
Anfragen ausgegangen werden.
Und da die zur Verfügung stehenden Daten auch aktualisiert werden müssen, muss ein
regelmäßiges Update aus dem OLTP-System, dem System des OnLine Transaction
Processing, welches die aktuellen Geschäftsvorfälle unterstützt, stattfinden.
Zunächst werden die benötigten Daten aus dem OLTP-System extrahiert und ins Data
Warehouse importiert. Für diesen Datentransfer gibt es verschiedene Möglichkeiten, zunächst
einmal kann mit normalen SQL-Anweisungen in off-Zeiten des OLTP gearbeitet werden, hier
können auch bereits Pre-Joins vorgenommen werden, um die Performance zu steigern, zur
Performance-Steigerung komme ich gleich noch detaillierter.
Eine weitere Möglichkeit die Daten ins Data Warehouse zu portieren ist die SnapshotMethode, bei der eine bzw. mehrere Tabellen per Replikation übertragen werden.
Sollen die Daten zur Laufzeit des OLTP importiert werden, so bietet sich vor allem die
Methode des Log-Sniffings an, man benötigt hier ein Programm, dass die Log-Files des
OLTPS entsprechend auf Änderungen durchsucht.
Mittels Insert-, Update- oder Delete-Trigger ist des weiteren alternativ ein direktes
Übernehmen jeder Änderung möglich.
Stehen spezielle Data Extract Programme zur Vefügung, so sollten diese in den Off-PeakZeiten des OLTP verwendet werden.
Wie bereits oben geschildert, ist mit einer großen Datenmenge pro lesender Transaktion zu
rechnen. Um die Transaktionsdauer nicht unnötig in die Länge zu treiben, bietet sich hier die
Denormalisierung der OLTP-Datenbank an, so werden während des Ladens bereits PreAggregate angelegt und Pre-Joins über Tabellen angelegt, um die Anzahl der JoinOperationen im laufenden Betrieb zu minimieren. Pre-Joins führen zum STAR- oder gar zum
SNOWFLAKE-Schema.
Soll die Zeit in die Analysen mit einbezogen werden, so werden oft multidimensionale
Datenbanken eingesetzt.
Datenbank-Entwurf (checked)
Die wesentliche Fragestellung beim Datenbankentwurf ist, WIE man von einer
Vorstellung/Idee schließlich zu einem fertig gestellten Datenbanksystem kommt.
Dabei muss sichergestellt werden, dass die Anforderungen an das System über einen langen
Zeitraum erfüllt werden und dass das System nicht nach 2 Wochen unter Volllast
zusammenbricht oder nur noch im Scheckentempo läuft.
Als Vorgehensweise wird meist ein 4 Phasen-Modell, ähnlich den Modellen des Software
Engineering gewählt: Analyse, konzeptueller Entwurf, logischer Entwurf und physischer
Entwurf.
Dieses Modell wird gewählt, um möglichst früh Anforderungen an die Datenverarbeitung und
Informationsverarbeitung definieren zu können, wobei man sich so spät wie möglich auf ein
konkretes Datenbanksystem festlegen sollte und erst ganz am Ende Hardware und
Betriebssystem entsprechend der Gesamtanforderungen auswählen sollte.
Um in der Analysephase den Informationsbedarf ermitteln zu können, erstellt man sich
zunächst drei Verzeichnisse:
Ein Datenverzeichnis, in dem man alle zu speichernden Daten mit deren Namen und
Eigenschaften auflistet.
Dann erstellt man sich ein Operationsverzeichnis, in dem man die Verwendung der Daten
auflistet, jeweils mit Eingabe und Ausgabeinformationen der Operationen.
Schließlich erstellt man ein Ereignisverzeichnis in dem festgehalten wird, welches Ereignis
eine Operation aufruft, hieraus ergeben sich dann schließlich die Abläufe.
In der konzeptuellen Phase versucht man, Sachverhalte und Gesetzmäßigkeiten in eine
formale Gestalt zu überführen, meist tut man dies mit Hilfe eines semantischen Modells.
Dieses Modell soll den Sachverhalt vollständig und korrekt beschrieben, wobei diese
Beschreibung unabhängig von einem bestimmten Datenbanksystem sein soll.
Ist ein relationales Modell auch für semantische Zwecke geeignet, so kann es als
Ausgangspunkt sukzessiver Transformationen gesehen werden, hierfür müssen Redundanzen
und Anomalien (Änderungsanomalie, Löschanomalie und Einfügeanomalie) beseitigt werden,
wofür sich besonders die Normalisierung eignet - für neue und bereits bestehende Modelle.
Die Modellierung kann mit dem ER-Modell durchgeführt werden, doch werden hier nur
statische Aspekte erfasst, für dynamische Eigenschaften eignen sich objektorientierte
Darstellungen.
Oft muss in der konzeptuellen Phase identifiziert werden, welche Anwendergruppen es gibt,
welche Aufgaben diese erfüllen und resultierend daraus, welche Sicht auf die Datenbasis sie
benötigen.
Am besten verwendet man hierfür eine 2-Phasen-Methode, in der ersten Phase werden die
verschiedenen Anwendergruppen identifiziert und für jede Anwendergruppe wird ein
eigenständiges konzeptuelles Modell erstellt.
In der zweiten Phase schließlich, werden die Modelle zu einem ganzen integriert. Diese
Integration beschreibt nach einigen Modifikationen schließlich den logischen Entwurf, der
bereits auf die logischen Strukturen eines konkreten Datenbanksystems ausgelegt ist.
Im physischen Entwurf gilt zunächst einmal die Grundregel, dass die Kosten durch den
physischen Zugriff zu minimieren sind.
So müssen die Konstrukte optimal auf die Platten aufgeteilt werden, Partitionierungen,
Clusterungen und Indexe müssen geplant und implementiert werden.
Schließlich müssen nach der Installation eines Datenbanksystems die Installationsparameter
angepasst werden, so die Directory-Größe, temporärer Speicherplatz, Reservierungen im
RAM, Cache für Datenseiten, Cache für DB-Kommandos usw..
Schließlich müssen die bereits geplanten Sichten mit der Benutzerverwaltung und der
Rechtevergabe implementiert werden.
Gegebenenfalls sollten dann noch benötigte Integritätsregelen und DB-Prozeduren
verwirklicht werden.
Vor Inbetriebnahme im Produktivbetrieb sollte das System ausführlich getestet werden!
Objektorientierte Datenbanken (checked)
Objektorientierte Datenbanken wurden zunächst am Beispiel von Versant behandelt.
Versant bietet eine JAVA und eine C++-Schnittstelle, so dass man aus üblichen
objektorientierten Programmen direkt auf eine Versanddatenbank zugreifen kann.
Es werden auch komplexe Datenmodelle unterstützt. Multithreading und Multi-ClientConnects werden verwendet. Des weiteren stehen spezielle Vorrichtungen für die
ausgewogene Nutzung von Netzwerkressourcen und für das Wideranlaufen nach Fehlern zur
Verfügung.
Es ist möglich, einem Programm bei bestimmten Ereignissen in der Datenbank eine EventNotification zu schicken.
Versant vergibt implizit Object-IDs, wodurch jedes Objekt in der Datenbank automatisch eine
eindeutige Identität hat.
Für den Entwickler stehen verschiedene Tools und Konsolen für Abfragen und
Konfigurationen bereit.
Ein bestehendes oder auch neu erstelltes JAVA-Programm lässt sich relativ leicht um eine
persistente Speicherung mit Versant erweitern:
Ein Session-Objekt für die Verbindung mit der Datenbank wird zu Beginn des Programms
angelegt:
TransSession transaction = new TransSession(“Datenbank“);
Änderungen an den Objekten werden in die Datenbank übernommen:
transaction.commit();
Doch woher weiß Versant nun, welche Objekte persistent gespeichert werden sollen und
welche nicht?
Die Klassen bzw. deren Objekte, die gespeichert werden sollen, werden in einer
Konfigurationsdatei mit dem Namen config.jvi vermerkt, und zwar nach der Syntax:
p Klasse
(p steht für persistent)
Für die Benutzung muss noch eine entsprechende Klasse von Versant geladen werden.
Es existiert hier also kein direkter Code um in der Datenbank Objekte zu speichern und zu
ändern. Entsprechend objektorientierter Prinzipien unterstützt das DBMS Vererbung,
Referenzen und Assoziationen.
Auch eine Anfragesprache ist in Versant implementiert, diese ist SQL mit seiner SelectAnweisung sehr ähnlich, erhält jedoch ein paar Erweiterungen speziell für Objektmengen.
Herunterladen