Informationssysteme Kernfach, WS 2004/05

Werbung
Informationssysteme Kernfach
ETHZ D-INFK WS04/05
Prof. M. Norrie
Michel Estermann
18. Februar 2005
Vorwort
Diese Skript basiert auf der Vorlesung Information Systems Kernfach von
Prof. M. Norrie an der ETH Zürich im Wintersemester 2004/05. Der Inhalt wurde aber mit Hilfe diverser Online Artikel und Bücher (siehe Literaturverzeichnis
auf Seite 105) und im speziellen mit dem Buch “Fundamentals of DATABASE SYSTEMS” [22] ergänzt.
Es handelt sich bei diesem Skript nicht um ein offizielles Skript der Vorlesung.
Es ist zur Zeit weder Vollständig noch wird es frei von Fehlern sein. Für die
Richtigkeit des Inhaltes wird keine Garantie übernommen.
Michel Estermann, 18. Februar 2005
Inhaltsverzeichnis
Vorwort
I
i
XML Dokument Management
1
1 XML
3
1.1
Strukturierte, semi-strukturierte und unstrukturierte Daten . . .
3
1.2
XML Datenmodell . . . . . . . . . . . . . . . . . . . . . . . . . .
4
1.3
XML Dokumente, DTD, XML Schema . . . . . . . . . . . . . . .
6
1.3.1
wohl geformte und gültige XML Dokumente, XML DTD .
6
1.3.2
XML Schema . . . . . . . . . . . . . . . . . . . . . . . . .
6
2 Speichern von XML Dokumenten
2.1
XML und Datenbanken . . . . . . . . . . . . . . . . . . . . . . .
2.1.1
9
9
XML DBMS Anforderungen . . . . . . . . . . . . . . . . .
10
2.2
XML DBMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
2.3
Integral Storage Systems . . . . . . . . . . . . . . . . . . . . . . .
11
2.4
Dispersed Storage Systems . . . . . . . . . . . . . . . . . . . . . .
11
2.4.1
Custom Design . . . . . . . . . . . . . . . . . . . . . . . .
12
2.4.2
General Design . . . . . . . . . . . . . . . . . . . . . . . .
12
2.4.3
Element Reihenfolge . . . . . . . . . . . . . . . . . . . . .
13
2.4.4
ID/IDREF Mapping . . . . . . . . . . . . . . . . . . . . . .
16
2.4.5
Indexierung von XML . . . . . . . . . . . . . . . . . . . .
17
XML Support in RDBMS . . . . . . . . . . . . . . . . . . . . . .
17
2.5.1
SQL/XML . . . . . . . . . . . . . . . . . . . . . . . . . .
19
2.5.2
XML Support einiger kommerzieller Produkte . . . . . . .
20
Spezialisierte Systeme . . . . . . . . . . . . . . . . . . . . . . . .
21
2.5
2.6
iii
iv
INHALTSVERZEICHNIS
2.7
2.8
2.6.1
NeoCore XMS . . . . . . . . . . . . . . . . . . . . . . . .
21
2.6.2
Lore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
21
Datenspeichermodelle für XML . . . . . . . . . . . . . . . . . . .
25
2.7.1
OEM . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
25
2.7.2
Monet . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
27
Übersicht/Vergleich
. . . . . . . . . . . . . . . . . . . . . . . . .
3 Querying von XML Dokumenten
II
29
31
3.1
Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
3.2
XQuery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
3.2.1
Daten Modell . . . . . . . . . . . . . . . . . . . . . . . . .
32
3.2.2
XPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
3.2.3
Node Test . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
Database Application Programming
4 SQL in Programmierumgebungen
37
39
4.1
Datenbankapplikationen . . . . . . . . . . . . . . . . . . . . . . .
39
4.2
Impedance Mismatch . . . . . . . . . . . . . . . . . . . . . . . . .
40
4.3
Embedded SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . .
40
4.3.1
Shared Variables . . . . . . . . . . . . . . . . . . . . . . .
40
4.3.2
Retrieving Data . . . . . . . . . . . . . . . . . . . . . . .
41
4.3.3
Zusammenfassung . . . . . . . . . . . . . . . . . . . . . .
42
Call Level Interface . . . . . . . . . . . . . . . . . . . . . . . . . .
43
4.4.1
JDBC: SQL Funktionen für JAVA Programmierung . . .
43
Applikations Logik auf der Server Seite . . . . . . . . . . . . . . .
48
4.5.1
48
4.4
4.5
Stored Procedures . . . . . . . . . . . . . . . . . . . . . .
5 Datenbanken und Programmierung
53
5.1
Motivation
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
5.2
Persistence in Programmiersprachen . . . . . . . . . . . . . . . .
54
5.2.1
Design Methoden . . . . . . . . . . . . . . . . . . . . . . .
55
5.2.2
Persistent Languages . . . . . . . . . . . . . . . . . . . . .
55
5.2.3
Persistence in Java . . . . . . . . . . . . . . . . . . . . . .
58
5.2.4
ObjectStore OODBMS . . . . . . . . . . . . . . . . . . . .
60
INHALTSVERZEICHNIS
v
6 Objekt-Orientierte Datenbanken
6.1
6.2
III
63
ODMG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
6.1.1
ODMG Object Model . . . . . . . . . . . . . . . . . . . .
64
6.1.2
Object Definition Language (ODL) . . . . . . . . . . . . .
68
6.1.3
Object Query Language (OQL) . . . . . . . . . . . . . . .
68
Java Data Objects (JDO) . . . . . . . . . . . . . . . . . . . . . .
69
Architekturen und Technologien
71
7 Client/Server/Middleware Architekturen
73
7.1
Zentralisierte DBMS Architektur . . . . . . . . . . . . . . . . . .
73
7.2
Client/Server (File-Server) Architektur . . . . . . . . . . . . . . .
73
7.3
Two-Tier Client/Server Architektur . . . . . . . . . . . . . . . .
74
7.4
Three-Tier Client/Server Architektur . . . . . . . . . . . . . . . .
75
7.5
Transaction Processing Monitors . . . . . . . . . . . . . . . . . .
77
7.6
Peer-to Peer Systeme . . . . . . . . . . . . . . . . . . . . . . . . .
79
7.7
Verteilte Datenbanksysteme . . . . . . . . . . . . . . . . . . . . .
79
7.7.1
Zukünftige Trends . . . . . . . . . . . . . . . . . . . . . .
80
Distributed Query Processing . . . . . . . . . . . . . . . . . . . .
80
7.8.1
Query Processing . . . . . . . . . . . . . . . . . . . . . . .
81
7.8.2
Dynamische Programmierung für Query Optimierung . .
82
7.8.3
Kostenschätzung für Pläne . . . . . . . . . . . . . . . . .
83
7.8.4
Query Execution Techniken . . . . . . . . . . . . . . . . .
84
7.8.5
Ausnützen der Client Ressourcen . . . . . . . . . . . . . .
87
7.8.6
Query Optimierung . . . . . . . . . . . . . . . . . . . . .
88
7.8.7
Query Ausführung . . . . . . . . . . . . . . . . . . . . . .
89
7.8.8
Dynamische Datenplatzierung . . . . . . . . . . . . . . . .
90
7.8
8 Transaktionsmodelle und Systeme
8.1
8.2
Transaktionen und Concurrency
91
. . . . . . . . . . . . . . . . . .
91
8.1.1
Warum Concurrency Control? . . . . . . . . . . . . . . . .
91
8.1.2
Transaktions Eigenschaften . . . . . . . . . . . . . . . . .
95
8.1.3
Serialisierbarkeit . . . . . . . . . . . . . . . . . . . . . . .
95
Concurrency Control Techniken . . . . . . . . . . . . . . . . . . .
95
vi
INHALTSVERZEICHNIS
8.3
8.2.1
Locks . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
95
8.2.2
Two Phase Locking (2PL) . . . . . . . . . . . . . . . . . .
96
8.2.3
Timestamp Ordering . . . . . . . . . . . . . . . . . . . . .
96
8.2.4
Multiversion Concurrency Control Techniken . . . . . . .
97
Locking Implementation . . . . . . . . . . . . . . . . . . . . . . .
98
8.3.1
Lock Manager
. . . . . . . . . . . . . . . . . . . . . . . .
98
8.3.2
Lock Granularität . . . . . . . . . . . . . . . . . . . . . .
99
8.4
Multigranularity Locking
8.5
Deadlock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
8.5.1
8.6
Deadlock Detection
. . . . . . . . . . . . . . . . . . . . . .
99
. . . . . . . . . . . . . . . . . . . . . 102
Performance Issues und Bottlenecks . . . . . . . . . . . . . . . . 102
8.6.1
Lock Trashing . . . . . . . . . . . . . . . . . . . . . . . . 102
8.6.2
Hot Spots . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
8.6.3
Query Update Problem . . . . . . . . . . . . . . . . . . . 103
Teil I
XML Dokument
Management
1
Kapitel 1
XML
XML (Extensible Markup Language) ist ein standardisiertes Textformat für die
Repräsentation von (semi)-strukturierten ((semi)-structured ) Informationen. Eine Markup Sprache ist ein Mechanismus zur Beschreibung von Strukturen in einem Dokument. Die XML Spezifikation definiert einen Standard zum Einfügen
von Markups in ein Dokumentes. XML ist deshalb eine so genannte meta-markup
language, d.h. es gibt im Gegensatz z.B. zu HTML nicht ein vordefiniertes Set
von Tags, sondern die Tags können selber definiert werden und so Informationen zur Struktur und Bedeutung der Daten liefern. XML beschreibt also nicht
die Formatierung von Daten sondern die Struktur und Bedeutung dessen. Im
Zeitalter des Internet wird XML als Format für den Datenaustausch zwischen
verschiedenen Systemen über das Web immer wichtiger, ausserdem gibt es viele
Tools für die Arbeit mit XML und bereits sind viele Daten in XML veröffentlicht. Der XML Standard wird durch das World-Wide Web Konsortium (W3C)
verabschiedet. Die Spezifikation von XML und andere Informationen finden sich
unter www.w3.org/XML/.
1.1
Strukturierte, semi-strukturierte und unstrukturierte Daten
Informationen die in einer Datenbank gespeichert werden sind strukturierte
Daten (structured data) weil sie durch ein striktes Format repräsentiert werden.
Alle Einträge (records) in einer Relationalen Datenbank Tabelle haben die selben Attribute, evtl. mit einem NULL Eintrag. Die Attribut-Kolonne muss aber
vorhanden sein.
Es gibt aber viele Daten die einer gewissen Struktur folgen, aber nicht alle gesammelten Informationen haben die selbe identische Struktur. Viele Attribute
sind vielleicht bei allen Daten gleich, einige kommen aber nur in gewissen vor.
Zudem können zu einem späteren Zeitpunkt neue Attribute hinzukommen. Solche Daten nennt man semi-strukturierte Daten. Oft werden solche Daten als
Bäume oder Graphen dargestellt.
3
4
KAPITEL 1. XML
<?xml version="1.0" standalone="yes"?>
<newsfeed>
<news>
<title>Dell does it again</title>
<href>http://www.zdii.com/</href>
<abstract language="english">
2:19 PM PT Direct PC Maker continues an a roll,
earning $305 million in the quarter
</abstract>
<source>zdnet</source>
</news>
<news>
<title>
Judges sets date for Microdoft Justice department
</title>
<href>http://www.zdnet.com/</hfref>
<source>zdnet</source>
</news>
</newsfeed>
Abbildung 1.1: XML Beispiel
Ein wichtiger Unterschied zwischen strukturierten und semi-strukturierten Daten ist, wie das Schemakonstrukt (Attributnamen, Abhängigkeiten, Datentypen) gehandhabt wird. In semi-strukturierten Daten ist die Schemainformation
mit den Daten vermischt, weil jedes Datenobjekt verschiedene Attribute haben
kann, die im voraus vielleicht noch nicht bekannt sind. Dieser Typ von Daten nennt man selbst beschreibende Daten (self-describing data). Zusätzlich zu
den strukturierten und semi-strukturierten Daten gibt es die unstrukturierten Daten, mit nur wenigen oder keinen Informationen über die Art der Daten.
Typische Beispiele sind Textdokumente oder HTML Seiten, welche zwar viele
Informationen in Form von Text enthalten können, die aber kaum eine Struktur aufweisen. Einzig die Aufteilung in Kapitel, Unterkapitel usw. kann als eine
gewisse Struktur aufgefasst werden, daraus sind aber kaum Informationen zu
entnehmen.
1.2
XML Datenmodell
Das Basisobjekt im XML Dokument ist xml. Die Hauptkonzepte zur Strukturierung sind Elemente (elements) und Attribute (attributes). Der Begriff
Attribute in XML wird nicht gleich benutzt wie in der Datenbank Terminologie.
Attribute in XML sind zusätzliche Informationen für Elemente.
Abbildung 1.1 zeigt ein Beispiel eines XML Dokuments mit einem Komplexen
Element (complex element) <newsfead> welches wiederum das complex element
<news> enthält. Das Element <abstract> hat ein Attribute language.
1.2. XML DATENMODELL
5
newsfeed
news
title
"Dell..."
href
"http://..."
news
abstract
language source
"2:19..."
"english"
"zdnet"
title
"Judge..."
href
"http://..."
source
"zdnet"
Abbildung 1.2: “tree data model” des XML Beispiels
Man kann dieses Repräsentation der Daten als XML Dokument leicht in eine Repräsentation als Baummodell (tree model ) umwandeln (Abbildung 1.2).
Das XML Datenmodell nennt man deshalb auch Baummodell (tree model )
oder Hierarchisches Modell (hierarchical model ). Mathematisch kann man
das XML Datenmodell folgendermassen definieren [43]:
Definition 1. Ein XML Dokument ist ein gewurzelter Baum d=(V, E, r,
labelE , labelA , rank) mit Knoten V und Kanten E ⊆ V × V und einem “ausgezeichneten” Knoten r ∈ V , dem root node. Die Funktion labelE : V → string
ordnet Labels an Knoten, d.h. Elementen zu; labelA : V → string → string
ordnet Paare von Strings, Attribute und ihre Werte, Knoten zu. Character Data (CDATA) sind als spezielle ‘string’ Attribute von cdata Knoten modelliert,
rank : V → int etablierte eine Rangierung, um eine Ordnung unter Knoten
mit dem selben Parent Knoten zu ermöglichen. Für Elemente ohne irgendein
Attribute ist labelA gleich der leeren Menge.
Im Allgemeinen ist es möglich drei Haupttypen von XML zu charakterisieren:
• Daten-zentrierte XML Dokumente(data-centric): Diese Dokumente haben viele kleine Datenelemente welche einer spezifischen Struktur folgen,
also die z.B. einer strukturierten Datenbank entnommen wurden. Sie sind
als XML Dokument umgewandelt worden, um sie über das Web zu verbreiten oder darzustellen.
• Dokument-zentrierte XML Dokumente (document-centric): Diese sind
Dokumente mit viel Text, z.B. News Artikel oder Bücher. In diesen Dokumenten gibt es nur wenige oder keine strukturierten Datenelemente.
• Hybride XML Dokumente (hybrid): Diese Dokumente enthalten Teile mit
strukturierten Daten und andere Teile die vorwiegend unstrukturiert sind.
6
KAPITEL 1. XML
<!-- Example DTD of News Information -->
<!ELEMENT newsfeed (news)*>
<!ELEMENT news (title, href, abstract?, source?>
<!ELMENT title (#PCDATA)>
<!ELEMENT href (#PCDATA)>
<!ELEMENT abstract (#PCDATA)>
<!ATTLIST abstract language CDATA #IMPLIED>
<!ELEMENT source (#PCDATA)>
Abbildung 1.3: DTD zum Beispiel Abbildung 1.1
1.3
1.3.1
XML Dokumente, DTD, XML Schema
wohl geformte und gültige XML Dokumente, XML
DTD
Ein XML Dokument wie das Beispiel aus Abbildung 1.1 ist wohl geformt (wellformed ), wenn es mit der XML Deklaration (XML Version und andere relevanten Attribute) beginnt und syntaktisch den Richtlinien des Baummodelles folgt.
D.h. es hat ein einzelnes root element und jedes Element ist durch Start- und
End-Tags innerhalb der Start- und End-Tags seines parent element definiert.
Ein wohl geformtes XML Dokument ist syntaktisch korrekt und kann von einem generischen Prozessor traversiert und als Baum repräsentiert werden. Ein
Standard Set von API (application programming interface) Funktionen (DOM
genannt) erlaubt Programmen diesen Baum zu manipulieren. Für die Benutzung
von DOM muss das XML Dokument also zuerst geparst werden. Ein anders API,
SAX, bietet eine andere Möglichkeit, es erlaubt das Verarbeiten eines XML Dokuments on the fly indem es dem Programm meldet wenn ein Start-Tag oder
End-Tag gelesen wird.
Ein wohl geformtes Dokument kann beliebige Tag-Namen für die Elemente haben. Dies ermöglicht dem Verfasser eine grösstmögliche Freiheit, reduziert aber
die Möglichkeit zur automatischen Interpretation der Elemente. Ein stärkeres
Kriterium ist, wenn ein XML Dokumente gültig (valid ) ist. In diesem Fall muss
das Dokument wohl geformt sein und einer bestimmten Struktur folgen, die in
einem XML DTD (Document Type Definition) oder XML Schema File definiert ist.
Abbildung 1.3 zeigt die DTD zum Beispiel aus Abbildung 1.1. Um mehr Über
DTD zu lernen gibt es ein Online Tutorial[52].
1.3.2
XML Schema
Die XML Schema Sprache ist ein Standard zur Spezifizierung der Struktur
eines XML Dokuments. Im Gegensatz zu DTD benutzt sie aber den selben
Syntax wie ein normales XML Dokument, ist also selbst ein XML Dokument
und kann von den selben Prozessoren verarbeitet werden. Abbildung 1.4 zeigt
das XML Schema zum Beispiel aus Abbildung 1.1. Mehr zu XML Schema findet
man auf der Seite des w3c (www.w3.org/XML/Schema).
1.3. XML DOKUMENTE, DTD, XML SCHEMA
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="newsfeed">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="news" type="News" minOccurs="0"
maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="News">
<xsd:sequence>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="href" type="xsd:string"/>
<xsd:element name="abstract" minOccurs="0"
maxOccurs="1">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xs:attribute name="language"
type="xsd:string" use="optional"/>
</xsd:extension>
</xsd:simpleContent>
</xsd/complexType>
</xsd:element>
<xsd:element name="source" type="xsd:string"
minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Abbildung 1.4: XML Schema zum Beispiel Abbildung 1.1
7
8
KAPITEL 1. XML
Kapitel 2
Speichern von XML
Dokumenten
2.1
XML und Datenbanken
Die Benutzung von XML Dokumente zur Verwaltung von Daten hat verschiedene Nachteile:
• Hierarchische Ansicht der Daten
• Nicht effizient im Sinne von Speicherplatz und Verarbeitung
• beschränkte Kontrolle der Vollständigkeit der Daten
Trotzdem ist es sinnvoll Informationen als XML Dokumente zu speichern, wenn
die Informationsquellen XML Dokumente sind. Als XML für den Datenaustausch immer populärer wurde, kam deshalb auch schnell das Bedürfnis auf, die
Inhalte der erhaltenen XML Dokumente sinnvoll in DBMS abzulegen. Zudem
bietet sich das Speichern an, da die XML Notation Hersteller- und Platformunabhängig ist und komplexe Daten mit XML flexibler als mit Relationalen
Datenbanken repräsentiert werden k”önnen. Und wenn man Daten schon als
XML erhält oder ver”öffentlicht, warum soll man sie dann nicht auch als XML
speichern und verwalten.
Für verschiedene Anwendungen macht das Speichern und Verwalten von Informationen als XML Dokument Sinn:
• Dokument Management Applikationen: speichern und Verwalten von Geschäfts Dokumenten, Publikationen usw.
• Archiv Applikationen: Langzeit-Speicherung von Informationen.
• Applikationen mit komplexen Daten: z.B. Geografische, Biochemische, Medizinische Applikationen. . .
• Publizier Applikationen: Content Management System
9
10
KAPITEL 2. SPEICHERN VON XML DOKUMENTEN
2.1.1
XML DBMS Anforderungen
• XML Datenmodell
– Das XML Datenmodell ist komplexer und weniger streng als das relationale Modell, zudem k”önnen die einzelnen Elemente Metadaten
enthalten.
– Eine neue Query-Sprache und andere Mechanismen zur Ausführung
von Suchanfragen (Indexing, Optimierung) sind gefordert.
– Das XML Datenmodell bezieht sich auf individuelle Dokumente und
nicht auf Dokumentensammlungen. Externe Abhängigkeiten müssen
gehandhabt werden k”önnen.
• Update Policen
– Verschiedene Update Policen für XML Dokumente, wie read-only,
append-only und revision check-out, müssen unterstützt werden.
• Schema Evolution
– Dokumentenschemas entwickeln sich typischerweise mit der Zeit und
oft m”öchte man mit verschiedene Schemaversionen und deren Dokumente arbeiten k”önnen.
– Man braucht Suchanfragen, die über mehrere Versionen funktionieren.
2.2
XML DBMS
XML Datenbanken k”önnen in zwei Gruppen unterteilt werden:
1. Native XML DBMS : DBMS die spezielle für die Speicherung von XML
Dokumenten entwickelt wurden: z.B. NeoCore XMS, Excelon, InfonyteDB, Tamino
2. XML-enabled RDBMS : herk”ömmliche DBMS werden von der Herstellern erweitert um XML zu unterstützen: z.B. IBM DB2, Oracle9i, MS
SQL Server 2000
Eine andere Unterteilung der DBMS kann durch die Art der Speicherung von
XML Dokumenten vorgenommen werden:
1. Integral Storage Systems: Ein relationales oder objektorientiertes DBMS
(database managment system) kann benutzt werden um das XML Dokument als Ganzes als BLOB/CLOB (binary large object/character large object) oder Referenz zu einem File innerhalb eines DBMS Record/Objekt zu
speichern. Dieses Methode kann benutzt werden, wenn das DBMS spezielle
Module zur Verarbeitung von Textdokumenten besitzt oder für dokumentzentrierte (siehe 1.2 auf Seite 5) und schemalose XML Dokumente.
2.3. INTEGRAL STORAGE SYSTEMS
11
2. Dispersed Storage Systems: Ein DBMS wird benutzt um den Dokumentinhalt als Datenelemente zu speichern. Ein XML Dokument wird geparst und der Inhalt in seperaten Records/Objects abgelegt. Diese Methode funktioniert zur Speicherung von verschieden Dokumenten die einem
spezifischen Schema (XML DTD, XML Schema) folgen. Weil alle Dokumente die selbe Struktur aufweisen, kann eine relationale (oder objektorientierte) Datenbank zur Speicherung der Blattelemente (leaf-level data
elements) innerhalb des XML Dokuments kreiert werden. Diese Methode
erfordert Mapping Algorithmen um ein Datenbank Schema zu definieren,
welches mit der Struktur eines XML Dokuments, wie sie im XML DTD
oder XML Schema spezifiziert ist, kompatibel ist und die Wiederherstellung des XML Dokuments aus den gespeicherten Daten erlaubt.
3. Spezialisierte Systeme: Eine neuer Typ von Datenbank System auf der
Basis des Hierarchischen Modells (hierachical (tree) model ) wird entwickelt
und implementiert. Das System enthält spezialisierte Indexing und Querying Techniken und funktioniert für alle Arten von XML Dokumenten.
2.3
Integral Storage Systems
Wenn die XML Dokumente wie normale Textdokumente, z.B als BLOB oder
CLOB, in einer Datenbank gespeichert sind, werden typischerweise zusätzlich
noch Textindizes oder Metadaten in der Datenbank abgelegt. Dazu sind besondere Tools wie Volltextsuche n”ötig.
Für das Querying solcher Dokumente kann XQuery (siehe 3.2 auf Seite 31) verwendet werden.
Kann auf Dokumente direkt zugegriffen werden, dann taucht das Problem der
Integrität der Indexierung und der Konsistenz der Dokumentdateien auf. Dies
kann verhindert werden, indem geschätzte Bereiche zu Sicherung verwendet und
Zugriffsrechte verwaltet werden (z.B. nur noch Leserechte für weitere Zugriffe
nach einer Anforderung eines Dokuments).
Für die Speicherung und Verwaltung von Dokumenten allgemein und XML Dokumenten im speziellen gibt es verschiedene Document-/Content Management
Systeme auf dem Markt (z.B. Content@XML). Oft wird in solchen Systemen
eine Kombination mit einer daten-zentrierte L”ösung implementiert (siehe unten), d.h die Dokumente k”önnen in Elemente aufgeteilt werden, und diese
Elemente werden abgespeichert. Ebenfalls unterstützen einige moderne DBMS
wie Oracle9i das direkte Speichern von XML Elementen (siehe Abbildung 2.4).
2.4
Dispersed Storage Systems
Wir wollen nun XML Dokumente daten-zentrierte (siehe 1.2 auf Seite 5) in einer relationalen Datenbank abspeichern. D.h. Daten werden als XML importiert
und/oder als XML exportiert, evtl. wollen wir strukturierte Daten und Textdaten mischen.
Wir müssen dazu die XML Daten Parsen, Speichern und XML Daten aus Datenbank Objekten generieren k”önnen. Zudem brauchen wir Mechanismen für
12
KAPITEL 2. SPEICHERN VON XML DOKUMENTEN
die Indexierung und das Suchen.
Um XML Daten in einem relationalen DBMS abspeichern zu k”önnen muss das
XML Dokument auf Relationen gemapt werden. Dabei müssen die Relationen
die XML Dokument Typen Definitionen (DTDs oder XML Schemas) und das
XML Dokument in Bezug seiner hierarchischen Struktur, der Reihenfolge der
Elemente sowie dem Inhalt repräsentiert werden k”önnen.
Es gibt dabei unverträglichen Unterschiede der beiden Datenmodelle zu überwinden (siehe Tabelle 2.1).
XML
Relational
- Daten in einzelner hierarchischer
Struktur
- Nodes haben Element- und/oder
Attributwerte
- Elemente k”önnen andere Elemente enthalten
- Elemente sind geordnet
↔
- Daten in mehreren Tabellen
↔
- Zellen haben einzelne atomic values
↔
- Elemente k”önnen rekursiv sein
↔
- Query mit XML Standard
↔
- Zeilen und Kolonnen sind ungeordnet
- wenig Unterstützung für rekursive
Elemente
- SQL basiertes Querying
Tabelle 2.1: Datenmodel Mismatch
2.4.1
Custom Design
Ist die Struktur eines XML Dokuments bekannt, weil die DTD oder das XML
Schema vorhanden ist, kann eine spezielle für diese Struktur angepasste L”ösung
konstruiert werden, indem man die DTD oder das XML Schema Dokument parst
und für jedes Elemente eine Tabelle kreiert. Die Tabelle hat dabei Kolonnen mit
den Attributen und dem Wert des Elements und eine Referenz auf das parentElement (beim root-Element eine Referenz auf das Dokument). Bei vielen Dokumenten mit unterschiedlichen Strukturen entstehen dabei aber viele Tabellen
mit wenigen Einträgen, was zu Ineffizienz beim Suchen und Speicherbedarf führen kann. Ausserdem ist die Position des Elementes innerhalb des Dokuments
nicht ersichtlich, da aber die Reihenfolge der Elemente typischerweise eine Rolle
spielt muss man um dieses Problem zu beheben eine Positionsfeld einfügen.
2.4.2
General Design
Eine bessere M”öglichkeit ist die DTD (oder das XML Schema) und die korrespondierenden Elemente in je einer Tabelle zu repräsentieren (siehe die Beispiele
aus den Abbildungen 2.1, 2.2). Verschiedene DTDs und deren Elemente werden
2.4. DISPERSED STORAGE SYSTEMS
13
durch eindeutige IDs bestimmt. Die Elemente haben eine eindeutige ID, welche
auch ihre Position im Dokument bestimmt und referenzieren zum entsprechenden DTD Element (ID aus der DTD Tabelle).
2.4.3
Element Reihenfolge
Wir haben oben gesehen, dass man zur Bestimmung der Reihenfolge der Elements im Dokument eine Kolonne mit den Positionen einführen kann, oder einen
Identifier der die Position bestimmt benutzt. Es gibt aber noch andere Methoden. Es folgte eine Zusammenfassung von M”öglichkeiten:
• Identifier
Wir definieren die Position im Dokument mit Identifiers (IDs), wie im
Beispiel oben (Abbildung 2.1, 2.2).
Wird ein Node gel”öscht, gibts keine Probleme. Wird ein neues Element
eingefügt, k”önnen die IDs des Vorgängers und des Nachfolgers bestimmt
werden. Die neue ID wird als die Mitte zwischen den beiden neu berechnet.
Wenn also z.B. in unserem Beispiel eine neue Telephonnummer phone
zwischen die beiden bestehenden eingefügt werden soll, wird diese ID als
N.4.1,5 (zwischen N.4.1 und N.4.2 ) bestimmt.
• Relative Position
Eine spezielle Tabelle parentchild mit den Feldern parent_node, child_node
und position und mit einem PRIMARY KEY aus allen Drei wird benutzt.
Das Feld position gibt dabei die Position des child_node innerhalb des
parent_node an.
Wenn ein Element gel”öscht wird, ist nichts zu machen. Wird ein Element eingefügt, müssen nur die Positionen der child_nodes mit dem selben parent_node auf den neuen Stand gebracht werden.
• Absolute Position
Ein Feld position bestimmt eindeutig die Position im Dokument. Man
braucht keine spezielle Tabelle, sondern das Feld kann in der Node-Tabelle
(node) eingefügt werden. Die Eindeutigkeit kann mit einem UNIQUE constraint ON node (position) gewährleistet werden.
L”öschen eines Element ist wieder kein Problem. Beim Einfügen eines
neuen Elements müssen aber alle Position angepasst werden, was bei vielen
Daten mühsam und Zeitaufwendig sein kann.
• Links
Wir benutzen in der Node-Tabelle die Felder next_sibling und first_child
und zusätzlich vielleicht noch ein previous_sibling, wobei damit Referenzen auf das nächste (vorherige) Kind-Element und eine Referenz auf
das erste Kind-Element im aktuellen Element gemeint sind.
Da es sich hierbei um eine linked list handelt, k”önnen bekannte Algorithmen zum L”öschen und Einfügen für diese Datenstruktur benutzt
werden. Bei der doppelt verlinkten Liste (mit next und previous) sind
diese Operationen bekanntlich einfacher zu handhaben.
14
KAPITEL 2. SPEICHERN VON XML DOKUMENTEN
Beispiel DTD
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
<!ELEMENT
business_card
name
surname
given
other
title
address
street
city
postcode
contact
phone
(name, title, address, contact)>
(surname, given, other?)>
#PCDATA>
#PCDATA>
#PCDATA>
#PCDATA>
(street, city, postcode)>
#PCDATA>
#PCDATA>
#PCDATA>
(phone*)>
#PCDATA>
Dokument Hierarchie
business_card
N.1
N.2
name
title
N.1.1
N.1.3
surname
other
N
N.3
N.4
address
contact
N.3.1
N.3.3
street
postcode
N.4.1
phone
N.4.k
phone
N.3.2
N.1.2
city
given
DTD Representation
NodeID
N
N.1
N.2
N.3
N.4
N.1.1
N.1.2
N.1.3
N.3.1
N.3.2
N.3.3
N.4.1
ElementType
business card
name
title
address
contact
surname
given
other
street
city
postcode
phone
Datatype
RootNode
Structural Element
#PCDATA
Structural Element
Structural Element
#PCDATA
#PCDATA
#PCDATA
#PCDATA
#PCDATA
#PCDATA
#PCDATA
Cardinality
1
1
1
1
1
1
1
0..1
1
1
1
1..k
wobei N eine Integer ID für die DTD ist
Abbildung 2.1: Beispiel einer relationalen Representation eines XML Dokuments
2.4. DISPERSED STORAGE SYSTEMS
15
Beispiel Instanz
<business_card>
<name>
<surname>Smith</surname>
<given>John</given>
<other>K</other>
</name>
<title>Professor</title>
<address>
<street>53 Hight Street</street>
<city>London</city>
<postcode>SW11 3XY</postcode>
</address>
<contact>
<phone>0181 546 3421</phone>
<phone>079 452 345</phone>
</contact>
</business_card>
Element Representation
Doc Node
M.K.1.1
M.K.1.2
M.K.1.3
M.K.2
M.K.3.1
M.K.3.2
M.K.3.3
M.K.4.1
M.K.4.2
M.(K+1).1.1
...
DTD Node
N.1.1
N.1.2
N.1.3
N.2
N.3.1
N.3.2
N.3.3
N.4.1
N.4.2
N.1.1
...
Value
Smith
John
K
Professor
53 High Street
London
SW11 3XY
0181 546 3421
079 452 345
Jones
...
wobei N wie vorher eine Integer ID für die DTD ist
Für M nehmen wir eine ID für das Dokument und K für das business card Element
innerhalb des Dokuments
Abbildung 2.2: Fortsetzung Beispiel
16
KAPITEL 2. SPEICHERN VON XML DOKUMENTEN
node
uid
node_name
node_data
document
uid root
parent_node
attribute
uid
attr_name
attr_value
attr_type
parent_node
ID_IDREF
id
idref
doc_id
Abbildung 2.3: Modellieren des ID/IDREF Konzepts
2.4.4
ID/IDREF Mapping
In DTDs k”önnen Referenzen mit ID und IDREF definiert werden. Wie kann
dieses Konzept mit einem relationale Modell modelliert werden?
Eine einfache L”ösung ist, die ID/IDREFs als normale Attribute zu mappen.
Das hat aber den Nachteil, dass Eigenschaften wie uniqueness der ID nicht automatisch vom relationalen Modell erzwungen werden. Eine besser M”öglichkeit
besteht darin, die ID/IDREFs als primary/foreign keys zu mappen. Zu Beachten
ist dabei, dass Probleme auftreten k”önnen, wenn man mehrere Dokumente
abspeichern m”öchte, da eine ID nur innerhalb eines Dokumentes eindeutig
sein muss. Zusätzlich k”önnen mit dieser Methode keine IDREFS (beachte die
Mehrzahl) modelliert werden.
Ein M”öglichkeit das Konzept der ID/IDREF(S) zu modellieren besteht darin eine Tabelle ID_IDREF (siehe Abbildung 2.3) zu verwenden. Die id Kolonne
enthält Referenzen auf Attribute des Typen ID und idref enthält Referenzen
auf Attribute mit dem Typ IDREF. Eine dritte Kolonne doc_id wird verwendet
um mehrere Dokumente handhaben zu k”önnen.
Für das Einfügen eines neuen Tupels in die ID_IDREF-Tabelle und um die Eindeutigkeit der Attribute vom Typ ID eines Dokument zu gewährleisten, müssen
noch Trigger implementiert werden. Wenn ein neues Tupel in die ID_IDREFTabellen eingefügt wird, muss man sich versichern, dass id auf ein Attribut mit
dem Typ ID und IDREF auf eines mit Typ IDREF verweist.
Als Beispiel haben wir folgende DTD:
<!ELEMENT drawing EMPTY>
<!ATTLIST drawing
drawingID ID #REQUIRED
2.5. XML SUPPORT IN RDBMS
17
desc CDATA #IMPLIED>
<!ELEMENT shape (line|text)*>
<!ATTLIST shape
drawingIDREF IDREF #REQUIRED>
...
Mit dem Beispiel XML Dokument:
<drawing drawingID="drawing1"/>
<shape drawingIDREF="drawing1">
...
</shape>
Würde die Tabelle attribute dann folgendes Tupel enthalten:
0
1
drawingID
drawingIDREF
”drawing1”
”drawing1”
ID
IDREF
5
6
Und die Tabelle ID_IDREF hätte dann das Tupel:
0
1
2.4.5
0
Indexierung von XML
• Value Index
– Index über atomic values: Elemente Inhalt oder Attribut-Values
– Übliche DBMS Index Methoden wie B-Tree oder Hash Index
• Full Text Index
– Index über individuelle W”örter aus Tags oder Inhalt
– Methoden aus dem Information Retrieval (Kapitel ??) werden benutzt, z.B. inverted index
• Path Index
– Index über die Dokument Struktur, d.h node values
2.5
XML Support in RDBMS
Wie bereits unter 2.3 erwähnt, unterstützen moderne DBMS XML in verschiedener Hinsicht. Oracle9i unterstützt beispielsweise ein SQL Typ XMLType um
XML Elementen direkt abzuspeichern (Abbildung 2.4).
Das Oracle XML SQL Utility erlaubt das “zerreissen” (shredding von XML Dokumente, für das daten-zentrierte Abspeichern (dispersed storage: 2.4). Element
Typen-Namen werden dabei auf Kolonnen-Namen in Tabellen gemappt. Elemente die nur Daten als Inhalt enthalten, werden auf skalare Kolonnen und
Elemente mit Sub-Elemente auf Objekttypen gemappt. Eine Liste von Elemente werden auf Kollektionen (collections) gemappt. (Beispiel siehe Abbildung 2.5)
18
KAPITEL 2. SPEICHERN VON XML DOKUMENTEN
CREATE TABLE Claim(
ClaimID
NUMBER(3),
Submitted
DATE,
DamageReport
SYS.XMLType,
Policy
SYS.XMLType,
Awards
SYS.XMLType
);
INSERT INTO Claim VALUES(
345123,
sysdate(),
SYS.XMLType.createXML(’
<DamageReport>
Bathroom ceiling collapsed
due to bath overflow in
flat above.
</DamageReport>
’)
SYS.XMLType.createXML(’
........
’)
.......
);
Abbildung 2.4: Speichern von XML Elementen in Oracle9i
<ROWSET>
<ROW num="1">
<ClaimID>143453</ClaimID>
<Submitted>2002-10-05</Submitted>
<CustomerID>6524</CustomerID>
<Amount>350</Amount>
</ROW>
</ROWSET>
⇓
Oracle XML SQL Utility
⇓
INSERT INTO Claim
(ClaimID, Submitted, CustomerID, Amount)
VALUES(’143453’,’2002-10-05’,’6524’,’350’)
Abbildung 2.5: Beispiel: Oracle XML SQL Utility
2.5. XML SUPPORT IN RDBMS
19
Oracle’s XSQL
Oracle hat eine einfaches Vokabular um SQL Statements in XML Dokumenten
zu repräsentieren.
Beispiel 2.1:
<?xml-stylesheet type="text/xsl" href="claim.xsl"?>
<query connection="XMLdemo">
SELECT VALUE(c) as CLAIM
FROM insurance_claim_view c
WHERE c.ClaimPolicy,PrimaryInsured.LastName=‘Smith’
</query>
⇒ Das XML Dokument wird geparst und die Query wird durch das DBMS bearbeitet. Als Resultat wird das Claim Dokument (XML) für Smith zurükgegeben.
Oracle’s XML Generator Funktionen
Oracle9i bietet drei eingebaute Funktionen um XML zu generieren:
• dbms_xmlgen
– Generiert XML Dokumente aus den Resultaten von Queries
• sys_xmlgen
– Generiert XML Dokumente aus Values, Objekttypen und XMLType
Instanzen
• sys_xmlagg
– Generiert XML Dokumente durch Zusammenfügen mehrere XML
Dokumente
2.5.1
SQL/XML
SQL/XML ist ein ANSI und ISO Standard welcher Unterstützung für die Benutzung von XML im Zusammenhang mit einem SQL Datenbank Systems anbietet.1
SQL/XML erm”öglicht XML Dokumente in einer SQL Datenbank zu speichern, diese Datenbank zu durchsuchen (durch Benutzung von XPath und XQuery) und existierende SQL Daten in Form von XML Dokumenten zu ver”öffentlichen.
Mehrere Firmen haben sich zu einer offen Gruppe (SQLX Group) zusammengeschlossen um Vorschlage für diesen neuen Standard zu entwickeln. (siehe
[20][21])
1 Die 1. Ausgabe des SQL/XML Standards wurde 2003 von der International Organization for Standardization (ISO) als Teil 14 des SQL Standards ISO/IEC 9075-14:2003
ver”öffentlicht
20
2.5.2
KAPITEL 2. SPEICHERN VON XML DOKUMENTEN
XML Support einiger kommerzieller Produkte
Es folgte eine tabellarische Zusammenfassung des XML Supports einiger beliebter kommerzieller Systeme.
Oracle9i
Produkte Info Oracle9i: www.oracle.com/technology/products/oracle9i/index.html
XML Daten Typ
Ja
Speicherung
Text oder Strukturiert
Value Index
Ja
Fulltext Index
Ja
Path Index
Ja
Querying
SQL Methoden mit XPath 1.0
Stärken
Gute Unterstützung von Standards(XPath, SQL/XML);
XML Views;
Import/Export von XML Schemas;
XML Processing (DOM,SAX);
XSLT Processing;
Schwächen
kein einheitliches Vorgehen
IBM DB2
Produkte Info IBM DB2: www-306.ibm.com/software/data/db2/
XML Daten Typ
Ja
Speicherung
Text oder Benutzerdefinierte Struktur
Value Index
Ja
Fulltext Index
Mit TextExtender ja
Path Index
Mit TextExtender ja
Querying
SQL Methoden mit XPath Dialekt
Stärken
Schwächen
schlechte Unterstützung der Standards;
Schlechte Integration von Tools mit SQL;
Nur teilweiser Support für SQL/XML[24];
Microsoft SQL Server 2000
Produkte Info MS SQL Server: www.microsoft.com/sql/default.mspx
XML Daten Typ
–
Speicherung
Textbasiert;
Modellbasiert mit OPENXML;
Benutzerdefinierte Struktur mit OPENXML STORED Queries
Value Index
Ja
Fulltext Index
Keine XML Spezifische Funktionen
Path Index
–
Querying
SQL Extension und XPath Dialekt
Stärken
Schwächen
Keine XML Datentypen;
kein Support für Updates durch XML Prozessoren;
Kein Support für SQL/XML;
2.6. SPEZIALISIERTE SYSTEME
2.6
2.6.1
21
Spezialisierte Systeme
NeoCore XMS
Wie im White Paper von NeoCore [39] bzw. Xpriori [53] beschrieben, ist NeoCore XML Management System (XMS) ein XML information management system, im Gegensatz zu einem
database management system (DBMS), weil es sowohl Metadaten als auch Daten (also Struktur und Inhalt) verwaltet. NeoCore XMS ist fähig die Struktur der Daten aus einem XML
Dokument herzuleiten, wenn sie gesendet oder modifiziert werden. Das Produkt ist komplett
Schemaunabhängig und Entwickler sind somit vom Datenbankdesign, der Tabellenerstellung
und der Indexdefinition befreit.
NeoCore XMS nimmt eine daten-zentrierte Sicht des XML Dokuments an.
In NeoCore XMS werden Index Einträge in so genannte Icons konvertiert (eigenschafterhaltende Symbole fester Länge, welche den Text des Index Eintrag repräsentieren), wodurch es
m”öglich ist viele Index Einträge zu speichern, da Icons weniger Platz brauchen und schneller zu verarbeiten sind. Dadurch wird es in NeoCore XMS m”öglich alles zu indexieren.
Eine XML Repräsentation eines Elements wie z.B. folgendes:
<Name>
<Last>Brandin</Last>
<First>Chris</First>
</Name>
Erzeugt folgende Index Einträge:
• <Name>
• <Name><Last>
• <Name><First>
• <Name><Last>Brandin
• <Name><First>Chris
• Brandin
• Chris
NeoCore XMS verwendet XPath Expressions für Queries.
2.6.2
Lore
Lore (Lightweight Object Repository2 [37] ist ein speziell für das verwalten von semi-strukturierten
Daten entwickeltes DBMS. Lore’s Daten Modell ist das OEM (siehe 2.7.1). Lore’s Query Language Lorel (siehe 2.6.2) ist eine Erweiterung von OQL (siehe 6.1.3).
Lorel Query Sprache
Lorel ist eine Erweiterung von OQL, die volle Spezifikation findet man in [1].
Der Grundbaustein von Lorel ist die simple path expression, welche aus einem Namen gefolgt
von einer Sequenz von Labels gebildet wird. Eine simple path expression ist z.B. auctions.item.name.
Die Semantik beinhaltet die Menge der Objekte die erreicht werden k”önnen, wenn man von
auctions startet und einer Kante mit Label item und dann einer Kante mit Label name folgt.
Pfadausdr cke k”önnen direkt in einem SQL Stil benutzt werden. Die folgende Query würde
für unser Beispiel OEM Modell aus Abbildung 2.9, das Objekt &2 als Resultat zurückgeben:
Beispiel 2.2:
2 Das Lore System ist auf zwei Arten leichtgewichtig: das von Lore unterstütze Objekt
Modell ist leichtgewichtig und das System selber ist leichtgewichtig, weil es keine multiuser
updates oder “schwergewichtige” DBMS Features bietet
22
KAPITEL 2. SPEICHERN VON XML DOKUMENTEN
QUERY
select auctions.open_auctions.auction.object
where auctions.openauctions.auction.bids.bid > 500000
RESULT
object
name "ring"
description "large diamond ring"
comment "very good condition"
Ein Prinzip von Lorel ist es, dass man sich, um eine Query zu schreiben, weder um Unregelmässigkeiten in der Datenbank sorgen machen oder die genau Struktur der Objekte kennen sollte,
noch sollte man sich um präzise Typen kümmern müssen. Die Query will keinen Run-time
Error ausl”ösen, wenn bid einen string Wert hat oder komplex ist, oder wenn bid oder
object single-valued, set-valued oder sogar für gewisse Gruppenmitglieder nicht vorhanden
ist. Die Query aus dem Beispiel wird egal wie die aktuelle Struktur der Datenbank aussieht
erfolgreich ausgeführt und eine angemessene Antwort liefern.
Der Lore Query Prozessor schreibt Queries in eine etwas kompliziertere Query im Stil von
OQL um. Die Query aus unserem obigen Beispiel wird folgendermassen umgeschrieben:
QUERY
select O
from auctions.open_auctions.auction.bids B, auctions.open_auctions.auction.object O
where exists P in B.bid : P > 500000
Abbildung 2.6 zeigt den Lore Query Plan für diese Query.
Iterators und Objekt Assignments
Für das Query processing wird ein rekursiver Iterator verwendet. Mit Iteratoren beginnt
die Ausführung oben am Queryplan, wobei jeder Knoten im Plan pro mal ein Tupel von
seinen Kindern anfragt und auf den Tupel Operationen durchführt. Wenn ein Knoten seine
Operationen beendet hat, gibt er das resultierenden Tupel nach oben an sein Parent weiter.
die Slots für die Beispiel Query zeigt Abbildung 2.7.
Scan Operator
Der Scan Operator ist in seiner Funktionalität ähnlich dem relationalen Scan. Es werden aber
nicht die Menge der Tuples in einer Relation gescannt, sondern dieser Scan gibt alle OIDs
(siehe 2.7.1) der Subobjekte von einem gegebenen Objekt, einem bestimmten Pfad folgend
zurück. Der Scan Operator ist folgendermassen definiert:
Scan ( StartingOASlot, Path_expression, TargetOASlot)
Scan startet von der OID die im StartingOASlot gespeichert ist, und platziert bei jeder Iteration die OID des nächsten Subobjekts, das die Path_expression erfüllt in den TargetOASlot
und das solange bis keine passenden Subobjekte mehr vorhanden sind.
Query Ausführung
In Lore gibt es verschiedene Strategien eine Query auszuführen:
• Top Down: Scan basierte Query Plane führen einen top-down Traversierung durch
(Abbildung 2.6). Für unser Beispiel heisst das: finde die Objekte entlang des Pfades
auctions.open_auctions.auction.object dann schaue für jedes Element ob ein Pfad
auction.bids.bid existiert der für bid einen Wert > 500000 hat.
• Bottom Up: benutze einen Index um alle atomaren Objekte mit einem Wert > 500000
zu finden. Dann traversiere rückwärts entlang der umgekehrten Path_expression durch
die Daten. Diese Methode braucht einen zusätzlichen Index um Parents von Knoten zu
finden.
• Hybrid: Evaluiere einen Teil der from Klausel um eine Menge von gültigen Objekten
zu identifizieren. Dann benutze den Index um atomare Objekte die die where Klausel
erfüllen zu finden und benutze dann eine andere Index Struktur um bis zum selben
Punkt wie die Top Down Suche aufwärts zu steigen und kombiniere dann die traversierten Pfade um das Resultat zu erhalten.
2.6. SPEZIALISIERTE SYSTEME
23
Project
(OA4)
Join
Select
(OA6 = TRUE)
Join
Scan
(OA2,"object",OA4)
Join
Scan
(OA2,"bids",OA3)
Join
Join
Scan
(OA1,"auction",OA2)
Aggr
(Exists, OA5, OA6)
Select
(OA5 > 500000)
Scan
(OA3,"bid",OA5)
Scan
(OA0,"open_auctions",OA1)
Scan
(Root,"auctions",OA0)
Abbildung 2.6: Lore Query Plan
OA0
(auctions)
OA1
OA2
OA3
(OA0.open auctions) (OA1.auctions) (OA2.bids)
OA4
OA5
OA6
(OA2.object) (OA3.bid) (true/fals)
Abbildung 2.7: Beispiel für Object Assignment
24
KAPITEL 2. SPEICHERN VON XML DOKUMENTEN
• Inside Out: identifiziere Bereiche des Graphen die den mittleren Teil der Path Expression erfüllen, dann traversiere aufwärts durch die Parents und abwärts durch die
Children um den restlichen Teil der Path Expression zu evaluieren.
Indexing
In traditionelle relationale Datenbanken werden Indexe auf Attribute kreiert um Tuples mit
einem bestimmten Attributwerte schnell zu finden. In Lore reicht ein solcher Value Index
alleine nicht aus, weil der Pfad zu einem Objekt genauso wichtig wie der Wert des Objekts
ist. In Lore gibt es deshalb zwei verschiedene Indexe:
• Vindex oder Value Index um Objekte mit einem spezifischen Wert zu finden. Ein Vindex hat als Input ein Label, einen Operator und einen Wert. Er gibt alle atomaren
Objekte die eine eingehende Kanten mit dem spezifizierten Label und einen Wert der
den spezifizierten Operator Wert erfüllen (z.B. <500000) haben. Die gewünschten eingehenden Labels sind normalerweise zu Query Processing Zeit bekannt, weshalb Vindex
nach Labels aufgeteilt werden. Ein Vindex wird über alle atomaren Objekte mit den
Basistypen Integer, Real oder String gebildet. Administratoren ist es erlaubt selektiv
Indexe über häufig gebrauchte Labels zu bilden. Da man in Lore Werte mit verschieden
Typen vergleichen kann werden pro Label drei verschiedene Indexe gebildet.
– String Vindex für String basierte atomare Werte (string,HTML,URL, usw.).
– Real Vindex für alle numerisch basierten Werte (integer und real).
– String-coerced-to-real Vindex für alle string Values die in Integer oder real umgewandelt werden k”önnen (werden im Index als Real gespeichert).
Jeder Index ist als B+ Baum implementiert
• Lindex oder Link (edge) Index welcher Parents lokalisiert. Ein Lindex nimmt als Input
eine OID und ein Label und gibt alle OIDs der Parents via das spezifizierte Label
zurück. Lindexe sind n”ötig um Parents zu lokalisieren, weil in OEM inverse Zeiger
nicht unterstützt werden. Wenn kein Label spezifizert wird, werden alle Parents und
deren Labels zurückgegeben. Es wird für die ganze Datenbank ein Lindex kreiert, der
mittels erweitertem Hashing implementiert ist.
Zwei andere Arten von Indexen wären für das OEM auch noch m”öglich3 :
• Tindex oder Text Index lokalisiert atomare String Values die spezifische W”örter
oder Wortgruppen enthalten.
• Pindex oder Path Index erlaubt schnellen Zugriff auf alle erreichbaren Objekte via
einem spezifizierten Pfad. Pindex gibt für einen Pfad p alle Objekte o zurück die über
p erreichbar sind. Lore indexiert nur Pfade die an einem named Objekt beginnen und
keine reguläre Ausdrücke enthalten.
Lore: DataGuides
Da eine Lore Datenbank kein explizites Schema hat, sind Query Formulierungen und Query
Optimierungen eine besondere Herausforderung. Ohne einer gewissen Ahnung der Struktur
der zugrunde liegenden Datenbank kann das Schreiben von aussagekräftigen Queries schwierig
sein.
Ein DataGuide ist eine kurze und genau Zusammenfassung der Struktur einer OEM Datenbank, selbst wieder als OEM Objekt gespeichert. Jeder m”ögliche Pfadausdruck der Datenbank ist genau einmal gespeichert und ein DataGuide hat keinen Pfadausdruck gespeichert,
der nicht existiert. Im typischen Situation ist ein DataGuide wesentliche schmaler als die Originaldatenbank. Den DataGuide für unser Beispiel zeigt Abbildung 2.8. DataGuides werden
dynamisch generiert und wenn die Struktur ändert dynamisch angepasst. Sie sollen dem Benutzer helfen eine Query zu schreiben und durch die Datenbank zu browsen um die Struktur
der Datenbank zu untersuchen. Zudem unterstützen sie den Query Prozessor und helfen dem
System Zugriffsstatistiken zu erstellen.
3 Wurden in der Vorlesung vorgestellt, finden aber im Paper [37] keine Erwähnung, sind
also wahrscheinlich in Lore nicht implementiert
2.7. DATENSPEICHERMODELLE FÜR XML
25
open_auctions
auctions
auction
item
object
initial
name
bids
description
bid
comment
time
val
Abbildung 2.8: Lore DataGuide für die Beispiel Datenbank aus Abbildung 2.9
2.7
2.7.1
Datenspeichermodelle für XML
OEM
Das Object Exchange Model (OEM) ist ein an der Stanford Universität entwickeltes Object
Model, das spezielle für das Verwalten von semi-strukturierte Daten designt wurde[37]. Daten
in diesem Modell können als beschrifteter gerichteter Graph (labeled directed graph) betrachtet
werden. Die Knoten (vertices) im Graph sind Objects und jedes Objekt hat einen eindeutigen
object identifier (oid). Atomic Objects haben kein Kanten die von ihnen wegführen (outgoing
edges) und enthalten eine wert aus einem der Basis Typen integer, real, string, gif, java,
audio, etc. Alle anderen Objekt haben eine beliebige Anzahl ausgehende Kanten und werden
complex objects genannt. Ein Beispiel zeigt Abbildung 2.9. Objekt &2 ist z.B. ein komplexes
Objekt mit den atomaren Subobjekten &5, &6 und &7. Das atomare Objekt &5 hat dabei den
Wert “ring”. Names sind spezielle Labels die als Aliase für Objekte und als Einstiegspunkt in
die Datenbank dienen. In unserem Beispiel ist auctions ein Name für das Objekt &1. Jedes
Objekt, das nicht durch einen Pfad von einem Namen (Root Object) aus erreicht werden kann
wird als gelöscht betrachtet.
In einer OEM Datenbank gibt es keine fixe Schemanotation. Alle schematischen Informationen
sind in den Labels enthalten und können dynamisch ändern. Eine OEM Datenbank ist also
selbst beschreibend und es gibt keine auferlegte Ordnung. Das Model kann Unvollständigkeit von Daten ebenso handhaben wie Struktur- und Typen- Heterogenität. D.h. das Kanten
mit einem gleichen Label auf Objekte mit verschiedenen Typen (verschiedene atomare Typen
sowie komplexe Typen) zeigen. Da es sich beim Model um einen gerichtet Graphen handelt
können natürlich auch Zyklen (cycles) vorkommen.
Speicherung von OEM Objekten in Lore
Lore arrangiert Objekte in physikalischen disk pages; jede page hat eine Anzahl slots mit
einem einzelnen Objekt. da Objekte eine variable Länge haben platziert Lore Objekte in
einem first-fit Algorithmus und bietet ein object-forwarding, falls ein Objekt zu gross für
dessen Page werden sollte. Zusätzlich unterstützt Lore grosse Objekte, die sich über mehrere
Pages ausbreiten können. Objekte werden in depth-first Art auf die Pages verteilt, in erster
26
KAPITEL 2. SPEICHERN VON XML DOKUMENTEN
auctions
&1
item
item
open_auctions
name
&2
&3
object
"ring"
auction
auction
&5
&4
comment
&6
"large diamond
ring"
&10
&9
&8
description
name
description
"camera"
&7
"very good
condition"
initial
&12
bids
bids
initial
&15
&14
&13
75
500’000
bid
bid
bid
&18
&17
&16
550’000
time
&19
[12:00 2/11/2004]
val
&20
85
Abbildung 2.9: OEM Daten Model
&11
"compact
digital
camera"
2.7. DATENSPEICHERMODELLE FÜR XML
<auktion>
<artikel key="ID108">
<name>Ring</name>
<beschreibung>
Grosser Diamant
</beschreibung>
</artikel>
<artikel key="ID203">
<name>Kamera</name>
<beschreibung>
Digitalkamera
</beschreibung>
<kommentar>
fast neu
</kommentar>
</artikel>
</auktion>
27
auktion, o1
"ID108" key
artikel, o2
name, o3
cdata,o4
string
artikel, o7
beschreibung, o5
name, o8
cdata,o6
cdata, o9
string
"Ring" "Grosser Diamant"
string
key
"ID203"
beschreibung, o 10 kommentar, o
12
cdata,o11
cdata, o13
string
"Kamera" "Digitalkamera"
string
"fast neu"
Abbildung 2.10: XML Dokument Ausschnitt und der korrespondierende Syntax
Baum
Linie, weil die scan-basierten Pläne (scan Operator) die Datenbank depth-first traversiert. Es
ist nicht immer möglich alle Objekte nahe zu ihren Parents zu platzieren, weil ein Objekt
mehrere Parents haben kann, in diesem Fall wird das Objekt mit einem willkürlichen Parent
gespeichert. Wenn ein Objekt nicht über einen Pfad von einem named Objekt aus erreicht
werden kann, wird es vom garbage collector gelöscht.
2.7.2
Monet
Begriffe
Die Konzepte von associations und path summaries bildet die Basis für das Monet XML
Model [43].
Definition 2. Ein Paar (o, ·) ∈ oid × (oid ∪ int ∪ string) nennt man eine association.
Die verschiedenen Typen von Assoziationen beschreiben verschiedene Teile des Baumes: Assoziationen vom Typ oid × oid representieren Kanten, d.h. Parent-Child Abhängigkeiten. Attributwerte (inklusive CDATA, durch Vertices mit Label ‘string’, die von ‘cdata’ beschrifteten
Knoten starten, repräsentiert) werden durch Assoziationen vom Typ oid × string modelliert,
während Assoziationen vom Typ oid × int für die Erhaltung der Dokument Topologie benutzt
werden.
Definition 3. Für einen Knoten o im Syntax Baum, bezeichnen wir die Sequenz von
Labels entlang des Pfades (Vertex und Edge Labels) von der Wurzel bis o mit path(o).
e
Als ein Beispiel betrachte in Abbildung 2.10 den Knoten mit OID o3 . Sein Pfad ist auktion →
e
e
artikel → name. Der korrespondierende Character Data String “Ring” hat den Pfad auktion →
e
e
a
e
a
artikel → name → cdata → string. → bezeichnen dabei Kanten zu Elementen und → zu
Attributen.
Pfade beschreiben die Position des Elements im Graph relativ zum Root Knoten.Die menge
aller Pfade in einem Dokument nennt man die path summary des Dokuments.
Das Monet XML Model
In Monet werden alle Assoziationen des selben Typs in der selben Binären Relation abgespeichert. Eine Relation die das Tupel (·, o) enthält wird path(o) genannt und umgekehrt ist
28
KAPITEL 2. SPEICHERN VON XML DOKUMENTEN
e
auktion → artikel
e
e
auktion → artikel → name
e
e
e
auktion → artikel → name → cdata
e
e
e
a
auktion → artikel → name → cdata → string
=
{ho1 , o2 i, ho1 , o7 i}
=
{ho2 , o3 i, ho7 , o8 i}
=
{ho3 , o4 i, ho8 , o9 i}
=
{ho4 ,“Ring”i,
ho9 ,“Kamera”i}
e
e
auktion → artikel → beschreibung
e
e
e
auktion → artikel → beschreibung → cdata
e
e
e
a
auktion → artikel → beschreibung → cdata → string
=
{ho2 , o5 i, ho7 , o10 i}
=
{ho5 , o6 i, ho10 , o11 i}
=
{ho6 ,“Grosser Diamant”i,
ho11 ,“Digitalkamera”i}
e
e
auktion → artikel → kommentar
e
e
e
auktion → artikel → kommentar → cdata
e
e
e
a
auktion → artikel → kommentar → cdata → string
e
a
auktion → artikel → key
=
{ho7 , o12 i}
=
{ho12 , o13 i}
=
{ho13 ,“fast neu”i}
=
{ho2 ,“ID108”i,
ho7 ,“ID203”i}
Abbildung 2.11: Monet Transformation Mt des Beispiels aus Abbildung 2.10
ein Tupel genau in einer Relation gespeichert. Monet mappt ein XML Dokument d in vier
Strukturen mittels der Monet Transformation Mt :
Definition 4. Ist ein XML Dokument d gegeben, dann ist die Monet Transformation ein
Quadrupel Mt (d) = (r, R, A, T) wobei
R die Menge der binären Relationen ist, welche alle Assoziationen zwischen Knoten enthält (Parent-Child);
A die Menge der binären Relationen ist, welche alle Assoziationen zwischen Knoten und
deren Attributwerten enthält(inklusive Character Data);
T die Menge der binären Relationen ist, welche alle Paare von Knoten und deren Ordnung ( ranking) enthält.
r ist die Wurzel (root) des Dokuments.
Abbildung 2.11 zeigt die Monet Transformierung des Beispiels aus Abbildung 2.10.
Strong und Weak Associations
Da XML Dokumente semi-strukturierte Daten sind, kann man nicht erwarten, dass alle Instanzen eines Typs die selbe Struktur haben. In unserem Beispiel hat das zweite artikel
Element eine kommentar Element, während das erste keines hat. In Monet unterscheidet man
deshalb zwischen zwei Arten von Assoziationen: (strong) associations und weak Associations.
Strong associations bilden den strukturierten Teil von XML (d.h. sie sind in allen Instanzen
eines Typs vorhanden); weak associations sind für den semi-strukturierten Teil (sie erscheinen
nicht in allen Instanzen).
Ausführung
Interessant ist die Frage wie eine OQL ähnliche Query übersetzt wird. Mit folgender Beispiel
Query wollen wir den Artikel mit den Namen “Kamera” der noch “fast neu” ist finden:
2.8. ÜBERSICHT/VERGLEICH
29
select a
from auktion -> artikel a,
a -> name -> cdata -> n,
a -> kommentar -> cdata -> k
where n="Ring" and k like "fast neu";
Zuerst wird eine Assoziation zwischen dem ersten und dem letzten Element im Pfad etabliert,
indem alle Pfadausdrücke die nicht in der Datenbank enthalten sind durch joining der binären
Relationen entlang der Pfad Spezifikation fallen gelassen werden. Dann wird die Schnittmenge der der spezifizierten Elemente genommen. Am Schluss werden die relevanten Elemente
selektiert. Die from Klausel spezifiziert für unser Beispiel folgende Elemente:
a
=
{o2 , o7 }
assoc(a → n)
=
{(o2 ,“Ring”), (o7 ,“Kamera”)}
assoc(a → k)
=
{o7 ,“fast neu”)}
Fehlende Features für XML
Das Monet Speichermodell stellt die Eindeutigkeit von ID und die Konsistenz der ID/IDREFs
nicht sicher. Zudem wurde zur Vereinfachung nicht zwischen CDATA und PCDATA unterschieden.
Um das ID/IDREF Konzept zu unterstützen müsste die Unterstützung von rich data types ID
und IDREF unterstütz werden. Um IDREFS speichern zu können müssten multi-valued Attribute unterstützt werden. Ausserdem müsste die binäre Relation A in der Monet Transformation
Mt erweitert werde um die Einzigartigkeit der ID und die Integrität mit den ID/IDREFS kodieren zu können. Die Unterstützung von ID/IDREF könnte erreicht werden, indem man zwei
Subsets A spezifiziert: AID und AIDREF . Diese zwei Subsets würden dann die Paare von
Knoten/Attributwerte in A enthalten die ein ID oder IDREF sind. Das System müsste dann
diese Information auf dem laufenden und Konsistent halten.
2.8
Übersicht/Vergleich
Es folgt eine kleiner Vergleich zwischen den verschiedenen Speichermodellen. Einerseits die
unter Abschnitt 2.4 vorgestellten Methoden Custom mit der Ausnützung des Schemas und
das General Design, welches auf der Basis von DOM ein XML Dokument speichert, sowie
die unter Abschnitt 2.7 vorgestellten Datenmodelle für semi-strukturierte Daten Lore (OEM)
und Monet:
30
KAPITEL 2. SPEICHERN VON XML DOKUMENTEN
• höhere Performance
• Beim Querying der Datenbank sind weniger TabellenScans involviert
Custom Design
• Statisches Modell: jede kleine Änderung der Dokument Struktur kann grosse Änderungen in der Tabellen Struktur hervorrufen
• Optionale Attribute werden entweder in separaten
Tabellen modelliert oder durch die Verwendung von
NULLs
• Die Struktur der Queries ist sehr nahe der Struktur
des Schemas (einfach zu lesen)
• Generelles Model für jede Art von XML Dokumenten
• Einfache und reguläre Struktur (wenige Tabellen)
• Speichert keine Informationen über die gespeicherten
Dokumente
DOM basiert
• Um die Struktur eines Dokuments zu erfahren, muss
das ganze Dokument gescannt werden und die Struktur extrahiert werden
• Die Queries sind länger, schwieriger zu lesen und kann
das scannen von grossen Tabellen bedeuten.
• Generelle Lösung für semi-strukturierte Dokumente
• DataGuides bieten Informationen über die Struktur
eines Dokuments
Lore (OEM)
• Direkte Kontrolle der Objektspeicherung führt zu einem effizienten Layout auf der Disk (Clustering in
depth-first Ordnung)
• Um die Struktur eines Dokuments zu erfahren, muss
das ganze Dokument gescannt werden und die Struktur extrahiert werden
• Wenn keine angemessener Index vorhanden ist, sind
ineffiziente Queries möglich, da mehrere Disk Scans
nötig sind
• Generelle Speicherung von semi-strukturierte Dokumente
Monet
• Gute Performance durch Aufteilung der Daten in mehrere Tabellen
• Das Handling von optionalen (unstrukturierten) Daten ist möglicherweise ineffizient (viele Tabellen mit
wenigen Zeilen)
• Lose Transformation von XML Dokumenten
Kapitel 3
Querying von XML
Dokumenten
3.1
Einführung
Wir brauchen eine Query Language um XML Daten verarbeiten zu können. Die logische
bzw. physikalische Datenunabhängigkeit sollte dabei bewahrt werden, d.h. die Daten sollen
als Abstraktes Modell beschrieben werden und nicht als physikalischer Datenspeicher. Wir
wollen zudem eine deklarative Programmiersprache, die das “was” und nicht das “wie” beschreibt. SQL ist dabei nicht ausreichend, weil man die Eigenheiten wie z.B. hierarchische,
geordnete Struktur, textuelle Repräsentation und mögliche Schemalosigkeit des XML Modells
handhaben können muss.
XML Daten unterscheiden sich von relationalen Daten in verschiedenen bedeutenden Aspekten, die das Design einer Query Language stark beeinflussen. Relationale Daten tendieren zu
einer regulären Struktur, was erlaubt, die beschreibenden Metadaten in einem separaten Katalog zu speichern. XML Daten dagegen sind oft heterogen und die Metadaten sind direkt im
Dokument enthalten. XML Dokumente enthalten oft viele Ebenen von ineinander geschachtelten Elementen. XML Dokumenten haben eine innere Reihenfolge, während relationale Daten
grundsätzlich ungeordnet sind, ausser wenn eine Ordnung aus den Daten abgeleitet werden
kann. Ein weiteres Problem kommt dazu, weil fehlende Informationen durch die Abwesenheit
eines Elements und im allgemeinen nicht wie beim relationalen Modell durch NULL repräsentiert werden. Die existierenden Query Sprachen sind deshalb für XML Dokumente nicht
geeignet.
3.2
XQuery
XQuery ist eine funktionale Sprache, welche mehrere Arten von Ausdrücke umfasst, die mit
voller Allgemeinheit verschachtelt und zusammengestellt werden können. Sie basiert auf dem
Typsystem von XML Schema und wurde so designt, dass sie zu anderen mit XML nahen
Standards kompatibel ist[16].
XML Query wird vom der XML Query Working Group des World-Wide Web Consortium
(W3C) entwickelt und ist noch nicht vollständig abgeschlossen1 .
XQuery hat sich aus einem Sprachvorschlag namens Quilt entwickelt und ist stark von existierenden W3C Standards wie XML Schema, XSLT, XPATH und XML selber beeinflusst.
Speziell XPath ist so wichtig und eng mit XQery verbunden, dass XQuery als ein Superset
von XPath definiert ist. Das ursprüngliche Design konzentriert sich nur auf die Gewinnung
1 Berichte
unter www.w3.org/XML/Query
31
32
KAPITEL 3. QUERYING VON XML DOKUMENTEN
von Informationen aus XML Dokumenten und stellt keine Möglichkeit zum ändern von Informationen (update) bereit. XQuery umfasst zwei Syntax: eine maschinenlesbare XML Form
machine-readable form (XQueryX ) und eine für die Lesbarkeit für Menschen optimierte Form
human-readable form (XQuery).
3.2.1
Daten Modell
Das query data model wie es in [51] beschrieben ist, bietet eine abstrakte Repräsentation
eines oder mehrerer XML Dokumente oder Dokumentfragmente. Das Daten Modell basiert
auf der Notation einer Sequenz. Eine sequence ist eine geordnete Kollektion von Null oder
mehr items. Ein item kann ein node oder ein atomic value sein. Ein atomic value ist eine
Instanz von einem der built-in Datentypen, die durch XML Schema definiert sind, also z.B.
string, integer, decimal und date. Ein node entspricht einem der sieben Arten von Knoten,
element, attribute, text, document, comment, processing instruction und namespace node. Ein
node kann andere Knoten als Kinder haben. Unter all den Knoten in einer Hierarchie gibt es
eine totale Ordnung, die man document order nennt.
Beispiel 3.1:
XML
<book year="2000 2004">
<title>The Social Life of Information</title>
<author>Brown</author>
<author>Duguid</author>
<publisher>Harvard Business School press</publisher>
<price>11.87</price>
<review><em>Very</em>readable</review>
</book>
XQuery
element book{
attribute year{2000, 2004},
element title {"The Social Life of Information"},
element author {"Brown"},
element author {"Duguid"},
element publisher {"Harvard Business School press"},
element price {11.87},
element review {element em {"Very"},"readable"}
}
3.2.2
XPath
Wie in [50] beschrieben, besteht der Hauptzweck der vom W3C entwickelten Sprache darin
Teile eines XML Dokuments zu adressieren. Sie bietet auch grundsätzliche Möglichkeiten um
Strings, Zahlen und Boolean zu manipulieren. XPath operiert auf der abstrakten, logischen
Struktur eines XML Dokuments und nicht dem XML Text. XPath hat seinen Namen wegen
der Benutzung von Pfadnotationen wie in URLs um durch die hierarchische Struktur eines
XML Dokuments zu navigieren.
Beispiel 3.2:
XML Dokument:
<bib>
<book year="2003">
<title>XML Data Management</title>
<author>Chaudhri</author>
3.2. XQUERY
33
<author>Rashid</author>
<author>Zicari</author>
<publisher>Addison-Wesley</publisher>
<price>34.99</price>
<review>Comprehensive guide to XML and databases</review>
</book>
<book year="2000 2004">
<title>The Social Life of Information</title>
<author>Brown</author>
<author>Duguid</author>
<publisher>Harvard Business School press</publisher>
<price>11.87</price>
<review><em>Very</em>readable</review>
</book>
</bib>
XPath Expression:
doc("books.xml")/bib/book
Verarbeitung :
• öffne books.xml mit der eingebauten Funktion doc() und gib den Dokumentknoten
zurück
• selektiere das Element bib am Dokumentanfang mit /bib
• selektiere die book Elemente innerhalb des bib Elements mit /book
äquivalent dazu könnte man auch
doc("books.xml")//book
schreiben.
Ein Pfadausdruck (auch location path) besteht aus einer Reihe von Schritten, location steps,
die durch ein / (“slash”) getrennt sind. Die Ausdrücke werden von links nach rechts ausgewertet
und das Resultat jedes Schrittes ist eine Sequenz von Knoten. Der Wert der path expression
ist die Sequenz von Knoten, die aus dem letzten Schritt resultiert.
Jeder Schritt wird im Kontext von einem bestimmten Knoten, dem Kontextknoten (context
node) ausgewertet. Im Allgemeinen kann ein Schritte ein Ausdruck sein, der ein Sequenz von
Knoten zurück gibt. Eine wichtige Art von Schritt, ein axis step beginnt am context node und
bewegt sich in einer bestimmten Richtung, der axis durch die Knotenhierarchie. Während der
axis step sich entlang dieser Richtung bewegt, selektiert er Knoten die ein Selektionskriterium
erfüllen. Diese Selektionskriterium kann einen Knoten basierend auf seinem Namen, seiner
Position relativ zum context node oder einem Prädikat basierend auf dem Wert des Knoten
auswählen.
Der ungekürzte Syntax besteht aus einer axis und einem Selektionskriterium, die durch zwei
Doppelpunkte getrennt sind (siehe Abbildung 3.1).
Es gibt aber auch einen gekürzten Syntax:
child::
attribute::
/descendant-or-self::node()/
self::node()
parent::node()
Dieser Locationpfad wird als default benutzt, wenn keine Axis angegeben wird. Kann also auch weggelassen
werden.
Diese Axis kann als @ abgekürzt werden.
Wird als // abgekürzt
der context node kann mit . abgekürzt werden.
der context node parent kann mit .. abgekürzt werden.
34
KAPITEL 3. QUERYING VON XML DOKUMENTEN
• child::para selektiert die para Element die Kinder des context node sind
• child::* selektiert alle Kinder des context node
• child::text() selektiert alle text node Kinder des context node
• child::node() selektiert alle Kinder des context node, unabhängig vom Typ
• attribute::name selektiere das name Attribute des context node
• attribute::* selektiert alle Attribute des context node
• descendant::para selektiert die para Element die Nachkommen des context node sind
• ancestor::div selektiert alle div Vorfahren des context node
• ancestor-or-self::div selektiere die div Vorfahren des context node und wenn der context
nod ein div Element ist, auch den context node selbst
• descendant-or-self::para selektiere die div Nachkommen des context node und wenn der
context nod ein div Element ist, auch den context node selbst self::para selektiere den
context node, wenn er ein para Element ist, sonst selektiere nichts
• child::chapter/descendant::para selektiere die para Elemente die Nachkommen des chapter
Kindelements des context node sind
• child::*/child::para selektiert alle para grandchildren des context node
• / selektiere die document root (welche immer der Vater (parent) des document element ist)
• /descendant::para selektiert alle para Elemente im selben Dokument wie der context node
• /descendant::olist/child::item selektiert alle item Elemente, welche eine olist Vater haben
und im selben Dokument wie der context node sind
• child::para[position()=1] selektiere das erste para Kind des context node
• child::para[position()=last()] selektiere das letzte para Kind des context node
• child::para[position()=last()-1] selektiere das vorletzte para Kind des context node
• child::para[position()>1] selektiert alle Kinder des context node ausser dem ersten
• following-sibling::chapter[position()=1] selektiere den nächsten chapter sibling des context node
• preceding-sibling::chapter[position()=1] selektiere den vorherigen chapter sibling des context node
• child::para[attribute::type="warning"] selektiert alle para Kinder des context node, welche
ein type Attribute mit dem Wert warning haben
• child::para[attribute::type=’warning’][position()=5] selektiere das fünfte para Kind des
context node welches ein type Attribute mit dem Wert warning hat
• child::para[position()=5][attribute::type="warning"] selektiere das fünfte para Kind des
context node, wenn es ein type Attribute mit dem Wert warning hat
• child::chapter[child::title=’Introduction’] selektiere die chapter Kinder des context node, welche eines oder mehr title Kinder mit den String Wert Introduction haben
• child::chapter[child::title] selektiere die chapter Kinder des context node, welche eines
oder mehr title Kinder haben
• child::*[self::chapter or self::appendix] selektiere die chapter und appendix Kinder des
context node
• child::*[self::chapter or self::appendix][position()=last()] selektiere das letzte chapter
oder appendix Kind des context node
Abbildung 3.1: XPath Axis Beispiele mit ungekürzter Syntax
3.2. XQUERY
3.2.3
35
Node Test
Wie man schon im Beispiel aus Abbildung 3.1 sieht, können verschiedene Knoten(-typen)
angesprochen werden:
*
node()
text()
comment()
processing-instruction()
node name
selektiert alle Knoten (mit dem selben prinzipiellen Knotentyp)
selektiert alle Knoten unabhängig von deren Typ
selektiert alle Text-Knoten
selektiert alle Kommentar-Knoten
selektiert alle processing-instruction-Knoten
selektiert Knoten mit dem spezifischen Knotennamen
36
KAPITEL 3. QUERYING VON XML DOKUMENTEN
Teil II
Database Application
Programming
37
Kapitel 4
SQL in
Programmierumgebungen
4.1
Datenbankapplikationen
Um auf eine Datenbank zuzugreifen haben die meisten DBMS ein interaktives Interface in
welches SQL Kommandos direkt eingetippt und ausgeführt werden können. Dieses Interface
genügt meist, für das Erstellen einer Datenbank und ihren Tabellen oder einzelnen einfachen
Queries. Die meisten Interaktionen mit einer Datenbank werden in der Praxis aber durch
Applikationen ausgeführt. Eine andere übliche Methode auf eine Datenbank zuzugreifen ist
über ein Webinterface um z.B. Bücher zu bestellen oder Flugtickets zu reservieren.
Es existieren verschiedene Techniken um Datenbank Interaktionen in Applikationen einzubinden:
1. eingebettete Datenbankkommandos in einer allgemeinen Programmiersprache (Embedded
SQL): Es werden Datenbank Statements in eine Host Programmiersprache eingebettet
embedded . Ein Precompiler oder Preprocessor scannt zuerst den Programmcode und
extrahiert Datenbankstatments für die Verarbeitung durch das DBMS. Im Programmcode werden sie durch function calls zum DBMS-generierten Code ersetzt.
Abschnitt 4.3
2. Benutze eine Library mit Datenbank Funktionen(Call Level Interface): Ein Library wird von der Hostsprache benutzt um Datenbankinteraktionen durchzuführen. Es
gibt z.B. Funktionen um eine Verbindung zur Datenbank herzustellen, um eine Query
auszuführen usw. Diese Methoden bietet ein so genanntes Application Programming
Interface (API) um von einer Applikation auf eine Datenbank zuzugreifen.
Abschnitt 4.4
3. Entwickle eine neue Sprache: Eine database programming language wird von Anfang
an neu entwickelt um kompatibel mit dem Datenbank Modell und der Query Sprache
zu sein. Zusätzliche Programmierstrukturen wie Schleifen (loop) und bedingte Statements werden einer Datenbanksprache hinzugefügt, um sie zu einer vollumfänglichen
Programmiersprache zu machen.
Abschnitt 4.5
Es findet zwischen dem Client mit der Applikation und dem Datenbankserver eine Aufgabentrennung statt. Der Client präsentiert die Daten und der Server verwaltet sie. Was man
sich bei der Entwicklung einer Datenbankapplikation überlegen muss, ist wo die Logik der
Applikation ausgeführt wird. Soll sie auf dem Server ausgeführt werden (SQL/PSM, Stored
Procedures, Triggers) oder doch besser auf dem Client (ESQL, SQL/CLI, ODBC, JDBC) ?
Also die Frage ob wir einen Thin Client oder einen Thick Client wollen.
39
40
KAPITEL 4. SQL IN PROGRAMMIERUMGEBUNGEN
4.2
Impedance Mismatch
Impedance Mismatch ist die Bezeichnung für das Problem, welches wegen den Unterschieden zwischen dem Datenbankmodell und dem Programiersprachenmodell auftaucht. Das erste Problem das auftauchen kann, ist dass sich die Datentypen der Programmiersprache von
den Attributtypen im Datenbankmodell unterscheiden. Man braucht also für jede Hostprogrammiersprache ein binding welches für jeden Attributtype den kompatiblen Datentyp der
Programmiersprache spezifiziert.
Ein anderes Problem tritt auf, weil das Resultat der meisten Queries ein set oder bag von
Tuples zurück gibt und jedes Tupel besteht aus einer Sequenz von Attributen. In einem Programm muss man oft auf individuelle Daten innerhalb der Tuples zugreifen können. Man muss
also eine query result data structure, eine Tabelle, auf eine entsprechende Datenstruktur der
Programmiersprache mappen. Ausserdem braucht es Mechanismen um über die Tupels eines
Resultats zu iterieren.
Wir können zum Programmieren von Applikationen nicht Standard SQL benutzen, weil wir
keine Pointers, Loops und Branches haben (Das Standard SQL ist nicht Turing complete).
4.3
Embedded SQL
Die meisten SQL Statements, inklusive Daten oder Constraint Definitionen, Queries, Updates oder View Definitionen, können in eine Programmiersprache (host language) eingebettet
werden. Ein eingebettetes SQL Statement wird durch den Präfix EXEC SQL von Programmiersprachen Statements unterschieden, damit der preprocessor (oder precompiler ) die SQL
Statements vom restlichen Programmcode trennen und an dessen Stelle Function Pointers
einfügen kann. Der preprocessor übernimmt auch den Typ, die Syntax und die Semantik
Überprüfung sowie die Fehlerbehandlung der SQL Statements.
Im folgenden wird C als host language benutzt
4.3.1
Shared Variables
Innerhalb eines eingebetteten SQL Kommandos wollen wir vielleicht auf eine spezielle deklarierte Variable zugreifen können. Diese shared variables werden in einer declare section
deklariert und innerhalb eines SQL Statements mit einem Doppelpunkt (:) als Präfix geschrieben.
Beispiel 4.1:
void getStudio() {
EXEC SQL BEGIN DECLARE SECTION;
char studioName[50], studioAddr[256];
char SQLSTATE[6];
EXEC SQL END DECLARE SECTION;
printf("Studiu Name: ");
scanf("%s", studioName);
printf("Studio Address:");
scanf("%s", studioAddr);
EXEC SQL INSERT INTO Studio(name, address)
VALUES (:studioName, :studioAddr);
}
SQLSTATE enthält einen fünfstelligen Errorcode (siehe Tabelle 4.1).
4.3. EMBEDDED SQL
SQLSTATE
01001
01003
01004
02000
07002
22001
40001
41
Beschreibung
Cursor update or delete failure due to
optimistic concurrency check failure.
Elimination of null values by set operator.
Select or fetch into host variable that is
too short.
No data found.
Number of columns does not match
number of host variables.
String data truncated (on right) on insert or update.
Transaction rollback due to deadlock.
Tabelle 4.1: SQLSTATE Error Codes
4.3.2
Retrieving Data
SELECT FROM WHERE Queries sind wegen dem Impedance Mismatch nicht direkt embeddable.
Man kann einen cursor als einen Pointer betrachten, der auf ein einzelnes Tupel im Resultat
einer Query zeigt. Der Cursor wird deklariert, wenn die SQL Query im Programm deklariert
wird. Später im Programm holt das OPEN CURSOR Kommando das Queryresultat von der Datenbank und setzt den Cursor auf eine Position vor der ersten Reihe im Resultat. FETCH
Kommandos bewegen den Cursor zur nächsten Reihe im Queryresultat und kopiert den Attributwert in die host language Variable die im FETCH Kommando spezifiziert wurde mit der
INTO Klausel. Mit CLOSE wird ein Cursor wieder geschlossen.
Beispiel 4.2:
void getAllMoviesOfStudio(char *studio) {
EXEC SQL BEGIN DECLARE SECTION;
char movie[50];
int year;
char SQLSTATE[6];
EXEC SQL END DECLARE SECTION;
EXEC SQL DECLARE mcursor CURSOR FOR
SELECT title, year
FROM Movies JOIN Studios ON studio = name
WHERE name = :studio;
EXEC SQL OPEN mcursor;
do {
EXEC SQL FETCH FROM mcursor INTO :movie, :year;
if (strcmp(SQLSTATE, "00000")) {
printf("Found Movie: %s (%d).\n", movie, year);
} else {
break;
}
} while (1);
EXEC SQL CLOSE mcursor;
}
Cursor können auch in SQL UPDATE oder DELETE Statements benutzt werden indem die WHERE
Klausel erweitert wird:
• DELETE FROM table WHERE CURRENT OF cursor
42
KAPITEL 4. SQL IN PROGRAMMIERUMGEBUNGEN
• UPDATE table SET column = value
WHERE CURRENT OF cursor
Scrolling cursor können benutzt werden um sich nicht sequentiell durch ein Relation zu
bewegen. Der Syntax für die Deklaration eines solchen Cursors lautet:
EXEC SQL DECLARE name SCROLL CURSOR FOR query
Um mit einem scrolling cursor Tuples zu holen lautet der Syntax:
• EXEC SQL FETCH (NEXT | PRIOR) INTO variable
• EXEC SQL FETCH (FIRST | LAST) INTO variable
• EXEC SQL FETCH RELATIVE offset INTO variable
• EXEC SQL FETCH ABSOLUTE index INTO variable
Um konkurrierende Updates handhaben zu können kann ein Cursor ein Set von Tuples lock en
während er offen ist:
EXEC SQL DECLARE name INSENSITIVE CURSOR FOR query
Ein anderer Cursor kann aber trotzdem read-only auf ein solches gesperrtes Set zugreifen:
EXEC SQL DECLARE name SCROLL CURSOR FOR query FOR READ ONLY
Oft sind Queries erst zur Compilezeit unbekannt und werden erst während der Programmausführung generiert. Um Queries während der Laufzeit zu parsen und auszuführen benutzt
man das so genannte Dynamic SQL. Man deklariert dazu eine Variable (z.B. sqlstring) in
die man die Query schreibt, dann wird die Query erzeugt und ausgeführt.
Beispiel 4.3:
void doUpdate() {
EXEC SQL BEGIN DECLARE SECTION;
char sqlstring[256];
EXEC SQL END DECLARE SECTION;
printf("Enter the Update Command: ");
scanf("%s", sqlstring);
EXEC SQL PREPARE sqlcommand FROM :sqlstring;
EXEC SQL EXECUTE sqlcommand;
}
Man kann die letzten zwei Schritte (PREPARE und EXECUTE) auch kombinieren, wenn eine Query
nur einmal ausgeführt werden muss:
EXEC SQL EXECUTE IMMEDIATE sqlstring
4.3.3
Zusammenfassung
Die Konzepte, welche mit ESQL (embedded SQL) eingeführt wurden sind auch für die meisten
database connectivity libraries (4.4) üblich.
Es gibt für die meisten üblichen Programmiersprachen, wie Ada, C, C++, Cobol, Fortran,
Pascal, Eiffel, PL/1 Implementationen. Für Java gab es eine Implementation namens SQLJ.
Die DMBMS Hersteller plannen aber nicht den ESQL Support weiterzuführen sondern forcieren den Umstieg auf Call Level Interfaces (siehe Abschnitt 4.4).
4.4. CALL LEVEL INTERFACE
4.4
43
Call Level Interface
Embedded SQL (Abschnitt 4.3) wird manchmal als statische Methode der Datenbank Programmierung bezeichnet, weil der Querytext innerhalb des Programmcode geschrieben ist und
nicht geändert werden kann ohne den Code neu zu compilieren und neu zu verarbeiten (ein
Ausnahme ist dynamic SQL, Abschnitt 4.3.2 Seite 42).
Die Benutzung von function calls ist eine dynamischere Methode der Datenbankprogrammierung als ESQL und wird heute vorgezogen. Eine Library von Funktionen, auch als application
programming interface (API) bekannt, wird für den Zugriff auf eine Datenbank benutzt. Obwohl diese Methode flexibler ist, da keine preprocessor benötigt wird, hat diese Methode den
Nachteil, dass der Syntax und andere Checks zur Laufzeit ausgeführt werden müssen. Ein
anderer Nachteil ist, dass manchmal eine komplexere Programmierung nötig ist um auf Queryresultate zuzugreifen, da der Typ und die Anzahl der Attribute des Resultates vielleicht im
Voraus noch nicht bekannt ist.
Es existieren verschiedene Implementationen:
• Open Database Connectivity (ODBC)
• SQL/CLI (Call Level Interface), ein Nachfolger von ODBC, als Teil des SQL Standards. Wird z.B. für C benutzt.
• JavaTM Database Connectivity1 (JDBC) Abschnitt 4.4.1
R
• ActiveX
Data Objects(ADO)
4.4.1
JDBC: SQL Funktionen für JAVA Programmierung
Das JDBC API bietet Java Applikationen einen mid-level Zugriff auf die meisten Datenbanksysteme, mittels SQL[23].
JDBC ist Suns Versuch ein platform-neutrales Interface zwischen Java und Datenbanken zu
kreieren. Mit JDBC kann ein Programmierer auf ein Standardset von Datenbankzugriffsmerkmalen und (normalerweise) einer Untermenge von SQL (SQL-92) zählen. Das JDBC API definiert eine Menge von Interfaces, welche die Hauptfunktionalitäten einer Datenbank kapseln.
Ein Datenbank Hersteller (oder ein Drittentwickler) schreibt einen JDBC driver für ein bestimmtes Datenbanksystem.
Die Grundfunktionalitäten von JDBC für J2SE werden durch die JDBC class libraries
java.sql.* geboten. Eine Extension für J2EE bieten die javax.sql.* Libraries.
JDBC Driver
Ein JDBC driver ist eine Menge von Klassen welche die JDBC Interfaces für ein bestimmtes
Datenbanksystem implementieren.
JDBC wurde so designt, dass es einem einzelnen Java Programm erlaubt ist zu verschiedenen
Datenbanken zu verbinden. Man nennt diese Datenbanken manchmal data sources. Diese
data sources können in verschiedenen DBMS von unterschiedlichen Herstellern auf verschiedenen Maschinen gespeichert sein. D.h. Zugriff auf verschiedene data sources innerhalb eines
Java Programms benötigt vielleicht JDBC driver von verschiedenen Herstellern. Um diese
Flexibilität zu erreichen wird eine spezielle Klasse, der driver manager benutzt. Ein Treiber
sollte beim driver manager registriert werden bevor er benutzt wird.
Um einen Treiber explizit zu laden kann die generische Java Funktion zum Laden von Klassen
benutzt werden, dadurch wird der Treiber geladen, beim Manager registriert und für das Programm verfügbar gemacht. Abbildung 4.1 zeigt ein einfaches Beispiel mit einer Verbindung
auf eine Microsoft SQL Server und einem einfachen Abfrage Statement. Statements und wie
Resultate behandelt werde folgt weiter unten.
1 Laut Sun ist JDBC nicht ein Akronym für Java Database Connectivity ([23] Kapitel 2),
da aber die meisten Leute das so annehmen und auch in der Vorlesung (und den Folien) dieses
Akronym benutzt wurde, erwähne ich es hier
44
KAPITEL 4. SQL IN PROGRAMMIERUMGEBUNGEN
import java.sql.*;
import java.lang.*;
public class JDBCExample {
public static void main(String[] args) {
try {
// load Microsoft SQL Server driver
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
}
catch (ClassNotFoundException e) {
System.out.println("Unable to load Driver Class");
return;
}
try {
// connect to database, specifying database, username and password
Connection con = DriverManager.getConnection(
"jdbc:microsoft:sqlserver://grant.inf.ethz.ch:1433","user","pwd");
// create and execute SQL Statement
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT name FROM address");
//display SQL Results
while(rs.next()) {
System.out.println(rs.getString("name"));
}
// release databse resources and close connection
rs.close();
stmt.close();
con.close();
}
catch (SQLExeption sqle) {
System.out.println("SQL Exception: "+sqle.getMessage());
}
}
}
Abbildung 4.1: Einfaches Beispiel einer Datenbankabfrage mit Java JDBC
4.4. CALL LEVEL INTERFACE
45
JDBC URLs
Ein JDBC driver benutzt eine URL um eine bestimmte Datenbank zu identifizieren und eine
Verbindung zu ihr herzustellen. Diese URLs haben im allgemeinen folgende Form:
jdbc:driver :databasename
Statements
In JDBC gibt es drei Arten von Statements:
Statement
PreparedStatement
CallableStatement
Repräsentiert ein einfaches, allgemeines Statement
Repräsentiert ein vorkompiliertes SQL Statement
erlaubt den Zugriff auf stored procedures innerhalb der Datenbank
selbst
Um ein Statement auszuführen haben wir zwei Möglichkeiten:
executeQuery
executeUpdate
Führt ein Query aus und gibt ein ResultSet zurück
Für Queries bei denen kein Resultat erwartet wird (gibt nichts
zurück)
Wie ein einfaches Statement mit Statement gebildet wird zeigt das Beispiel von Abbildung 4.1
Die PreparedStatement Objekte machen grob gesagt das gleiche wie normale Statements, sie
führen ein SQL Statement aus. PreparedStatement erlaubt aber ein SQL Statement vorzukompilieren und es mehrmals laufen zu lassen und dabei die Parameter wenn nötig anzupassen.
Wie mit Statement kreiert man ein PreparedStatement Objekt von einem Connection Objekt,
indem man die prepareStatement Methode von Connection benutzt.
Da die Hauptidee von PreparedStatement darin besteht das Statement wiederholt mit anderen Parametern auszuführen, spezifizieren wir keine Werte im Statement sondern benutzten
ein Fragezeichen (?) als Platzhalter und spezifizieren die Parameter vor dem Ausführen des
Statements. Tabelle 4.2 zeigt die verschieden SQL Typen, den entsprechenden Java Typ und
die setXXX Methoden zum setzen. Abbildung 4.2 zeigt ein Beispiel mit PreparedStatement.
Resultate
JDBC benutzt das java.sql.ResultSet Interface um das Query Resultat abzukappseln. Man
kann ResultSet als ein Objekt betrachten, welches die darunterliegende Tabelle der Query
Resultate representiert und indem man Methoden benutzt um durch die Reihen zu navigieren
und einzelne Kolonnenwerte auszulesen.
Wenn man mit einem ResultSet beginnt ist der Cursor vor der ersten Reihe positioniert. Mit
der next() Methode des ResultSet Objekts bewegt man den Cursor um eine Zeile weiter. Den
Wert eines Koloneneintrages in der entsprechen Zeile kriegt man mit einer getXXX("columnname")
Methode, XXX steht dabei für den Typ der zurückgegeben wird (gleich wie in den setXXX Methoden in Tabelle 4.2).
Der Cursor kann mit verschiedenen Methoden durch das ResultSet navigiert werden:
• next()
• previous()
• absolute(int row)
• relative(int row)
• first()
• last()
46
KAPITEL 4. SQL IN PROGRAMMIERUMGEBUNGEN
import java.sql.*;
import java.lang.*;
public class JDBCExample {
public static void main(String[] args) {
try {
// load Microsoft SQL Server driver
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
}
catch (ClassNotFoundException e) {
System.out.println("Unable to load Driver Class");
return;
}
try {
// connect to database, specifying database, username and password
Connection con = DriverManager.getConnection(
"jdbc:microsoft:sqlserver://grant.inf.ethz.ch:1433","user","pwd");
// create and execute SQL Statement
PreparedStatement pstmt = con.prepareStatement(
"INSERT INTO address (name,year,phone) VALUES (?,?,?)");
// clear old parameters
pstmt.clearParameters();
// set parameters
pstmt.setString(1, "Peter Meier");
pstmt.setInt(2, 1975);
pstmt.setString(3, "01 304 45 67");
// execute the statement
pstmt.executeUpdate();
// release databse resources and close connection
pstmt.close();
con.close();
}
catch (SQLExeption sqle) {
System.out.println("SQL Exception: "+sqle.getMessage());
}
}
}
Abbildung 4.2: PreparedStatement Beispiel einer Datenbankabfrage mit Java
JDBC
4.4. CALL LEVEL INTERFACE
SQL Daten Typ
CHAR
VARCHAR
LONGVARCHAR
NUMERIC
DECIMAL
BIT
TINYINT
SMALLINT
INTEGER
BIGINT
REAL
FLOAT
DOUBLE
BINARY
VARBINARY
LONGVARBINARY
DATE
TIME
TIMESTAMP
CLOB
BLOB
Java Typ
String
String
String
java.math.BigDecimal
java.math.BigDecimal
Boolean (boolean)
Integer (byte)
Integer (short)
Integer (int)
Long (long)
Float (float)
Double (double)
Integer (byte)
byte[]
byte[]
byte[]
java.sql.Date
java.sql.Time
java.sql.Timestamp
java.sql.Clob
java.sql.Blob
47
setXXX Methode
setString(int index, String x)
setString(int index, String x)
setString(int index, String x)
setBigDecimal(int index, java.math.BigDecimal x)
setBigDecimal(int index, java.math.BigDecimal x)
setBoolean(int index, boolean x)
setByte(int index, byte x)
setShort(int index, short x)
setInt(int index, int x)
setLong(int index, long x)
setFloat(int index, float x)
setDouble(int index, double x)
setByte(int index, byte x)
setBytes(int index, byte[] x)
setBytes(int index, byte[] x)
setBytes(int index, byte[] x)
setDate(int index, java.sql.Date x)
setTime(int index, java.sql.Time x)
setDate(int index, java.sql.Timestamp x)
setClob(int i, java.sql.Clob x)
setBlob(int i, java.sql.Blob x)
Tabelle 4.2: SQl Datentypen, Java Typen und setXXX Methoden
NULL Werte
Manchmal enthalten Datenbankkolonnen null Werte. Auf Grund der Art der Implementierung
diverser Datenbank APIs ist es für JDBC unmögliche eine Methode zur Verfügung zu stellen,
die entscheidet ob eine Kolonne null ist oder nicht. Methoden die nicht ein Objekt einer
bestimmten Sorte zurückgeben sind besonders anfällig. Die Methode getInt() gibt z.B. einen
Wert 0 zurück, wenn das Resultat leer ist. Wie soll man jetzt aber entscheiden, ob der Wert
einer Kolonne “0” ist oder eben vielleicht leer (“null”)? JDBC handhabt dieses Problem mit
einer wasNull() Methode, welche angibt, ob von der zuletzt gelesenen Kolonne null gelesen
wurde oder nicht.
Beispiel 4.4:
int numberInStock = res.getInt("STOCK");
if (rs.wasNull())
System.out.println(" Result was null");
else
System.out.println("In Stock: "+ numberInStock);
Als Alternative kann man getObject() verwenden und auf null testen2 .
Beispiel 4.5:
Object numberInStock = res.getObject("STOCK");
if (numberInStock == null)
System.out.println(" Result was null");
2 Einige
Treiber unterstützen dieses Verhalten nicht ganz richtig
48
KAPITEL 4. SQL IN PROGRAMMIERUMGEBUNGEN
Datum und Zeit
JDBC definiert drei Klassen für das Speichern von Datums und Zeit Informationen: java.sql.Date,
java.sql.Time und java.sql.Timestamp. Diese korrespondieren zu den SQL Typen DATE, TIME
und TIMESTAMP. Die java.util.Date Klasse passt zu keiner der SQL Typen, deshalb definiert
JDBC eine neue Menge von wrapper -Klassen, die die Standard Klasse Date erweitern (oder
limitieren).
Der Syntax für Date, Time und Timestamp sind:
java.sql.Date
‘yyyy-mm-dd’
java.sql.Time
‘hh:mm:ss’
java.sql.Timestamp ‘yyyy-mm-dd hh:mm:ss.ms.microseconds.ns’
Zusammenfassung
Die Konzepte von ESQL und JDBC sind sich ähnlich. JDBC ist die state-of-art Datenbank
Programmierung für Java. JDBC mit java.sql 3 und ihre Extension javax.sql 4 unterstützt noch viele weitere Konzepte und Methoden, wie Transaktionen, SQL Fehlerbehandlung,
connection pooling usw., welche hier nicht behandelt werden sollen. Für mehr Infos siehe die
JDBC Dokumentation von Sun [45] oder “Java Enterprise in a Nutshell” [23].
4.5
4.5.1
Applikations Logik auf der Server Seite
Stored Procedures
Bis jetzt gingen wir davon aus, dass das Datenbankapplikationsprogramm auf dem Client ausgeführt wird. Obwohl dies für die meisten Anwendungen angebracht ist, ist es manchmal sinnvoll Datenbankmodule (Prozeduren oder Funktionen) zu kreieren, die im DBMS gespeichert
sind und ausgeführt werden. Man nennt diese Module database stored procedure (obwohl
es sich um Funktionen oder Prozeduren handeln kann). Im SQL Standard wird der Begriff
persistent stored modules (PSM) für stored procedures benutzt, weil diese Programme, wie
die Daten persistent im DBMS gespeichert sind.
Stored procedures sind unter den folgenden Umständen sinnvoll:
• Wenn ein Datenbankprogramm von verschiedenen Applikationen gebraucht wird, kann
man sie auf dem Server speichern und von jeder Applikation ausführen. Das reduziert
den Arbeitsaufwand für duplikaten Code und erhöht die Softwaremodularität.
• Ein Programm serverseitig auszuführen kann den Datentransfer und somit in gewissen Situationen die Kosten für die Datenkommunikation zwischen Client und Server
reduzieren.
• Die Prozeduren können die Macht der Modellierung, wie sie von Views geboten werden,
erweitern, indem sie komplexere Datentypen erlauben und für den Datenbankbenutzer
zur Verfügung stellen. Zudem können komplexere Zwangsbedingungen (constraints)
überprüft werden, als dies mit Trigger möglich ist.
Im Allgemeinen erlauben viele Kommerzielle DBMSs das Schreiben von stored procedures in
allgemeinen Programmiersprachen (general-purpose programming language). Als alternative
können stored procedures aus einfachen SQL Kommandos bestehen.
Verschiedene Hersteller haben ihre eigene Sprache zur Implementation (z.B PL/SQL (Programming Language/SQL) von Oracle, Transact-SQL von MS SQL-Server). Wie bereits erwähnt sind stored procedures in SQL-99 als SQL/PSM standardisiert.
3 Package Dokumentation:
java.sun.com/j2se/1.4.2/docs/api/java/sql/package-summary.html
4 Package Dokumentation:
java.sun.com/j2se/1.4.2/docs/api/javax/sql/package-summary.html
4.5. APPLIKATIONS LOGIK AUF DER SERVER SEITE
49
SQL/PSM
SQL/PSM ist der Teil des SQL Standards, der spezifiziert wie persistent stored modules geschrieben werden. Er beinhaltet Statements zum kreieren von Funktionen und Prozeduren
und zusätzliche Programmierkonstrukte um die Möglichkeiten von SQL so zu erweitern, dass
Programmcode für Prozeduren und Funktionen geschrieben werden können.
Prozeduren werden folgendermassen kreiert (Beispiel 4.6):
CREATE PROCEDURE procedure name ( parameters )
local declaration
procedure body ;
Für Funktion gilt folgender Syntax (Beispiel 4.7):
CREATE FUNCTION procedure name ( parameters )
RETURNS returntype
local declaration
function body ;
SQL/PSM
• IN
• OUT
• INOUT
definiert drei Mode von Parametern:
nur Input
nur Output
Input und Output
IN ist der Default-Mode, wenn nichts deklariert wird. Parameter von Prozeduren können irgendeinen Mode haben, die Parameter von Funktionen aber nur den IN Mode.
Beispiel 4.6:
CREATE PROCEDURE MoveStudio(IN oldAddress VARCHAR(255), IN newAddress VARCHAR(255))
UPDATE Studius
SET address = newAddress
WHERE address = oldAddress;
Beispiel 4.7:
CREATE FUNCTION AverageMovieLengthOfStudio(IN name CHAR(30))
RETURNS INTEGER
DECLARE result INTEGER;
SELECT AVG(length) INTO result
FROM Movies
WHERE studio = name;
RETURN result;
Statements Prozeduraufrufe:
CALL procedure name ( arguments )
Funktionen können nicht aufgerufen werden, sondern werden direkt als Teil einer expression
ausgeführt.
Der RETURN Ausdruck gibt einen Wert zurück, beendet aber nicht die Funktion. Der Rückgabewert kann sich nach dem Return Statement immer noch ändern.
50
KAPITEL 4. SQL IN PROGRAMMIERUMGEBUNGEN
Deklaration lokaler Variablen:
DECLARE name type
Assignments:
SET variable = expression
NULL ist ein zulässiger Wert für eine Zuordnung.
Verzweigung:
IF condition THEN
statements
ELSEIF condition THEN
statements
ELSE
statements
ENDIF
Schleifen:
Basic:
LOOP
statements
END LOOP
Der Loop kann mit
LEAVE label
verlassen werden (label identifiziert dabei den Loop der beendet werden soll).
While:
WHILE condition DO
statements
END WHILE
Repeat:
REPEAT
statements
UNTIL condition
END REPEAT
For:
FOR name AS cursor CURSOr FOR
query
DO
statements
END FOR
Exceptions:
Exceptions werden mit
DECLARE (CONTINUE | EXIT | UNDO) HANDLER FOR conditions
statement
deklariert. Die Kontollfluss Angaben bedeuten folgendes:
CONTINUE springe zum Statement nach dem Statement, dass die Exception
ausgelöst hat
EXIT
verlasse den compound (BEGIN statements END)
UNDO
wie EXIT mache aber alle Änderungen rückgängig
Beispiel 4.8:
4.5. APPLIKATIONS LOGIK AUF DER SERVER SEITE
51
CREATE FUNCTION GetYear(
in name CHAR(30),
)
RETURNS INTEGER
DECLARE NotFound CONDITION FOR SQLSTATE ’02000’;
DECLARE Too Many CONDITION FOR SQLSTATE ’210001;
BEGIN
DECLARE EXIT HANDLER FOR NotFound, TooMany;
RETURN (SELECT year
FROM Movies
WHERE title = name);
END;
Transaktionen Im generic SQL Interface ist ein Statement gleich einer Transaktion.
In programmierbarem SQL machen mehrere Statements eine Transaktion, die von einer Applikation initiert werden kann. Transaktion können committed oder aborted werden. Um eine
Transaktion zu initieren benutzt man
START TRANSACTION
Um eine Transaktion zu beenden, benutzt man entweder
COMMIT
wenn man die Transaktion bestätigen will, oder
ROLLBACK
um die ganze Transaktion bei einem Fehler rückgängig zu machen.
Transaktion können verschiedene Zugriffsmode haben
SET TRANSACTION READ ONLY
SET TRANSACTION READ WRITE
Verschiedene read-only Transaktionen können dabei parallel laufen.
Für den Zugriff auf Daten definiert SQL vier Isolationslevel für Transaktionen:
read uncommitted dirty reads sind möglich
read committed
read-only committed values
repeatable read
jedes lesen gibt den selben Wert
serializable
enspricht einer seriellen Ausführung
52
KAPITEL 4. SQL IN PROGRAMMIERUMGEBUNGEN
Kapitel 5
Integrierung von
Datenbanken und
Programmierung
5.1
Motivation
Für einige Applikationssyteme brauchen wir fortgeschrittene Datenbanksyteme mit Anforderungen die herkömmliche RDBMS (relationale Datenbank Management Systeme) nur schwer
oder gar nicht erfüllen können. Beispiele sind:
• Computer-Aided Design (CAD): Daten im Zusammenhang mit mechanischem und
elektrischem Design haben eine grosse Anzahl verschiedener Typen die meist nur wenige Instanzen besitzen. Ein Design kann sehr gross sein und aus Tausende von Teilen
und voneinander abhängigen Teildesigns zusammengesetzt sein. Zudem entwickelt sich
ein Design im Verlauf der Zeit und die Änderungen müssen verbreitet werden. Für
die einzelnen Komponenten eines Designs müssen evtl. noch Alternativen in Betracht
gezogen werden. Vielleicht arbeiten hunderte von Mitarbeitern am selben Design und
die gleichzeitige gemeinsame Arbeit an einem Design sollte von einer Applikation unterstützt werden.
• Computer-Aided Manufacturing (CAM): Die Daten sind ähnlich zu den Daten im
CAD. Dazu kommen noch Daten die im Zusammenhang mit der Produktion stehen.
Applikationen zeigen den aktuellen Stand des Systems in der laufenden Produktion
an. Andere Applikationen kontrollieren physikalische Prozesse und müssen in realtime
reagiere. Die Applikationen können in einer Hierarchie organisiert und spezialisierte
Regeln können mit Standardalgorithmen kombiniert sein.
• Computer-Aided Software Design: Daten, die im Zusammenhang mit dem Softwareentwicklungs Zyklus stehen sind gespeichert (z.B. Planung, Anforderungen, Implementierung, Test, Dokumentation). Auch hier kann das Design extrem gross sein und wie
bei anderen Designs muss die gemeinsame Arbeit mehrerer Benutzer am selben Projekt unterstützt sein. Die Abhängigkeiten zwischen verschieden Komponenten sollten
festgehalten werden um bei Änderungen zu unterstützen.
Die typischen Merkmale solcher Applikationen sind:
• Verwaltung von grossen, komplexen Objekten
• Unterstützung von Varianten, z.B. können für einen Knoten in einem Produktebaum
mehrere Varianten möglich sein
53
54
KAPITEL 5. DATENBANKEN UND PROGRAMMIERUNG
• komplexe Abhängigkeiten und Zwangsbedingungen, z.B. limitiert die Wahl einer Variante A in der Stufe k der Produktehierarchie die Auswahl in der Stufe n
• Unterstützung von Design Prozessen wie z.B. Kooperation unter Teams, Versionen und
lange Transaktionen
Herkömmliche RDBMS weisen einige Schwächen auf:
• Objekte der “realen” Welt können nur schlecht repräsentiert werden.
• Relationen sind ein semantisch stark überladenes Konstrukt, alle unterschiedlichen Arten von Informationen (Datenobjekte, Abhängigkeiten, Spezialisierungen usw.) werden
auf die selbe Art modelliert.
• Relationen sind für homogene Daten designt, d.h. es wird angenommen, dass alle Daten
die selbe Struktur haben.
• Rekursive Queries bereiten Schwierigkeiten.
• Der impedance mismatch macht das Mischen von Datenmodellen und Programmierparadigmen schwierig.
Traditionelle Programmiersprachen bieten die Möglichkeit Daten zu manipulieren, deren Lebenszeit die Aktivität des Programms nicht überdauern. Wenn Daten die Aktivierungszeit
eines Programms überdauern müssen wird ein File I/O- oder DBMS- Interface benutzt, d.h.
Daten sind entweder short term Daten und werden mit den Möglichkeiten die die Programmiersprache bietet manipuliert oder sie sind long term Daten und werden von einem Filesystem oder DBMS gehandhabt. Das mapping zwischen den zwei Typen findet normalerweise
einerseits im Filesystem oder DMBS statt und anderseits mit explizitem Übersetzungscode
im Programm.
Das es zwei verschiedene Arten gibt Daten zu betrachten hat einige Nachteile. Erstens ist in
einem Programm ein beachtlicher Anteil von Code (typischerweise 30% [25]) für den Transfer
von Daten von/zu Dateien in DBMS. Viel Platz und Zeit wird zudem benötigt um Daten vom
Programmformat in das Format für die Langzeitspeicherung zu übersetzen. Der zweite grosse
Nachteil ist, dass der Datentypenschutz, welcher die Programmiersprache bietet, oft durch das
Mapping verloren geht.
5.2
Persistence in Programmiersprachen
Für Applikationen wie die oben erwähnten, muss man die Programme mit den Eigenschaften
von DBMS, wie Langlebigkeit der Daten ausstatten und auf grosse, umfangreiche Daten und
Code konkurrierend zugreifen können. Das Ziel von persistent programming ist es die Konstruktion solcher Applikationen, oft auch Persistent Applications Systems (PASs) genannt1 ,
zu unterstützen [11].
Die Technologie die dem Bau von PASs zugrunde liegt, baut auf ganz ganz unterschiedlichen
Mechanismen und philosophischen Annahmen auf. dazu gehören Betriebssysteme, Kommunikationssysteme, Datenbanksysteme, Benutzerinterface-Systeme, Kommandosprachen, Editoren, Dateisysteme, Compiler usw. Programmierer müssen die Variationen, wie verschiedene
Typen, Bindingschema, Naming, verstehen und damit umgehen können.
Die Datenbank und Programmiersprachen Gemeinschaften forschten und entwickelten unabhängig von einander Produkte, obwohl sie viele ähnliche Service anbieten müssen. Der Fokus
der DB Gemeinschaft ist dabei auf die verlässliche Speicherung von grossen Datenmengen
und die Unterstützung von effizienten Operationen auf diesen Daten gerichtet. Der Fokus
der Programmiersprachen Gemeinschaft ist mehr darauf gerichtet dem Programmierer zu helfen präzisen verständlichen Code zu schreiben. Intellektuelle und softwaremässige Investitionen beider Gemeinschaften gehen gegen die Adaption von Ideen der anderen Seite. Separate
Entwicklungen und konsequente Inkonsistenzen tendieren dazu sich festzusetzen und anzuwachsen. Es gibt aber auch Ideen und Ansätze die beiden zusammenzubringen, was uns zum
persistent programming führt.
1 heute
stösst man vermehrt auf den Begriff enterprise applications für solche Applikationen
5.2. PERSISTENCE IN PROGRAMMIERSPRACHEN
55
Wir definiere persistence als eine Eigenschaft von Daten, welche die Zeitperiode angibt in welcher Daten existieren und benutzbar sind. Man das Spektrum von persistence folgendermassen
kategorisieren:
1. flüchtige (transient) Resultate in Berechnungen (expression evaluation).
2. lokale Variablen in Prozedur Aktivierungen.
3. Globale Variablen und Heapeinträge die auch ausserhalb ihrer Scope bestehen.
4. Daten welche für die ganze Programmdauer gelten.
5. Daten die über mehrere Ausführungen von mehreren Programmen gelten.
6. Daten die solange gelten, wie ein Programm benutzt wird
7. Daten die verschiedene Versionen eines Programms überdauern
8. Daten die verschiedene Versionen des Langzeitspeichersystems (persistent support system) überdauern.
Die ersten vier Kategorien werden normalerweise von Programmiersprachen unterstützt, die
anderen vier von File- und DBM Systemen.
5.2.1
Design Methoden
M. P. Atkinson et al. stellen in [7] und [8], für einen ersten Versuch ein System zu produzieren
welches persistence unterstützt, die Hypothese auf, dass es möglich sein muss Langlebigkeit
mit einem Minimum an Änderungen an einer Sprache in die Sprache zu integrieren. Sie verwendeten dazu die Sprache S-algol (eine höhere Programmiersprache die an der University
of St Andrews für Lehrzwecke entwickelt wurde). Zusätzlich zu der Hypothese erkannten sie
noch einige Prinzipien für langlebige (persistent) Daten:
1. persitence independence: Die Persistence von Datenobjekten ist Unabhängig davon wie
das Programm das Datenobjekt manipuliert.
2. Orthogonality: Allen Daten soll die volle Bandbreite an Persistence erlaubt sein und
alle Daten werden unabhängig von Langlebigkeit, Grösse und Type uniform behandelt.
3. Identification: Die Wahl wie man persistence auf Sprachlevel bietet und identifiziert ist
unabhängig von der Wahl der Daten Objekte in der Sprache.
Es wurden verschiedene Methoden untersucht um Persistence von Daten zu identifizieren. Einige assoziieren Persitence mit den Variablennamen, andere mit dem Typ in der Deklaration.
Unter dem Gesichtspunkt des Prinzips der Unabhängigkeit persitence independence sind diese
Methoden nicht erlaubt. Die Wahl der Datenelemente welche die Lebenszeit eines Programms
überdauern sollen ist ein nächster wichtiger Punkt. Viele moderne Sprachen haben Garbage
Collection und die Erreichbarkeit von Objekten, wie in Garbage Collection, ist eine sinnvolle
Wahl um persistent Objekte zu identifizieren.
5.2.2
Persistent Languages
Es wurden verschiedene Persistent Languages entwickelt:
• PS-algol (Atkinson et al.): Die Initial-Version wurde 1983 veröffentlicht [7][8]. PS-algol
identifiziert Objekte mit der Erreichbarkeit, wie oben beschrieben. PS-Algol Beispiele
zeigen Abbildung 5.1 und 5.2. Spätere Versionen erlaubten die Referenzierung von Prozeduren in Structures und machte sie so persistent. Es wurden einige Applikationen in
PS-Algol programmiert: RAQUEL [32][19], eine relationale Algebra Sprache für Querying von relationales Datenbanken [17]; EFDM (Extended Functional Data Model ist eine
Implementation des Funktionalen Daten Modells (FDM) wie in [42] beschrieben [17];
PROTEUS Knoten eines heterogenen verteilten Datenbanksystems.
• Napier88 (Atkinson, Morris) [38]
• PJama (Atkinson, Jordan) [12][26][27][47]: Persistent Java siehe Abschnitt 5.2.3
56
KAPITEL 5. DATENBANKEN UND PROGRAMMIERUNG
structure person(string name, phone.no; pntr addr, other)
structure address (int no; string street, town; pntr next.addr)
let db = open.database("Address.list","mypass","write")
if db is error.record then write "Can’t open database" else
begin
write "Name:"
; let this.name = read.a.line
write "Phone number:" ; let this.phone = read.a.line
write "House number:" ; let this.house ) readi
write "Street:"
; let this.street = read.a.line
write "Town:"
; let this.toen = read.a.line
let p = person(this.name, this.phone,
address(this.house, this.street, this.town, nil), nil)
let addr.list = s.lookup("addr.list.by.name", db)
s.enter(this.name, addr.list, p)
commit
end
Abbildung 5.1: Ein PS-algol Programm um eine neue Person einer Adressliste
in einer Datenbank hinzuzufügen
• E (Carey) [41]: Effektiv ein persistent C++, das als Teil des Exodus Projekts an der
University of Wisconsin entwickelt wurde. Das Projekt führte mit der Entwicklung
eines Toolkit für die Entwicklung von DBMS das Konzept des “database engineer”
ein. E führte persistence durch spezielle Datenbanktypen ein. Z.B. sind int und dint
Basistypen. Benutzerdefinierte Typen werden als persistent types deklariert, wenn die
Instanz persistence Möglichkeiten hat.
• Galileo (Albano) [4][5]
• Poly (Matthews) [35]2
• Amber (Cardelli)
• BOYAZ (Zamulin)
• Pascal/R (Schmidt): Beispiel einer Sprache die versucht eine allgemeine Programmiersprache so konsistent wie möglich mit einem Datenmodell zu kombinieren. Das
Relationale Modell ist in Pascal durch die Identifikation von Tuples und Records einem
Datenbank-Konstruktor und der Einführung von einem “Relation of” als Konstruktor
ähnlich zum “Set of” integriert. Ein Pascal/R Beispiel zeigt Abbildung 5.3. Pascal/R
kann mit verschiedenen Datenbanken des selben Datenbanktyp laufen. Relationen können Argumente von Prozeduren sein. Relational-Operatoren und ein “for each” Konstruktor um durch die Relationen zu iterieren3 wurden eingeführt.
• Modula-R (Schmidt): Nachfolger von Pascal/R von Schmidt.
• DBPL (Schmidt): Die Weiterführung von Pascal/R und Modula-R.
• Tycoon/Tycoon 2 (Matthes, Schmidt) [33][34][36]: Neuste Sprache aus der Gruppe
um Schmidt.
• Persistent Prolog
• Persistent Oberon
• ......
2 Weitere
Dokumente unter http://www.lfcs.inf.ed.ac.uk/reports/95/ECS-LFCS-95335/index.html
3 ist für andere Strukturen wie files, array nicht verfügbar
5.2. PERSISTENCE IN PROGRAMMIERSPRACHEN
57
structure person(string name, phone.no; pntr addr, other)
let db = open.database("Address.list","myotherpass","read")
if db is error.record do {write "Can’t open database"; abort}
let addr.list = s.lookup("addr.list.by.name", db)
write "Name:"; let this.name = read.a.line
let this.person 0 s.lookup(this.name, addr.list)
if this.person = nil then write "Person not known" else
write "Phone number is:"; this.person(phone.no)
Abbildung 5.2: Ein PS-algol Programm das nach einer Telefonnummer einer
Person in der Adressliste sucht
{records destined to become tuple types}
PartRec = record
Pno: Pnum;
Name: string
end;
{relation types}
PartRel = relation <Pno> of PartRec;
{database type}
PartsDB = database
Part: PartRel;
....
end;
Abbildung 5.3: Pascal/R Beispiel
58
KAPITEL 5. DATENBANKEN UND PROGRAMMIERUNG
5.2.3
Persistence in Java
Wie wir bereits in Abschnitt 4.4.1 gesehen haben, kann man in Java mittels JDBC relativ konfortabel auf Datenbanken zugreifen. Wir haben aber ein grosses Impedance Mismatch
(Abschnitt 4.2) Problem.
Java bietet dem Programmierer ein einfaches aber mächtiges Object Model, ein strenges Typensystem (Strong Typing), automatisches Speicher Management und unterstützt Concurrency.
Leider können aber diese Eigenschaften ausserhalb der einzelnen Ausführung der Java Virtual
Machine nicht gehandhabt werden und Programmierer müssen sich explizit um die Speicherung des Applikationsstatus kümmern, z.B. mit Files Input/Outputs oder mit Datenbankverbindungen. Java bietet dabei mit Java Object Serialization (JOS) die Möglichkeit auch Java
Objekte als bitestream in einen beliebigen Stream zu schreiben, also z.b. um Objekte in/von
Files zuschreiben/lesen oder über eine Netzwerkverbindung zu senden/empfangen. JOS ist
eine Art “lightweight persistence” und eigentlich der Standard persistence Mechanismus für
die Java Plattform, er wird zum Beispiel im JavaBeansTM Framework intensiv genutzt. In
JOS werden mit den Daten auch Typinformationen gespeichert, es muss der ganze Objektgraph gelesen und gleichzeitig geschrieben werden. Ein Problem das bei der Spezialisierung
von Objekten beachtet werden muss, ist dass die Objektidentität nicht erhalten werden. Wird
ein Objekt wieder zurück geladen, wird ein neues Objekt mit dem selben Typ und Status wie
das Original kreiert. Werden also z.B. zwei Objekte serialisiert, die beide auf ein gemeinsames Objekt referenzieren, wird für beide Objekte das referenzierte Objekt serialisiert, beim
Wiederherstellen referenzieren die beiden Objekte also nicht mehr auf das selbe sondern nur
ein identisches Objekt. Ein weiterer Nachteil ist die Langsamkeit bei einer grossen Menge von
Daten. Ausserdem ist es nicht zuverlässig, wenn das System während dem lesen oder schreiben crasht, ist das File verloren. Abbildung 5.4 zeigt ein Beispielcode der Spezialisierung zum
Abspeichern eines Objekts in ein File.
PJama
Sich den Limitationen der traditionellen Methoden persitence zu erreichen bewusst, imitierte Sun Microsystems Laboratories in Zusammenarbeit mit der Persistence and Distribution
Group um Malcolm Atkinson der Glasgow University das Forest Projekt um Java das Prinzip
der orthogonal persistence [11] hinzuzufügen. Das Projekt PJama4 begann kurz nach dem
ersten öffentlichen Release der Java Plattform im Mai 1995. PJama ist eine Prototyp Implementierung einer persistent Programmierumgebung für Java, welche orthogonal persistence
bietet.
Wie bereits im Abschnitt 5.2.1 erwähnt, ist orthogonal Persitence durch drei Prinzipien definiert:
• Type Orthogonality : alle Daten unabhängig vom Typ können persistence gemacht
werden.
• Transitive Persistence: die Lebensdauer aller Objekte wird durch die Erreichbarkeit
von einer bestimmten Menge von Root Objekten aus ermittelt.
• Persistence Independence: es ist ununterscheidbar, ob ein Code auf transient oder
persistent Daten operiert.
PJama Applikationen werden mit einem store assoziiert, welcher dem Programmierer als eine
Instanz der PJavaStore Klasse repräsentiert wird. Eine Applikation wird initiiert indem man
den PJama Interpreter mit einem Store und einer aufzurufenden Klasse verbindet. Objekte
kommen als Kandidaten für persitence in Frage, wenn sie direkt oder indirekt von explizit
beim PJavaStore registrierten Root-Objekten erreichbar sind. Standardmässig werden beim
erfolgreichen Beenden einer Applikation alle erreichbaren Objekte atomically und dauerhaft
im Store gespeichert (updated). Applikationen die nicht regulär beendet werden, sondern eine
exception werfen ändern nichts am stabilen store. Es ist möglich PJavaStore.stabilizeAll
aufzurufen um explizit eine globale Stabilisierung hervorzurufen.
Ein Beispiel wie mit PJama persistent Daten kreiert werden können zeigt Abbildung 5.5.
Abbildung 5.6 zeigt einen Beispielcode, wie auf persistent objects zugegriffen werden kann.
4 Das Projekt hiess ursprünglich “PJava”, dieser Ausdruck ist aber heute für Personal
JavaTM reserviert.
5.2. PERSISTENCE IN PROGRAMMIERSPRACHEN
59
include java.io.*;
\\ Serializable class
public class Person implements Serializable{
....
}
public void savePerson(Person p, File f){
try{
// Serialize the Object (Person)
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));
oos.writeObject(p);
oos.close();
}
catch (IOException e) { /* Handle input/ouput exception */}
}
public Person getPerson(File f){
try{
// read the Object (Person) from file
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));
Object temp = ois.readObject();
ois.close();
return (Person)temp;
}
catch (IOException e) { /* Handle input/ouput exception */}
catch (ClassNotFoundException cnfe) {/* readObject() cann throw this */}
}
Abbildung 5.4: Beispiel: Serialisierung von Objekten zur Speicherung in ein File
60
KAPITEL 5. DATENBANKEN UND PROGRAMMIERUNG
public class Department{
....
public static void main(String[] args){
// PJama implicity starts a transaction when
// methode main is activated
Cource c = new Course("OODB");
Person p = new Person("Fred");
try{
// obtain a persistent store
PJavaStore pjs = PJavaStore.getStore();
// make a persistent root
pjs.newRott("OODB",c);
} catch (PJSException e){/*handle store exception */}
} // implicit commit
}
Abbildung 5.5: Kreieren einer persisten Datenstruktur mit PJama
....
try{
PJavaStore pjs = PJavaStore.getStore();
Hastable courses = (Hashtable)pjs.getPRoot("courses");
} catch (PJSException e){/*handle store exception */}
....
Course oodb = (Course) courses.get("Object Oriented Databases");
oodb.display();
....
Abbildung 5.6: Zugriff auf ein persistent object mit PJama
Für die Typensicherheit werden die Klasseninformation der persistent Objekte im store persistent gespeichert. Von den benannten Root-Objekten wird auf die Objekte direkt oder indirekt
zugegriffen und dann wird das Matching des erwarteten Typs auf den aktuellen Typ vorgenommen.
PJama wurde ohne eine Änderung an der Java Sprache, den Java Klassen (core classes) oder
am Java Compiler implementiert. Der persistent Mechanismus wird durch eine zusätzliche
API, die hauptsächliche Methoden der PJavaStore Klasse enthält, angeboten. Ein für diesen
Zweck gebauter store behält Java Objekte auf der Disk und managend space allocation, recovery und transactional updates. Die automatische Object-Fehlerbehandlung (im Falle eines
Crash bevor die Applikation beendet ist), die “Persitence Ernennung” und wiederherstellbaren, transaktionale Operationen werden durch Modifizierung der Java virtual machine (JVM)
erreicht. Die Hauptänderung besteht im hinzufügen eines Objekt Cache, der für den Rest der
VM ziemlich ähnlich wie der (garbage collected) Heap der JVM aussieht.
Neben der globalen Stabilisierung, ermöglicht PJama, verschiedene Transaktionsmodelle als
Spezialisierung der TransactionShell Klasse.
5.2.4
ObjectStore OODBMS
Die ObjectStore Produkte Familie5 umfasst die Produkte ObjectStore Personal Storage edition (PSE) Pro6 für grosse Singleuser Datenbanken und ObjectStore7 für High-Performance
5 www.objectstore.com
6 www.objectstore.com/products/pse
pro/index.ssp
7 www.objectstore.com/products/objectstore/index.ssp
5.2. PERSISTENCE IN PROGRAMMIERSPRACHEN
61
Multiuser Datenbanken.
ObjectStore PSE Pro für Java ist eine reine Java-basierte Objekt Datenbank für embedded
database und mobile computing Applikationen [40]. PSE Pro unterstützt Queries und Indexing, sowie einen persistent Garbage Collector. PSE Pro benutzt die Einfachheit der Objekt
Serialisation und die Zuverlässigkeit von DBMS zusammen mit einer in-memory-like Performance für den Zugriff auf persistent Objekte.
Um persisten Objekte zu speichern und auf sie zuzugreifen müssen alle ObjectStore Applikationen folgende Grundoperationen implementieren:
• Kreieren und join eine Session.
Signatur:public static Session create(String host, Java.util.Properties properties );
Session session=Session.create(null,null); session.join();
• Kreiere oder öffne eine Datenbank.
db=Database.create(dbName, ObjectStore.ALL_READ);,
db=Database.open(sbName, ObjectStore.UPDATE);
• Starte und commit (oder abort) eine Transaktion.
Transaction tr=Transaction.begin(ObjectStore.READONLY);,
tr.commit();, tr.abort();
• Kreiere oder hole eine Datenbank root. Persitence wird durch reachability erreicht.
db.createRoot(ällUsers", allUsers=new OSHashMap());,
allUsers = (Map) db.getRoot(ällUsers");
• Beende eine Session. session.terminate()
Zusätzlich müssen noch die Klassen die man in der Datenbank speichern will modifizieren (oder
annotate) damit sie persistent capable (oder persistent aware für Klassen die auf persistent
Objekte Zugreifen müssen). Man macht dies indem man nach dem Kompilieren des Source
Code einen Postprocessor über die Klassen laufen lässt: osjcfp -dest . -inplace *.class
gibt die annotierten class-Files. Die CLASSPATH Umgebungsvariable muss dann noch angepasst
werden, damit sie auf die annotierten class-Files zeigt.
Persistent Capable Classes
• java.lang.String
• Wrapper Klassen vom java.lang Packet : Boolean, Byte, Character, Double, Float,
Integer, Long, Short
• Arrays von primitive types (integer,boolean,. . . ) und von jedem persistent fähigen
Type.
• Von der Applikation definierte persistent capable classes
• Spezielle ObjectStore collections
62
KAPITEL 5. DATENBANKEN UND PROGRAMMIERUNG
Kapitel 6
Objekt-Orientierte
Datenbanken
Traditionelle Datenmodelle und System, wie das relationale und hierarchische haben gewisse
Mängel, wenn es darum geht komplexere Datenbank Applikation zu designen und implementieren, z.B. Datenbanken für engineering design und manufacturing (CAD/CAM und
CIM1 , Geographische Informationssysteme (GIS) (siehe ??) und Multimedia. Diese neueren Applikationen haben Anforderungen und Charakteristiken die sich von denen traditioneller Business Applikationen unterscheiden, weil z.B. komplexere Strukturen für Objekte,
länger andauernde Transaktionen, neue Datentypen für das Speichern von Bildern oder grossen Textelementen und die Notwendigkeit für applikations-spezifische nicht-standard Operationen. Object-orientierte Datenbanken Systeme (OODBMS) wurden vorgeschlagen um den
Bedürfnisse dieser komplexeren Applikationen gerecht zu werden. Die Objekt-Orientierte Vorgehensweise offeriert die Flexibilität mit einigen dieser Anforderungen umzugehen ohne auf
die in traditionellen DBMS vorhandene Datentypen und Query Sprachen begrenzt zu sein.
Ein Hauptmerkmal von Objekt-Orientierten Datenbanken ist die Möglichkeiten die dem Entwickler gegeben wird, beides, die Struktur von komplexen Objekten und die Operationen die
auf diesen Objekten ausgeführt werden können zu spezifizieren.
Ein anderer Grund für die Entwicklung von Objekt-Orientierten Datenbanken besteht in der
stark zu genommen Nutzung von Objekt-Orientierten Programmiersprachen für die Entwicklung von Applikationen. Datenbanken werden heute fundamentalen Komponenten in vielen
Software Systemen und traditionelle Datenbanken waren mit Objekt-orientierten Programmiersprachen wie C++, SMALLTALK oder JAVA schwierig zu nutzen (siehe Kapitel 4).
Objekt-Orientierte Datenbanken sind so designt, dass sie direkt, oder nahtlos, mit Software
integriert werden können, die mit Objekt-Orientierten Programmiersprachen entwickelt wurden.
Obwohl viele experimentelle Prototypen und kommerzielle OODBMS kreiert wurden, fanden
sie bis jetzt noch nicht weite Verbreitung, wohl wegen der grossen Popularität der traditionellen relationalen oder objekt-relationalen2 Datenbank Systemen.
6.1
ODMG
Als langsam kommerzielle OODBMSs erhältlich wurden, erkannte man schnell die Notwendigkeit für ein Standardmodell und eine Standardsprache um eine grösste mögliche Portability
1 Computer-Aided
Design/Computer-Aided Manufacturing und Computer-Integrated Manufacturing
2 auf die Mischform in der viele objekt-orientierte Eigenschaften in traditionelle relationale
DBMS integriert wurden, wird in diesem Skript nicht eingegangen. Die meisten Hersteller von
relationalen DBMS erweiterten ihre Systeme zu objekt-relationalen Systemen
63
64
KAPITEL 6. OBJEKT-ORIENTIERTE DATENBANKEN
und Interoperability für OODBM Systeme zu bieten. Portability ist generell, als die Möglichkeit ein bestimmtes Applikations Programm auf verschiedenen Systemen mit minimaler Modifikation des Programms selbst auszuführen, definiert. Das langjährige Fehlen eines Standard
für OODBMSs hat wahrscheinliche viele potentielle Benutzer davon abgehalten auf diese neue
Technologie umzusteigen. Ein Konsortium von OODBMS Herstellern und Nutzern, ODMG3
genannt, arbeitete deshalb einen Standard aus der als ODMG-93 (oder ODMG 1.0) Standard
bekannt ist4 . Dieser wurde später zum ODMG 2.0 Standard überarbeitet.
Der Standard besteht aus verschieden Teilen:
• Dem Object Model (Abschnitt 6.1.1)
• Der Object Definition Language (ODL) (Abschnitt 6.1.2)
• Der Object Query Language (OQL) (Abschnitt 6.1.3)
• und den Bindings zu objekt-orientierten Programmiersprachen (C++, SMALLTALK,
JAVA)
6.1.1
ODMG Object Model
Das ODMG Objekt Modell ist das Datenmodell auf dem die ODL und die OQL basieren.
ODMG benutzt das OMG Objekt Modell als Basis für das ODMG Objekt Modell. Dieses
Model bietet die Datentypen, Typen Konstruktors und andere Konzepte die in ODL benutzt
werden können um ein Datenbankschema zu spezifizieren.
Objects und Literals
Objects und Literals sind die Basisbausteine des Objekt Modells. Der Hauptunterschied zwischen den zwei besteht darin, dass Objekte einen Object Identifier und einen State (oder
aktueller Wert, Value) besitzen, während Literals nur ein Value aber keinen Object Identifier
haben. In beiden Fällen kann der Value eine komplexe Struktur haben. Ein Literal ist im
Grunde genommen ein konstanter Value, möglicherweise mit einer komplexen Struktur, der
sich nicht ändert.
Ein Objekt wird durch vier Charakteristiken beschrieben:
• Object Identifier : Ein eindeutiger systemweiter Identifier (oder OBJECT_ID.
• Name: Zusätzlich zur OBJECT_ID kann man Objekten optional einen innerhalb einer
Datenbank eindeutigen Namen geben. Meist haben nicht alle Objekte einen Namen
sondern hauptsächlich die welche Kollektionen von Objekten eines bestimmten Objekttypen enthalten. Diese Namen werden als entry points zur Datenbank benutzt.
• lifetime: Die Lifetime eines Objekts spezifiziert ob ein Objekt ein persistent Object
oder ein transient Object ist.
• Structur : Die Struktur eines Objekts spezifiziert durch die Benutzung eines Konstruktors wie ein Objekt konstruiert wird. Sie spezifiziert ob ein Objekt atomic 5 oder collection objects sind.
Es besteht ein grosser Unterschied zwischen atomic objects und atomic Literals. Im ODMG
Model ist ein atomic object jedes Objekt, das nicht eine Collection ist.
Im ODMG Model ist ein Literals ein Value der keinen Object Identifier hat. Trotzdem kann
der Value eine einfache oder komplexe Struktur haben. Es gibt drei Arten von Literals:
• atomic: Atomic Literals korrespondieren zu den Values von Basistypen und sind vordefiniert. Die Basistypen des Objekt Modells umfasst in ODL Notation unter anderen:
– Integer Zahlentypen: long, short, unsigned long, unsigned short
3 Object
Database Management Group (www.odmg.org)
Standard wurde 1991 von Rick Catell von SunSoft initiiert und umfasst am Anfang 5
Leute der OODBMS Haupthändler
5 Im ODMG Model, korrespondieren atomic objects nicht zu Objekten deren Values Basistypen sind. Alle Basistypen (integer, reals, usw.) sind im ODMG Model Literals
4 der
6.1. ODMG
65
– Floating-Point Zahlen: float, double
– boolean
– Zeichen und Zeichenketten: char, string
– Aufzählungstyp: enum
• structured : Structured Literals komplexe Typen ähnlich den struct in Programmiersprachen (z.B. C). Als eingebaute Strukturen gibts:
– date, interval, time und timestamp
In ODL werden Structs mit dem Keyword Struct kreiert.
• collection: Collection Literals spezifizieren ein Value das eine Kollektion von Objekten
oder Values ist, aber selber keine OBJECT_ID haben. Die Collections im Objekt Model
sind:
– set<t>: Eine ungeordnete Menge von Objekten vom Typ t (unikate)
– bag<t>: Eine ungeordnete Menge von Objekten vom Typ t (duplikate erlaubt)
– list<t>: Eine Liste von Objekten mit Typ t in der die Reihenfolge wichtig ist
(geordnet).
– array<t>: Ähnlich einer Liste, nur hat ein Array eine fixe Länge.
– dictionary<k,v>: Erlaubt die Kreation einer Kollektion von assoziierten Paaren
in denen alle k (key) Values einzigartig (unique) sind.
Atomic Objects werden in ODL mit dem Keyword class spezifiziert. Ein benutzerdefiniertes
Objekt wird mit class definiert indem man die Eigenschaften (Properties) und die Operationen (Methods) spezifiziert. Die Properties definieren den State eines Objekts und werden
weiter in attributes und relationships unterteilt. Ein Beispiel für zwei Objektdefinition
Employee und Department zeigt Abbildung ??.
Interfaces, Classes und Inheritance
Im ODMG Objekt Model existieren zwei Konzepte um Objekttypen zu spezifizieren: Interfaces und Classes. Dazu gibt es auch zwei Typen von Inheritance Beziehungen. Der ODMG
Terminologie folgend wird im folgenden das Wort Behavior für Operationen und das Wort
State für die Eigenschaften (Properties, Attribute und Relationships) benutzt.
Ein interface ist die Spezifikation des abstrakten Behavior eines Objekttypen. Obwohl ein
Interface state Eigenschaften haben kann, können diese nicht vom Interface vererbt werden.
Ausserdem ist ein Interface nicht instantiierbar (noninstantiable).
Eine class spezifiziert sowohl das abstrakte Behavior als auch den abstrakten State eines Objekts und ist instantiierbar.
Da Interfaces nicht instantiierbar sind, werden sie hauptsächlich benutzt um abstrakte Operationen die von classes oder Interfaces geerbt werden können zu spezifizieren. Man nennt
dies Behavior Inheritance und es wird durch das Symbol “:” spezifiziert. Ein Beispiel zeigt
Abbildung 6.2.
Eine andere Inheritance Beziehung EXTENDS genannt und mit dem Keyword extends spezifiziert wird benutzt um sowohl Behavior als auch State unter Klassen zu vererben. In einem
EXTENDS müssen sowohl der Supertyp als auch der Subtyp eine class sein (siehe Abbildung 6.2).
Extents, Keys
Im ODMG Objekt Modell kann ein Datenbank Designer ein extent für jeden Objekttypen
der mit einer class Deklaration definiert ist deklarieren. Dem Extent wird ein Namen gegeben und er wird alle persistent Objekte dieser Klasse enthalten, d.h. er ist ein Set Objekt der
alle persistent Objekte der Klasse enthält. Es ist zu Bemerken, dass in Programmiersprachen
normalerweise keine extent Konzept vorkommt, man hat Typen, welche die Menge aller möglichen Werte definieren und Variablen welche zu individuellen Values gebunden sind. Manchmal
wird der Begriff “activ domain” für extents benutzt.
Eine Klasse mit einem Extent kann einen oder mehr keys haben. Ein key besteht aus einem oder mehr Properties, deren Values für jedes Objekt im extent einzigartig (unique) sein
müssen.
66
KAPITEL 6. OBJEKT-ORIENTIERTE DATENBANKEN
class Employee
( extent all_employees
key ssn )
{
attribute
string
name;
attribute
string
ssn;
attribute
date
birthdate;
attribute
enum Gender{M, F} sex;
attribute
short
age;
relationship Department
works_for
inverse Department::has_emps;
void
reassign_emp(in string new_dname)
raises(dname_not_valid);
};
class Department
( extent all_departments
key dname. dnumber )
{
attribute
string
dname;
attribute
string
sdnumber;
attribute
struct Dept_Mgr{Employee manager, date startdate}
mgr;
attribute
set<string>
locations;
attribute
struct Projs{string projname, time weekly_hours}
projs;
relationship set<Employee>
has_emps inverse Employee::works_for;
void
add_emp(in string new_ename) raises(ename_not_valid);
void
change_manager(in string new_mgr_name; in date startdate);
};
Abbildung 6.1: Attribute, Relationships und Operationen in Klassendefinitionen
6.1. ODMG
67
interface HumanBeeing{
short
age();
}
class Person:HumanBeeing
( extent persons
key
ssn )
{
attribute struct Pname{string fname, string mname, string lname}
name;
attribute string
ssn;
attribute date
birthdate;
attribute enum Gender{M, F} sex;
attribute struct Address
{short no, string street, short aptno, string city, short plz}
address;
};
class Student extends Person
( extent students )
{
attribute string
class;
relationship Department majors_in inverse Department::has_majors;
relationship set<Grade> completed_sections inverse Grade::student;
void change_major(in string dname) raises(dname_not_valid);
};
class Grade
( extent grades )
{
attribute
enum GradeValues{1,1.5,2,2.5,3,3.5,4,4.5,5,5.5,6}
grade;
relationship Student student inverse Student::completed_sections;
};
class Department
( extent departments key dname )
{
attribute
string dname;
attribute
string dphone;
relationship set<Student> has_majors inverse Student::majors_in;
};
Abbildung 6.2: Beispiel eines kleinen ODL Schema mit den zwei verschiedenen
Inheritance Beziehungen. Die graphische ODMG Notation dazu zeigt Abbildung 6.3
68
KAPITEL 6. OBJEKT-ORIENTIERTE DATENBANKEN
Interface
HumanBeeing
HumanBeeing
Class
Person
Person
Interface (is−a) Inheritence (using ":")
has_majors
Department
major_in
Class Inheritance (using extends)
Student
completed_sections
section
1:1
Grade
1:N
M:N
Relationships
Abbildung 6.3: Eine Graphisches Scheme in ODMG Notation für das Beispiel
aus Abbildung 6.2
Collections
Die im ODMG Model eingebauten Collection (-Interfaces) haben wir bereits bei den Literal
im Abschnitt 6.1.1 gesehen. Für die Interface Deklarationen (oder zumindest ein Teil davon)
siehe die Vorlesungsfolien oder [22], Seiten 668-670. Hier nur einige Bemerkungen:
• Die Containment Relation “Subset” ist nur für sets definiert.
• Union, Intersection und Difference sind nur für Sets und Bags definiert.
• Es gibt keine Constraints für Collections
6.1.2
Object Definition Language (ODL)
Die ODMG Object Definition Language ODL basiert auf der OMG Interface Definition Language IDL. ODL ist designt um die semantischen Konstrukte des ODMG Objekt Modells
zu unterstützen und ist unabhängig von irgendeiner bestimmten Programmiersprache. Ihr
Hauptzweck ist es Objekt Spezifikationen zu kreieren. ODL ist nicht eine richtige Programmiersprache. Ein Benutzer kann ein Datenbankschema in ODL ganz unabhängig von einer
Programmiersprache definieren und dann die spezifischen Sprach-Bindings benutzen um zu
spezifizieren wie ODL Konstrukte auf Konstrukte in spezifischen Programmiersprachen, wie
C++, SMALLTALK oder JAVA gemappt werden können. ODL haben wir bereits in den
Beispielen in den Abbildungen 6.1 und 6.2 benutzt.
6.1.3
Object Query Language (OQL)
Die Object Query Language OQL basiert auf dem ODMG Objekt Model und SQL-92. OQL
ist wie SQL nicht compuational complete. Es gibt in OQL keinen expliziten Update Operator
aber man kann Operationen auf Objekten aufrufen. Ein einfaches Beispiel zeigt Abbildung 6.4.
Path Expressions, Results
Wenn o ein Objekt bezeichnet, dann ist
6.2. JAVA DATA OBJECTS (JDO)
69
select c.address
from Persons p, p.children c
where p.address.street="Main Street" and
count(p.child)>=2 and
c.address.city !=p.address.city
Abbildung 6.4: Eine einfache OQL Query
select distinct s
from Professors p, p.supervises s
where p.dept.name="Computer Science"
order by s.name
Abbildung 6.5: Eine einfache OQL Query mit einem geordneten Set als Resultat
• wenn p ein Attribute bezeichnet, o.p der Wert (Value) des Attributes in o
• wenn p eine Relationship bezeichnet, o.p das Objekt oder die Kollektion von Objekten
vewandt mit o von p
• wenn p eine Methode bezeichnet, o.p(...) das Resultat von p in o ausgeführt (möglicherweise mit parameter.
Ähnlich zu SQL werden normalerweise Duplikate in Kollektionen nicht eliminiert, d.h. es werden Bags als Resultate zurückgegeben. Um Duplikate zu eliminieren benutzt man das Keyword
distinct. Ebenfalls kann man das Set mit order by ordnen (siehe Beispiel in Abbildung 6.5).
Elemente in der select Klausel kann irgendeine Ausdruck sein, auch Ausdrucke mit Typconstructors (Abbildung ??).
Komplexe Queries können gebildet werden, indem man SubQueries in der from Klausel integriert (Abbildung 6.7).
6.2
Java Data Objects (JDO)
Die Abkürzung JDO steht für Java Data Objects, und die unter dem JSR-12 geführte Spezifikation beschreibt ein herstellerunabhängiges Framework zur persistenten Speicherung von
Java-Objekten in transaktionalen Datenspeichern. Die Spezifikation wurde im Mai 2001 von
bekannten Firmen wie Sun, IBM und Apple formuliert. JDO definiert eine einheitliche Schnittstelle für den Zugriff auf persistente Daten, wobei die physikalische Speicherung ziemlich egal
ist. Die Objektinformationen können in Dateien, Datenbanken oder sonstigen Systemen abgespeichert werden. Mit Hilfe von JDO kann der Programmierer Datenobjekte ohne Kenntnis
der Speichermechanismen bearbeiten. Dies ist für die Entwicklung grosser Systeme ein deutlicher Vorteil, denn die Entwickler müssen sich nicht näher mit den Interna von Datenbanken
herum ärgern, sie können sich auf die reine Applikationslogik konzentrieren. Die einzelnen
Hersteller, JDO-Vendor genannt, implementieren eine Speichermöglichkeit für ihr System[46].
select distinct Struct(proj1: p1, proj2: p2, leader:p1)
from Projects p1, Projects p2
where p1.leader=p2.leader
and p1.leader.worksfor="Computer Science"
Abbildung 6.6: Eine OQL Query mit einem Typeconstructor in der select
Klausel
70
KAPITEL 6. OBJEKT-ORIENTIERTE DATENBANKEN
select distinct p.title
from ( select s
from student s
where s.supervisedBy.name="John Smith"
) s1, s1.projects p
Abbildung 6.7: Eine OQL Query mit Subquery
Die Sun JDO Architektur bietet also Programmierern eine transparent java-zentrierte Sicht
auf Persistente Informationen.
Jede Klasse die durch eine JDO Implementationen gemanagt werden soll muss das PersistenceCapable
Interface implementieren. Wie das gemacht wird ist durch den Hersteller, der eine JDO Implementation anbietet bestimmt. Es kann entweder durch einen pre-processor der Java Source
Code generiert geschehen, oder durch mit einem post-processor der den Bytecode modifiziert.
Ein Hersteller (Händler) bietet einen PersistentManager an, der der Kontaktpunkt zwischen
der Applikation und der JDO Implementation darstellt. Ein Entwickler programmiert in Java
und persistente und transiente Daten werden mehr oder weniger uniform gehandhabt.
Wenn ein Hersteller einen Post-Processor benutzt um Klassen persistentfähig zu machen, dann
schreibt und kompiliert man zuerst den Java Code, danach kreiert man eine XML JDO Descriptor File, welches die Klassen beschreibt, die persistent gemacht werden sollen. Danach
lässt man den JDOEnhancer (Post-Processor) laufen, der das JDO Descripter File benutzt
und setzt danach mit der Ausgabe des JDOEnhancer eine Datenbank auf (z.B. könnten das
SQL Statements für Oracle sein).
Teil III
Architekturen und
Technologien
71
Kapitel 7
Client/Server/Middleware
Architekturen
7.1
Zentralisierte DBMS Architektur
Architekturen für DBM-Systeme folgten ähnlichen Trends wie die Architekturen für allgemeine Computersysteme. Frühe Multiuser-Systeme benutzten Mainframe Computer für die
Ausführung aller Funktionen des Systems und daran angeschlossene “dumme” Terminals ,
welche nur Anzeigemöglichkeiten und keine eigene Prozessorpower boten für die Benutzer. In
solchen Teleprocessing Systemen wurden alle Prozess remotely auf dem Mainframe ausgeführt
und nur Display Informationen und Steuerbefehle wurden an die Display-Terminals gesendet.
Als die Preise für Hardware mit der Zeit sanken ersetzten viele Benutzer ihre Terminals durch
PCs und Workstations. In Datenbanksystemen wurden diese Computer zuerst nachwievor wie
die alten Displayterminals benutzt, so dass das DBMS selbst immer noch ein zentralisiertes
System darstellte, in welchem alle DBMS Funktionalitäten, Applikationsprogramm Ausführungen und Benutzerschnittstellen Verarbeitungen auf einer Maschine ausgeführt wurden. Mit
der Zeit begannen DBMS die vorhanden Prozessorpower auf der Benutzerseite zu nutzen und
es entstanden die Client/Server Architektur.
7.2
Client/Server (File-Server) Architektur
Die Client/Server Architektur wurde entwickelt um mit einer Computerumgebung, die aus einer grossen Anzahl PCs, Workstations, Fileserver, Drucker, Datenbankserver, Webserver und
anderen Geräten die zu einem Netzwerk zusammengeschlossen sind umzugehen. Ein solches
Netzwerk besteht aus spezialisiertem Server die eine bestimmte Ressource zur Verfügung stellen. Client Maschinen bieten dem Benutzer ein Interface um die Ressourcen solcher Server
benutzen zu können, sowie lokale Prozessorpower um lokale Applikation laufen zu lassen.
Ein Fileserver speichert z.B. Dateien einer Applikation und ist mit einer Datenbank verbunden. Auf den Client Maschinen liefen die Applikationen und DBMS und wenn nötig fordern
die Workstations Dateien vom Server an. Der Fileserver dient also als gemeinsam genutzte
Harddisk (“shared hard disk drive ”). Der Fileserver an sich besitzt kein DBMS welches direkt
auf den Daten operiert, sondern liefert nur die angefragten Dateien an die Clients und diese
operieren mit ihren DBMS auf den Daten. Ein solches Vorgehen kann starken Netzwerkverkehr hervorrufen, was zu Performance Problemen führen kann. Eine vollständiges DBMS ist
auf jedem angeschlossenen Client nötig um Daten zu verarbeiten, die Concurrency, Recovery
und Integrity Control wird dadurch komplex, weil mehrere DBMS gleichzeitig auf die selben
Daten zugreifen können (wollen).
Zwei Haupttypen von DBMS Architekturen sind auf diesem Client/Server Framework entstanden: two-tier und three-tier
73
74
KAPITEL 7. CLIENT/SERVER/MIDDLEWARE ARCHITEKTUREN
7.3
Two-Tier Client/Server Architektur
SQL als Standardsprache für RDBMS ermöglichte eine klare logische Trennung von Client
und Server Aufgaben. Die Query und Transaktion Funktionalitäten bleiben auf der Serverseite und je nach Anwendung befindet sich auf einem Client mehr (thick client) oder weniger
(thin client) Logik, z.B. eine Applikation welche Daten weiterverarbeiten kann oder nur eine
einfacher Browser, der die erhaltenen Resultate darstellt. Client und Server müssen nicht unbedingt örtlich getrennt sein, befinden sich aber meist auf verschiedenen Rechnern in einem
Netzwerk. Wenn ein DBMS Zugriff nötig ist, baut die Applikation über ein Netzwerk oder IPS
(Client, Server auf der selben Maschine) eine Verbindung zum DBMS (auf der Serverseite) auf
und kann nach erfolgreicher Verbindung mit dem DBMS kommunizieren. Dazu verwendet eine
Applikation spezielle Protokolle, ODBC, JDB oder herstellerspezifische wie Net8 bei Oracle,
wie in Kapitel 4 auf Seite 39 beschrieben.
Es gibt verschiedene mögliche Topologien:
• single client, single server
• multiple clients, single server
• multiple clients, multiple servers
Die Aufgaben in einer Two-Tier Architektur sind klar aufgeteilt:
First Tier
Tasks
• Benutzerschnittstelle
Client
• Haupt Business und
• Datenverarbeitungs
Logik
- verwaltet die Benutzerschnittstelle
- akzeptiere und Checke die Syntax der
Benutzereingabe
- behandle die Applikationslogik
- generiere Datenbankanfragen und sende
sie dem Server
- liefere die Antwort an den Benutzer zurück
Second Tier
Datenbank
Server
Tasks
• serverseitige Validation
• Datenbankzugriff
- akzeptiere und verarbeite die Datenbankanfragen von Clients
- checke Authorisierung
- garantiere, dass Integrity Constraints
nicht verletzt werden
- führe Query/Update Prozessing durch
und sende die Antwort zum Client
- Unterhalte den System Katalog
- Biete konkurenzierenden Datenbankzugriff an
- Biete Recovery Control an
Vor und Nachteile der Two-Tier Architektur
• Vorteile
– erhöht die Sicherheit durch die saubere Trennung von Client und Server Aufgaben
und deren Adressräumen.
– erhöht die Performance da gewisse Aufgaben parallel erledigt werden können und
der Server auf Datenbankprozesse getunt werden kann.
– reduziert die Kommunikationskosten, da nur Anfragen und selektierte Daten über
das Netzwerk übertragen werden.
– erlaubt erweiterter Zugriff auf existierende Datenbanken und mappt genügend
gut auf offene System Architekturen
7.4. THREE-TIER CLIENT/SERVER ARCHITEKTUR
75
Database Server
Monolithic
ein DBS Prozess
Multiple Server
mehrere DBS Prozesse
Symmetrisch
ein DBS Prozess
pro Applikationsprozess
Asymmetrisch
dynamische Allocation von
DBS Prozessen zu
Applikationsprozessen
durch einen Dispatcher
Abbildung 7.1: Klassifizierung von Server
• Nachteile
– limitiert in Bezug der Unternehmens Skalierbarkeit (enterprise scalability)
Server in einer Client/Server Architektur können in Bezug auf Prozesse Klassifiziert werden
(Abbildung 7.1):
• Single Server Prozess
– Ein Server Prozess für viele Clients
– Synchronisierter Zugriff auf System Buffers und zentrale Systemtabellen
– Server Multi-Threading (ablaufinvarianter code)
– DBMS übernimmt die Aufgabe des Resource Managements und dupliziert OS
Funktionalitäten, z.B. Sybase, MS SQL Server
• Multiple Server Prozesse
– Kommunikation über gemeinsamen Speicher
– benutzt OS/Netzwerk Software für die Kommunikation zwischen Client/Server/Dispatcher
Prozessen und das scheduling
– Symmetrisch (Abbildung 7.2) oder Asymmetrisch (Abbildung 7.3)
Das Auftauchen des World Wide Web änderte die Rolle von Client und Server was zur ThreeTier Architektur führte.
7.4
Three-Tier Client/Server Architektur
Mitte der 1990er wurden Applikationen immer komplexer und konnten potentiell an hunderte
oder sogar tausende von Endbenutzer verbreitet werden.
Die traditionellen Two-Tier Architektur hatte zwei Probleme, welche wahre Skalierbarkeit
verhinderte:
• “thick clients” erforderten beträchtliche Ressourcen auf der Client Maschine
• signifikanter Administrations Overhead auf der Client Seite
76
KAPITEL 7. CLIENT/SERVER/MIDDLEWARE ARCHITEKTUREN
Clients
Datenbank Instanz
Servers
Abbildung 7.2: Symmetrischer Multiple Server
Dispatcher
Clients
Shared
Server
Datenbank Instanz
Abbildung 7.3: Asymmetrischer Multiple Server
7.5. TRANSACTION PROCESSING MONITORS
77
Die Three-Tier Architektur fügt einen zusätzlichen Layer zwischen Client und Datenbankserver. Dieser Applikationsserver oder auch “Middleware” ist für die Business Logik und die
Datenverarbeitung zuständig. Er akzeptiert Requests von den Clients, verarbeitet die Anfragen, sendet Datenbankkommandos an den Datenbankserver und agiert dann als Kanal um die
verarbeiteten Daten vom Datenbankserver an den Client weiterzuleiten. Die Client besitzen
eine einfache Benutzerschnittstelle zum Applikationsserver, z.B. ein normaler Web Browser.
First Tier
Tasks
• Benutzerschnittstelle (GUI)
Client
• einfache Eingabe Validierung
Second Tier
Applikations
Server
“Middleware”
Third Tier
Datenbank
Server
Tasks
• Business Logik
• Datenverarbeitungs
Logik
Tasks
• Daten Validierung
• Datenbankzugriff
Die Three-Architekur hat verschiedene Vorteile:
- braucht weniger teure Hardware weil der Client “thin” ist.
- Der Unterhalt der Applikation bleibt Zentral auf einem einzigen Server, was das Problem der Software Distribution des Two-Tier Modells beseitigt.
- erhöhte Modularität macht die Ersetzung eines Tier ohne die anderen zu beeinflussen
einfacher.
- load balancing ist durch die Trennung von Business und Datenbank Funktionen einfacher.
Zusammengefasst kann man sagen der middle Tier erhöht die Performance, Flexability, Maintainability, Reusability und Scalability durch zentralisierte Prozesslogik. zentralisierte Prozesslogik macht die Administration und Change Management einfacher indem System Funktionalitäten lokalisiert sind, damit Änderungen nur einmal vorgenommen und auf dem Middle
Tier platziert werden müssen.
Web Informations Systeme
Die Three-Tier Architektur fügt sich natürlich in die Webumgebung ein. Das Modell kann
auf weitere Tier erweitert werden, z.B. einen separaten Web- und Applikationsserver (Abbildung 7.4). Für Unternehmen reduzieren sich die Kosten in Bezug auf die benötigten Softwarelizenzen und die Distribution von Client Software durch die Tendenz in Richtung Web
Browser als Client Software drastisch.
7.5
Transaction Processing Monitors
Eine Transaction Processing (TP) Applikation ist ein Programm das eine administrative Funktion ausübt indem es für einen Online Benutzer auf eine shared Datenbank zugreift [13]. Ein
TP System ist eine integrierte Menge von Produkten welche TP Applikationen unterstützen. Diese Produkte sind z.B. Hardware, wie Prozessor, Speicher, Disks und Kommunikations
Controllers und Software, wie Betriebssystem (OS), DBMSs, Computernetzwerk und TP Monitore. Ein Grossteil der Integration dieser Produkte wird durch TP Monitors ermöglicht. Ein
78
KAPITEL 7. CLIENT/SERVER/MIDDLEWARE ARCHITEKTUREN
HTTP Request
Web
Browser
HTTP
Server
Application
Server
HTTP Response
(HTTP + HTML)
Internet
Abbildung 7.4: Web Informations System
Abbildung 7.5: Transaction Processing Monitor
TP Monitor ist eine Middleware Komponente, welche den Zugriff auf die Service von verschiedenen Ressourcen Managern ermöglicht. Sie bieten ein uniformes Interface für Entwickler von
Transaktions Software (Abbildung 7.5).
Eine Transaktion ist eine Arbeitseinheit die genau einmal ausgeführt wird und ein permanentes
Resultat liefert (siehe Kapitel 8.1). Eine Transaktion sollte folgende Eigenschaften haben:
• serializable: eine System sollte aussehen als ob die Transaktionen seriell ausgeführt
werden.
• all-or-nothing: entweder wird eine Transaktion als ganzes ausgeführt oder sie hat keinen
Effekt.
• persistent: das Resultat einer Transaktion sollte resistent gegen Ausfälle sein.
Der grösste Teil der Systemunterstützung für die oben genannten Anforderungen bietet das
DBMS . Wenn von einer einzelnen Transaktion aber auf mehrere DBMS zugegriffen wird,
braucht es eine zusätzliche Koordination der DBMS, was meist über TP Monitore (TPM)
geschieht. Die Hauptaufgabe eines TPM ist es den Fluss der Transaktionsanfragen zwischen
den Terminals welche Requests senden und den TP Applikationen, welche die Requests verarbeiten, zu koordinieren. Ein TPM kann folgende Aufgaben erfüllen:
• Transaction Routing
– leitet Transaktionen an ein spezifisches DBMS weiter und erhöht dadurch die
Skalierbarkeit.
• Distributed Transaction Management
– Transaktionen können Zugriff auf verschiedene DBMS benötigen.
7.6. PEER-TO PEER SYSTEME
79
Abbildung 7.6: Peer-to Peer System
• Load Balancing
– Verteile die Anfragen über mehrere DBMS, indem der Client an das am wenigsten
belastete DBMS weitergeleitet wird.
Funneling
– Etabliere Verbindungen zu den DBMS und trichtere (funnel) die Benutzeranfragen durch diese Verbindungen und reduziere dadurch die Anzahl der nötigen
Verbindungen.
• Increase Reliability
– Wenn ein DBMS ausfällt, kann der TPM die Anfrage zu einem anderen DBMS
weiterleiten oder die Transaktion zurückhalten bis das DBMS wieder verfügbar
wird und dann erneut senden.
7.6
Peer-to Peer Systeme
Abbildung 7.6 zeigt ein Peer-to Peer Netzwerk, indem sich Systeme in einer dynamischen
Client/Server Beziehung miteinander verbinden können, um Service und Daten zu teilen. Die
Peer-to Peer Architektur ist die allgemeinste Architekur. Jede Site kann als Server, der Teile
einer Datenbank speichert, oder als Client, der Applikationen ausführt und Queries initiiert,
agieren.
7.7
Verteilte Datenbanksysteme
Forscher und Anwender sind schon seit den 1970er Jahren an Verteilten Datenbanksystemen
(distributed database systems) interessiert. In dieser Zeit war der Schwerpunkt die Unterstützung von verteiltem Datenmanagement für grosse Firmen und Organisationen, die ihre Daten
auf verschiedenen Büros oder Filialen verteilt hatten. Obwohl das Bedürfnis und viele gute
Ideen und Prototypen vorhanden waren, waren die frühen verteilten Datenbanksysteme in
einigen Aspekten ihrer Zeit voraus und waren nie kommerziell erfolgreiche. Die Fortschritte in
der darunter liegenden Technologie, vor allem in der Kommunikationstechnik, machen verteilte Datenbanksysteme heute möglich und durch die veränderte Geschäftsanforderungen auch
nötig. Besonders Firmen benötigen aus verschieden Gründen verteilte Datenbanken [28]:
80
KAPITEL 7. CLIENT/SERVER/MIDDLEWARE ARCHITEKTUREN
• Kosten und Skalierbarkeit:
Heute sind tausend PC Prozessoren günstiger und leistungsst rker als ein grosser Mainframe Computer. Es macht also ökonomisch Sinn Mainframe Server durch PC Clusters zu ersetzen. Ausserdem ist es schwierig und teuer einen Mainframe Computer
aufzurüsten, wenn eine Firma wächst, aber ziemlich einfach neue PCs dem Netzwerk
hinzuzufügen.
• Integration von verschiedenen Software Modulen:
Es stellt sich heraus das kein einzelnes Softwarepaket alle Anforderungen einer Firma
erfüllen kann. Firmen müssen deshalb verschiedene Applikationen installieren, möglicherweise jede mit ihrer eigen Datenbank und das Resultat ist ein verteiltes Datenbanksystem.
• Integration von bereits existierenden Datenbanken:
Die Integration der älteren Systeme ist ein Beispiel dafür, wie Firmen dazu gezwungen
werden auf verteilte Datenverarbeitung zu setzen, in denen die alten Systeme mit den
neuen modernen Systemen koexistieren müssen.
• Verteilte Applikationen:
Es gibt eine Vielzahl neuer Applikationen die stark auf verteilter Datenbank Technologie basieren. Beispiele dafür sind computer-supported collaborative work (CSCW),
Workflow Management, E-Commerce usw.
• Druck des Marktes:
Firmen werden gezwungen ihr Business neu zu organisieren und state-of-the-art Technologien zu verwenden um Konkurrenzfähig zu bleiben. Beispiele sind Service und Präsenz im Internet. Hinzu kommen die vermehrte Globalisierung und grosse Fusionen.
7.7.1
Zukünftige Trends
Es zeichnen sich verschiedene Trends für verteilte Systeme ab:
• Mobile Informations Systeme: Der Benutzer möchte beim Zugriff auf Informationen
nicht an einen festen Standort gebunden sein:
– Tourist Information Systeme
– Kundendaten für einen Verkaufsagenten der Unterwegs ist
– Notfall Service
– Wissenschaftliche Feldarbeiten
– ...
Die Entwicklungen in Mobilen Geräten und Netzwerk Technologien machen Mobile
Informations Systeme zu einer Option.
• Information Environments Vielmehr als einen einzelnen point of access und einen
einzelnen request/response Interaktions Modus zu bieten, reagieren Informationssystem
auf Wechsel im Environment und liefern ihren Output über eine Vielzahl unterschiedlicher Output Kanäle.
7.8
Distributed Query Processing
Man nimmt an, dass Benutzer und Applikationen ein Query veranlassen indem sie eine deklarative Sprache wie SQL oder OQL dafür verwenden und dabei nicht wissen wo und in
welchem Format die Daten in einem verteilten Datenbanksystem abgelegt sind. Das Ziel ist
es eine solche Query so effizient wie möglich auszuführen um die Wartezeit eines Benutzers
oder einer Applikation so kurz wie möglich zu halten. Zu den üblichen Techniken der Query
Optimierung in traditionellen DBMS kommen bei verteilten DBMS noch neue hinzu.
7.8. DISTRIBUTED QUERY PROCESSING
81
Query
Parser
Result
internal repr.
Query
Rewriter
internal repr.
Query
Optimizer
plan
Plan
Refinement/
Code Gen.
Catalog
(Meta Data)
exec plan
Query
Execution
Engine
Base Data
Abbildung 7.7: Query Processing Phasen
7.8.1
Query Processing
Abbildung 7.7 zeigt eine klassische Query Processing Architektur. Diese Architektur kann für
alle Arten von DBMS benutzt werden. Der Query Prozessor erhält eine SQL oder OQL Query
als Eingabe, übersetzt und optimiert dies Query in verschieden Phasen in einen ausführbaren
Plan und führt diesen Plan aus um ein das Resultat der Query zu erhalten.
Parser
In der ersten Phase wird die Query geparst und und in eine interne Repräsentation übersetzt,
die später von den anderen Phasen einfach weiterverarbeitet werden kann. Ein Parser für SQL
oder OQL kann gleich wie für andere Sprachen entwickelt werden, siehe dazu [2]. Für verteilte
System kann der selbe Parser wie für zentrale Systeme benutzt werden.
Query Rewriter
Ein Query Rewriter führt Optimierungen durch, die unabhängig vom physikalischen Zustand
des Systems (z.B. Grösse der Tabellen, Ort von Tabellenkopien, Vorhandene Indizes, Maschinengeschwindigkeit, usw.) gut sind. Typische Transformationen sind das Eliminieren von
redundanten Prädikaten, Vereinfachung von Ausdrücken und das Entflechten von Subqueries
und Views. In einem verteilten System muss der Query Rewriter auch die Partition der Tabelle
auswählen die zum Beantworten der Query in Betracht gezogen wird.
Query Optimizer
Die Komponente führt Optimierungen durch, welche vom physikalischen Zustand des Systems
abhängig sind. Der Optimizer entscheidet welche Indizes verwendet werden sollen um die Query auszuführen, welche Methoden (Hashing oder Sortieren) benutzt werden um die Operation
der Query (z.B. joins und group-bys) auszuführen und in welcher Reihenfolge die Operationen
ausgeführt werden soll. Der Query Optimizer entscheidet auch wieviel Arbeitsspeicher für jede
Operation alloziert werden soll. In einem verteilten System muss der Optimizer zusätzlich noch
entscheiden an welchem Standort (site) jede Operation ausgeführt werden soll. Um dies zu
entscheiden zählt der Optimizer alternative Pläne auf und wählt durch eine Kostenschätzung
den besten Plan aus. Nahezu alle Query Optimizer basieren auf dynamischer Programmierung
(siehe Unterabschnitt 7.8.2) um Pläne effizient aufzuzählen.
Plan
Ein Plan spezifiziert präzise, wie eine Query ausgeführt werden soll. Pläne werden in wahrscheinlich allen Datenbanksytemen als Bäume repräsentiert. Die Knoten der Bäume sind Operatoren und jeder Operator führt eine bestimmte Operation durch (z.B. join, group-by, sort,
scan, usw.). An den Knoten eines Plans wird kommentiert wo der Operator ausgeführt wird.
Abbildung 7.8 zeigt ein Beispiel für eine Query in welcher eine Tabelle A und eine Tabelle
B benutzt werden, die sich an verschiedenen Standorten, Site 1 bzw. Site 2, befinden. Zum
lesen von A wird ein Index benutzt, für B keiner. A und B werden an den Standort Site 0
gesendet und dort mit join zusammengefügt. Die send und receive Operatoren kapseln alle
Kommunikationsaktivitäten ab, so dass alle anderen Operatoren wie in einem herkömmlichen
zentralen System implementiert und benutzt werden können.
Plan Refinement/Code Generation
Diese Komponente transformiert den vom Optimizer produzierten Plan in einen ausführbaren
Plan (z.B. einen assembler-ähnlichen Code um Evaluationen und Prädikate effizient auszuführen).
82
KAPITEL 7. CLIENT/SERVER/MIDDLEWARE ARCHITEKTUREN
site 0
join
receive
site 1
receive
site 2
send
send
idxscan(A)
scan(B)
Abbildung 7.8: Beispiel eines Query Evaluations Plans in einem verteilten DBMS
Query Execution Engine
Diese Komponente bietet eine generische Implementation aller Operatoren. State-of-the-art
Query Execution Engines basieren auf einem Iterator Modell, d.h. Operatoren sind als Iteratoren implementiert und alle Iteratoren haben das selbe Interface, damit beliebige zwei
Iteratoren mit einander verbunden werden können.
Catalog
Der Katalog speichert alle Informationen die für das Parsen, Rewriten und Optimieren gebraucht werden. Er unterhält das Schema der Datenbank (Definitionen der Tabellen, Views,
Benutzerdefinierte Typen und Funktionen, Constraints usw.), das Partitioning Schema (Informationen darüber, welche globalen Tabellen aufgeteilt wurden und wie sie wieder rekonstruiert
werden können) und physikalische Informationen wie den Standort der Kopien von partitionierten Tabellen, Informationen über Indizes und Statistiken die zur Kostenschätzung eines
Plans benutzt werden.
In einem verteilten System stellt sich die Frage wo dieser Katalog gespeichert werden soll. Die
einfachste Möglichkeit besteht darin den Katalog an einem zentralen Standort zu speichern. In
einem wide-area Netzwerk macht es Sinn, den Katalog an verschiedenen Standorten nachzubilden um Kommunikationskosten einzusparen. Es ist auch möglich die Katalog Informationen
an Standorten im wide-area Netzwerk zu Cachen. Beide Varianten sind sehr effektiv, weil Kataloge normalerweise recht klein sind und Katalog Informationen in den meisten Umgebungen
kaum verändert werden. Trotzdem kann in einigen wenigen Umgebungen der Katalog sehr
gross werden und muss immer mal wieder geändert werden. In solchen Umgebungen macht
es Sinn den Katalog aufzuteilen und die Katalogdaten dort abzuspeichern wo sie am meisten
gebraucht werden.
7.8.2
Dynamische Programmierung für Query Optimierung
Für die Auflistung der Pläne in der Query Optimierung wird in nahezu allen kommerziellen
Datenbank Produkten das Dynamische Programmieren benutzt. Der Vorteil der Dynamischen
Programmierung ist, dass sie den bestmöglichen Plan liefert, wenn das Kostenmodell genau
genug ist. Der Nachteil dieses Algorithmus ist seine exponentielle Komplexität, was ihn für
komplexe Queries untauglich macht. Speziell in verteilten Systemen ist die Komplexität des Algorithmus für viele Queries unerschwinglich. Als iterative dynamic programming Algorithmus
7.8. DISTRIBUTED QUERY PROCESSING
83
Input: SPJ Query q auf den Relationen R1 , R2 , . . . , Rn
Output: Query Plan für q
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
for i = 1 to n do {
optP lan({Ri }) = accessP lans(Ri )
pruneP lans(optP lan({Ri }))
}
for i = 2 to n do {
for all S ⊆ {R1 , . . . , Rn } such that |S| = i do {
optP lan(S) = ∅
for all O ⊂ S do {
optP lan(S) = optP lan(S) ∪ joinP lans(optP lan(O), optP lan(S − O))
pruneP lans(optP lan(S))
}
}
}
return optP lan({R1 , . . . , Rn })
Abbildung 7.9: Algorithmus der Dynamische Programmierung für Query Optimierung
ist eine Erweiterung des dynamic programming Algorithmus bekannt[29], welcher für einfache Queries genau so gut funktioniert und für Queries die mit dynamischer Programmierung
nicht gehandhabt werden können, gute Pläne liefert. Abbildung 7.9 zeigt den Algorithmus der
dynamischen Programmierung für die Queryoptimierung. Er funktioniert bottom-up, indem
er komplexere (Sub-) Pläne aus einfacheren (Sub-) Plänen bildet. Als erster Schritt bildet der
Algorithmus einen access plan für jede Tabelle die in die Query involviert ist (Zeilen 1 bis 4).
Wenn z.B. ein Replikat der Tabelle A in Site S1 und S2 vorkommt, listet der Algorithmus
scan(A, S1 ) und scan(A, S2 ) als alternativen access plans auf. Im zweiten Schritt listet der
Algorithmus alle two-way join plans auf indem er die access plans als Bausteine benutzt (Zeilen 5 bis 13). Als nächstes werden alle three-way join plans durch das Benutzen der two-way
plans und der access plans als Bausteine. Der Algorithmus fährt so fort, bis er alle n-way join
plans aufgelistet hat.
Das schöne an diesem Algorithmus ist es, dass schwächere Pläne so früh wie möglich weggeworfen (pruned) werden können (Zeilen 3 und 10). Ein Plan kann weggeschnitten (prune)
werden, wenn ein alternativer Plan existiert der die selbe oder mehr Arbeit mit geringeren
Kosten erledigt. Pruning reduziert die Komplexität der Query Optimierung beachtlich, je
früher eine Plan weggeschnitten werden kann um so besser, da keine komplexeren Pläne aus
diesen gebildet werden. In verteilten System ist das Prunning aber schwieriger, es kann z.B.
weder scan(A, S1 ) noch scan(A, S2 ) weggelassen werden, denn selbst wenn z.B. scan(A, S1 )
billiger ist als scan(A, S2 ) müssen beide behalten werden, den scan(A, S2 ) kann ein Baustein
des besten Overall Plans sein, z.B. wenn das Resultat bei S2 präsentiert werden soll. Nur
wenn die Kosten von scan(A, S1 ) plus die Kosten für das verschieben von A von S1 nach S2
billiger ist als scan(A, S2 ) kann scan(A, S1 ) wirklich weggeworfen werden.
Im allgemeinen kann ein plan P1 weggeworfen werden, wenn ein Plan P2 existiert, der die
selbe oder mehr Arbeit verrichtet und folgendes gilt:
∀i ∈ interestingSites(P1 ) : cost(ship(P1 , i)) ≥ cost(ship(P2 , i))
wobei interestingSites die Menge der sites bezeichnet, die potentiell in der Verarbeitung der
Query involviert sind.
7.8.3
Kostenschätzung für Pläne
Das klassische cost estimation Model besteht darin die Kosten für jede einzelne Operation
des Plans zu schätzen und diese aufsummieren. Die Kosten eines Plans sind definiert als der totale Ressourcenverbrauch des Plans. In einem zentralisierten System bestehen die Kosten eines
84
KAPITEL 7. CLIENT/SERVER/MIDDLEWARE ARCHITEKTUREN
Site 0
join
receive
Site 0
join
join
A
Site 1
send
Site 2
send
join
join
join
B
C
D
Minimum Resource Comsumption
receive
A
B
C
D
Minimum Response Time
Abbildung 7.10: Beispiele Pläne totale resource consumption vs. response time
Operators aus den CPU Kosten plus Disk I/O Kosten. In verteilten System müssen zusätzlich
noch die Kommunikationskosten berücksichtigt werden. Diese Kosten bestehen aus fixen Kosten pro Nachricht, per-byte Kosten für den Datentransfer und CPU Kosten um Nachrichten
an der Sende- bzw Empfangs-Site zu verpacken bzw. entpacken. Die Kosten können gewichtet
werden um den Einfluss von unterschiedlichen Geschwindigkeiten der verschieden Maschinen
und Netzwerke zu modellieren. Ebenso müssen evtl. die Auslastungen (loads) der verschiedenen Maschinen berücksichtigt werden. Als Resultat wird der Optimizer Pläne, die Operationen
auf schnellen, wenig belasteten Maschinen ausführen, bevorzugen und teure Kommunikations
Links zu vermeiden versuchen.
Das klassische Kosten Modell, das der totale Ressourcenverbrauch (resource consumption)
einer Query schätzt, ist nützlich um den overall throughput eines Systems zu optimieren:
wenn alle Queries so wenig Ressourcen wie möglich verwenden und stark belastete Maschinen
vermeiden, dann können so viel Queries wie möglich parallel ausgeführt werden. Das klassische
Modell berücksichtigt aber nicht intraquery Parallelität und somit findet ein Optimizer der auf
diesem Modell basiert im Falle einer schnellen Kommunikation und wenig belastetet Maschinen
nicht unbedingt den Plan mit der niedrigsten response Zeit für eine Query (Abbildung 7.10).
7.8.4
Query Execution Techniken
In verteilten Systemen gibt es alternative Möglichkeiten Queries auszuführen. Um das Beste
aus diesen Techniken herauszuholen, muss der Query Optimizer des Systems erweitert werden,
um entscheiden zu könne ob und wie diese Techniken für eine Query eingesetzt werden. Anders
gesagt bedeutet eine Integration dieser Techniken in ein verteiltes Datenbanksystem die Erweiterung der accessP lans und joinP lans Funktionen in einem dynamic-programming-based
Optimizer, um alternative Plane aufzählen zu können, die diese Techniken nutzen. Ebenso
müssen Kostenberechnungsformeln angegeben werden, damit Kosten und/oder Response Time von solchen Plänen geschätzt werden können.
Row Blocking
Kommunikation ist typischerweise mit send und receive Operatoren implementiert, die typischerweise auf TCP(IP, UDP oder einem anderen Netzwerkprotokoll basieren. Um die Zahl der
Nachrichten und somit Overhead zu reduzieren, verwenden nahezu alle Datenbank Systeme
eine row blocking genannte Technik. Die Idee besteht darin, Tuples, anstatt einzel, blockweise
zu übertragen. Die Grösse der Blocks ist ein auf der Nachrichtengrösse des Netzwerks basierter
Parameter der send und receive Operatoren.
7.8. DISTRIBUTED QUERY PROCESSING
site 0
85
union
receive
site 1
receive
site 2
send
scan(A1)
send
scan(A2)
receive
site 1
send
scan(A3)
Abbildung 7.11: 1. Beispiel für Multithreaded Query Execution
Der receive Operator besitzt ein Buffer Mechanismus und kann ein parent Operator mit Tuples
versorgen auch wenn sich die Übertragung des nächsten Blocks von Tuples verzögert. Als ein
Resultat ist es oft besser eine Blockgrösse zu wählen die grösser ist als die Nachrichtengrösse
des Netzwerks.
Multicast Optimierung
Manchmal muss eine Site die selben Daten an verschiedene andere Sites senden um eine Query
auszuführen. Wenn das Netzwerk kein effizientes Multicasting implementiert, ist es vielleicht
besser Daten von der Site A zur Site B und dann von Site B zur Site C zusenden, anstatt
von A nach B und C. Wenn z.B. Site A eine langsame CPU hat oder stark ausgelastet (load)
ist, kann es ebenfalls günstiger sein, wenn C die Daten von B weitergeleitet bekommt. Im
allgemeinen hat der Query Optimizer für unser Beispiel aus den folgenden drei Strategien die
beste auszuwählen:
• site A → site B und site A → site C
• site A → site B und site B → site C
• site A → site C und site C → site B
Multithreaded Query Execution
Um den besten Gewinn aus dem intraquery Parallelismus zu erhalten, ist es manchmal vorteilhaft an einer Site verschiedene Threads zu laufen zu lassen. Als Beispiel dient die Query
A1 ∪ A2 ∪ A3 aus Abbildung 7.11. Wenn die union und receive Operatoren in Site 0 in einem
einzelnen Thread ausgeführt werden, dann fordert Site 0 nur ein Block aufs Mal an (z.B. mit
round-robin) und die Möglichkeit die drei Partitionen von Site 1,2 und 3 parallel zu senden ist
nicht genutzt. Nur wenn die union und receive Operatoren in Site 0 in verschiedenen Threads
laufen, können die drei receive Operatoren kontinuierlich die send Operatoren in Site 1,2 und
3 nach Tuples fragen, so dass alle drei send Operatoren parallel laufen und Tuples produzieren
können.
Separate Threads für jeden Query Operator zu etablieren ist aber nicht immer die beste Lösung. Erstens muss die shared-memory Kommunikation zwischen den Threads synchronisiert
werden, was zusätzliche Kosten hervorruft und zweitens ist es nicht immer vorteilhaft alle
Operationen zu Parallelisieren. Für die Query aus Abbildung 7.12 ist es z.b. nicht unbedingt
sinnvoll die Tabellen A1 und A2 parallel zu erhalten uns zu sortieren. Wenn in Site 0 genügend Arbeitsspeicher vorhanden ist um grosse Teile von beiden Tabellen zu speichern, sollte
die zwei Paare von receive und sort Operatoren durchaus parallel ausgeführt werden. Ist dies
nicht der Fall sollten sie nacheinander ausgeführt werden, um einen Ressourcen “Streit” (Disk
Trashing, wenn beide sorts gleichzeitig auf die selbe Disk schreiben möchten) in der Site 0
zu vermeiden. Der Query Optimizer und/oder zur Laufzeit ein Scheduler muss entscheiden
welche Teile einer Query parallel und welche Operatoren deshalb im selben Thread laufen
sollen.
86
KAPITEL 7. CLIENT/SERVER/MIDDLEWARE ARCHITEKTUREN
site 0
merge−join
sort
receive
sort
receive
site 2
site 1
send
scan(A1)
send
scan(A2)
Abbildung 7.12: 2. Beispiel für Multithreaded Query Execution
Joins mit Horizontal Fragmentierten Daten1
Sind Tabellen horizontal Partitioniert machen es die logischen Eigenschaften der join und
union Operatoren möglich Joins auf verschiedene Arten auszuführen. Wenn z.B. die Tabelle
A horizontal in A1 und A2 partioniert ist, so dass gilt A = A1 ∪ A2 , dann kann A ./ B auf
zwei Arten berechnet werden
(A1 ∪ A2 ) ./ B oder (A1 ./ B) ∪ (A2 ./ B)
Ist A sogar in mehr als zwei Partitionen fragmentiert oder ist B auch noch fragmentiert ergeben
sich sogar noch mehr Varianten. Z.B. kann ((A1 ∪ A2 ) ./ B) ∪ (A2 ./ B) ein attraktiver Plan
sein, wenn B vervielfältigt wurde und sich eine Kopie von B auf einer Site befindet, die nahe
einer Site die A1 und A2 speichert liegt und sich eine andere Kopie in der Nahe der Site,
welche A3 speichert befindet.
Semijoins
Semijoin Programme wurden als eine weiter Technik für die Verarbeitung von Joins zwischen
Tabellen die an verschiedenen sites gespeichert sind vorgeschlagen. Wenn eine Tabelle A in
Site 1 und eine Tabelle B in Site 2 gespeichert ist, ist die “konventionelle” Vorgehensweise
A ./ B durchzuführen, die Tabelle A von Site 1 nach Site 2 zu senden und den Joint in site
2 durchzuführen (oder andersherum). Die Idee von einem Semijoin Programm ist es nun nur
diese Kolonnen von A die für die Evaluierung der Join Prädikate gebraucht werden (S1 =
π(A ∩ B)(A)) von Site 1 nach Site 2 zu senden, die Tuples von B welche den Join näher
bestimmen (S2 = S1 ./ B) in Site 2 zu finden, diese zur Site 1 zu senden und dann A mit diesen
Tuples von B in Site 1 zu matchen (S2 ./ A). Formal kann diese Prozedur folgendermassen
geschrieben werden2 :
A ./ B = A ./ (B n π(A))
Experimente haben gezeigt, dass Semijoin Programme für die Join Verarbeitung in verteilten
Standard Datenbank Systemen typischerweise nicht besonders attraktiv sind, da die Kosten für
den zusätzliche Overhead der Berechnungen, normalerweise höher sind als die eingesparten
Kommunikationskosten. In Applikationen, die auf verschiedenen, grossen Tabellen mit sehr
grossen Tuples operieren kann eine Semijoin Technik aber durchaus effizienter sein.
1 Horizontale
Fragmentation (horizontal fragmentation) verteilt eine Relation auf Sets von
Tuples (Zeilen), im Gegensatz zur vertikalen Fragmentation (vertical fragmentation), wo eine
Relation auf Subrelationen verteilt wird, wobei jede Subrelation als ein Subset der Kolonnen
der Originalrelation definiert ist.
2 n ist der Semijoin Operator und π(A) projiziert die Joinkolonnen von A aus.
7.8. DISTRIBUTED QUERY PROCESSING
7.8.5
87
Ausnützen der Client Ressourcen
Bei einem Client-Server System, indem die Datenbank persistently von einer Server Maschine
gespeichert wird und Queries von einem Client initiiert werden, stellt sich die Frage, ob die
Query zu den Daten gesendet werden soll (Ausführung auf dem Server), oder ob die Daten zur
Query verschoben werden sollen (Ausführung auf dem Client). Ein andere verwandte Frage
ist, ob und wie caching (z.B. um temporär Kopien der Daten auf dem Client zu speichern)
benutzt werden soll.
Query Shipping
Query Shipping wird heute in den meisten Relationalen und Objekt-relationalen DBMS benutzt. Das Prinzip besteht darin, eine Query auf dem Server auszuführen. In einem System
mit mehreren Servern funktioniert Query shipping nur mit einem middle-tier, der die Joins
zwischen auf verschiedenen Servern gespeicherten Tabellen vornimmt.
Data Shipping
Data Shipping wird in vielen Objekt-Orientierten DBMS benutzt und ist genau das Gegenteil
des Query Shipping. Queries werden auf der Client Maschine, welche die Query initiiert hat
ausgeführt und Daten werden rigoros im Arbeitsspeicher oder auf der Disk der Client Maschine
zwischengespeichert (cached).
Hybrid Shipping
Die Vorteile von beiden Vorgehen können in einer Hybrid Shipping Architektur kombiniert
werden. Hybrid Shipping bietet die Flexibilität eine Query auf der Client oder der Server
Maschine auszuführen und erlaubt das Caching von Daten durch die Clients. Das Cachen von
Daten auf dem Client ist besonders günstig, wenn viel auf den selben Datenobjekt operiert
wird.
Performance Trade-Offs
Query Shipping erfüllt gute Arbeit, wenn die Server Maschinen Leistungsstark und die Client
eher langsam sind. Auf der anderen Seite bringt Query Shipping keine gute Leistung, wenn
viele Clients vorhanden sind, da die Server potentielle Flaschenhälse im System darstellen. Data Shipping bringt gute Leistungen, weil die Client Maschinen ausgenutzt werde, kann aber
der Grund für hohe Kommunikationskosten sein, wenn das Caching nicht effektiv ist und eine
grosse Menge ungefilterter Basisdaten zu den Clients gesendet werden muss. Offensichtlich
hat Hybrid Shipping das Potential mindestens eine ebenso gute Performance zu erzielen, wie
die beste Performance von Data Shipping und Query Shipping, indem Caching und Client
Ressourcen wie beim Data Shiping ausgenutzt werden wenn dies förderlich ist oder sonst wie
beim Query Shipping vorgegangen wird. Der Preis für diese Flexibilität, ist die nicht grössere
Komplexität der Query Optimierung in einem solchen Hybrid Shipping System, denn der Optimizer muss mehr Optionen berücksichtigen. Experimente zeigten drei weniger offensichtliche
Effekte für Hybrid Shipping Systeme:
• Manchmal ist es in einem hybrid Shipping System besser Daten von den Serverplatten
zu lesen, selbst wenn die Daten auf dem Client im Cache liegen. Nehmen wir als Beispiel
eine Join Query von zwei Tabellen die auf zwei verschiedenen Servern liegen und nehmen
wir an, dass diese Tabellen ebenso auf der Disk des Client temporär gespeichert sind
(cached) und das Netzwerk schnell ist. In diesem Fall kann die Beste Methode die Query
auszuführen sein, beide Tabellen von den Servern zu lesen und den join auf dem Client
auszuführen. Auf diese Art kommen sich das Lesen der Daten von der Server Disk und
die Join-Bearbeitung auf der Disk des Clients nicht in die Quere.
• Wenn die Daten im Arbeitsspeicher des Client gespeichert (cached) sind, das Netzwerk
schnell ist aber die Query am effizientesten auf dem Server durchgeführt werden kann,
kann es besser sein die Basisdaten aus dem Cache oder Zwischenresultate vom Client
an den Server zurückzusenden.
• Transaktionen, welche viele kurze Update Operationen beinhalten, sollten am besten
auf dem Client ausgeführt und dabei die neuen Versionen der Tuples in den Cache
geschrieben werden. Der Vorteil besteht darin, dass solche Transaktionen auf dem Client
wieder rückgängig gemacht werden können ohne dass der Server dadurch beeinflusst
wird und die Updates können in einem Batch, mit wenig Overhead, an den Server
gesendet werden.
88
KAPITEL 7. CLIENT/SERVER/MIDDLEWARE ARCHITEKTUREN
display
update
Binäre Operatoren
z.B. join
Unäre Operatoren
z.B. sort, group-by
scan
Data Shipping
Client
Client
Konsument
Query Shipping
Client
Server
Produzent des linken
oder rechten Inputs
d.h. Client
Konsument
Produzent
Client
Server
Hybrid Shipping
Client
Client oder Server
Konsument oder
Produzent des linken
oder rechten Inputs
Konsument oder
Produzent
Client oder Server
Tabelle 7.1: Site Selection für Client-Server oder Peer-to-Peer
7.8.6
Query Optimierung
Site Selektion
Von der Perspektive eines Query Optimizers können Data Shipping, Query Shipping und Hybrid Shipping als Optionen für die Site Selektion modelliert werden. Jeder Operator eines
Plans hat eine site annotation, welche angibt wo der Operator ausgeführt werden soll. Tabelle 7.1 zeigt mögliche site annotations für verschieden Klassen von Query Operatoren und
die drei alternativen Vorgehensweisen. Eine Client Site Annotation gibt an, dass der Operator vom Client der die Query erstellt hat ausgeführt werden soll. Eine Consumer (Producer)
Annotation gibt an, dass der Operator an der selben Maschine ausgeführt werden soll, die
auch den Operator der das Resultat (Input) des Operators weiterverarbeitet ausführt. Eine
Server Annotation für einen scan gibt an, dass der scan auf einem der Server, die eine Kopie der gescannten Daten speichern, ausgeführt werden soll, während eine Server Annotation
für einen update angibt, dass die Operation auf allen Servern, die eine Kopie der betroffenen
Daten speichern, durchgeführt werden soll.
Wo und Wann Optimieren?
Es gibt zwei Fragen von speziellem Interesse für die Query Optimierung in einer Client-Server
Umgebung. Die erste Frage ist, wo das eine Query optimiert werden sollte. Das Vorgehen,
gewisse Schritte der Query-Verarbeitung auf dem Client (wo die Query herkommt) selber
durchzuführen, während andere auf dem Server durchgeführt werden sollten, macht Sinn, da
gewisse Operationen wie Parsing und Query Rewriting sehr gut auf dem Client selber ausgeführt werden können ohne das der Server gestört wird, während aber andere Schritte, wie
Query Optimization, eine gute Kenntnis des aktuelle Systemstatus benötigen und deshalb
dem Server überlassen werden sollten. In einem System mit vielen Servern hat keine einzelner
Server die komplette Kenntnis des ganze Systems. In einem solchen System, muss ein Server
die Query Optimierung übernehmen. Dieser Server muss entweder den Zustand des Netzwerkes und der anderen Servern auf Basis von statistischen Erfahrungswerten schätzen oder aber
versuchen durch Abfragen der aktuellen Zustande der Server herauszufinden.
Die zweite Frage ist, wann eine Query optimiert werden soll. Diese Frage stellt sich für so
genannte canned Queries, die Teil eines Applikationsprogrammes sind und während der Ausführung der Applikation evaluiert werden. Die traditionelle Vorgehensweise kompiliert und
optimiert eine solche Query zur Compilezeit des Applikationsprogrammes, speichert sie die
Pläne für die Queries in der Datenbank und lädt und führt diese Pläne immer aus, wenn die
Applikation ausgeführt wird. Offensichtlich kann diese Methode nicht auf Änderungen wie Serverauslastungsverschiebungen reagieren und der kompilierte Plan zeigt in vielen Situationen
eine schlechte Performance. Eine andere Idee besteht darin zu Compilezeit mehrere alternative Pläne und/oder Subpläne zu generieren und zur Ausführungszeit einen auszuwählen, der
am besten für den aktuellen Zustand des Systems passt. Sogar noch dynamischere Methoden optimieren Queries on the fly. Die Idee besteht darin die Ausführung eines kompilierten
oder dynamisch ausgewählten Plans zu starten und zu überwachen, ob die Zwischenergebnisse mit der erwarteten Rate produziert und ausgeliefert wurden. Werden die Erwartungen
nicht erreicht, wird die Ausführung gestoppt, Zwischenergebnisse werden materialisiert und
der Optimizer wird aufgerufen um einen neuen Plan für den verbleibenden Teil der Query zu
finden.
Two-Step Optimization
Two-step Optimierung funktioniert folgendermassen:
7.8. DISTRIBUTED QUERY PROCESSING
display
display
display
join
join
join
join
A
join
join
B
89
C
(a) Two−Step Plan zur Kompilezeit
D
A
join
join
B
C
(b) Two−Step Plan zur Laufzeit
D
A
join
D
C
B
(c) Optimaler Plan
Abbildung 7.13: Erhöhte Kommunikationskosten durch Two-Step Optimierung
1. Zur Compilezeit: Generiere einen Plan der die Join Reihenfolge, Join Methoden und
die Zugriffspfade spezifiziert.
2. Bevor die Query ausgeführt wird: Transformiere den Plan und führe die Selektion der
Site aus (d.h. entscheide wo jeder Operator verarbeitet wird).
Two-step Optimierung hat eine angemessene Komplexität, weil beide Schritte mit akzeptablen
Aufwand ausgeführt werden können. Two-Step Optimierung ist nützlich um die Auslastung in
verteilten System aus zu balancieren, weil die Ausführung von Operationen in stark belastet
Sites, durch die Site Selektion zur Laufzeit vermieden werden können. Dies Optimierungsmethode ist ebenso nützlich um das Caching in Hybrid Shipping System auszunutzen, da Query
Operatoren dynamisch auf einem Client platziert werden können, wenn dieser die Daten im
Cache hat. Ein Nachteil ist, dass Two-Step Optimierung in Plänen mit unnötig hohen Kommunikationskosten resultieren. Abbildung 7.13 zeigt warum. der Plan in (a) zeigt die Join
Reihenfolge die im ersten Schritt bestimmt wurde, (b) zeigt das Resultat der Site Selektionierung im zweiten Schritt (Ausführungszeit) und (c) zeigt den optimalen Plan. die Färbung in
(b) und (c) geben dabei die Site Annotation an (gleiche Färbung = gleiche Site). der zweite
durch two-step Optimierung erhaltene Plan hat höhere Kommunikationskosten als der optimale Plan, weil der erste Schritt den Datenort und den Einfluss der Join Reihenfolge auf die
Kommunikationskosten in einem verteilten System ignorierte.
7.8.7
Query Ausführung
Ein Thema das in Hybrid Shipping System auftaucht, ist wie man mit Transaktion umgehen
soll, die zuerst Daten im Client Cache updaten und dann eine Query auf einem Server ausführen in der diese Daten involviert sind. Z.B. wird der Lohn eines Angestellten, sagen wir
von Hr. Meier, zuerst erhöht und dann nach dem Durchschnittslohn aller Angestellten gefragt.
Der Update wird wahrscheinlich auf dem Client der die Transaktion startete ausgeführt (siehe
7.8.5). Auf der anderen Seite wird der Optimizer wahrscheinlich entscheiden die zweite Query
auf dem Server der die Angestellten-Tabelle speichert auszuführen, um nicht die ganze Tabelle
zum Client schicken zu müssen. Der Punkt ist, dass die Berechnung des durchschnittlichen
Lohnes, den neuen Lohn von Hr. Meier berücksichtigen muss, welcher dem Client nicht aber
dem server bekannt ist. Es gibt dafür zwei möglich Lösungen:
• Propagiere kurz vor der Ausführung der Query auf dem Server alle relevanten Updates
an den Server.
• Führe die Query auf dem Server durch und justiere das Resultat auf dem Client so,
dass die Änderungen miteinbezogen werden.
In beiden Fällen involviert das Ausführen der Query auf dem server zusätzliche Kosten, die
von einem dynamischen oder Two-Step Optimizer bei der Entscheidung, ob es billiger ist eine
Query auf dem Client oder der Server auszuführen, in Betracht gezogen werden muss.
90
KAPITEL 7. CLIENT/SERVER/MIDDLEWARE ARCHITEKTUREN
Ziel
Granularität
Speichermedium
Auswirkungen auf
den Katalog
Update protocol
Löschen der Kopie
Mechanismus
Replication
Server
grob
(ganze Tabellen,
Indizies,. . . )
typ. Disk
Ja
Propagierung
explizit
separates Holen
Caching
Client oder
Middle-Tier
fein
(individuelle Seiten
von Tabellen)
typ. Memory
Nein
Invalidierung
implizit
Fehlende Daten und
Behalten der Kopien
nach Gebrauch
Tabelle 7.2: Unterschiede zwischen Replication und Caching
7.8.8
Dynamische Datenplatzierung
In den vorherigen Abschnitten haben wir uns mit den Strategien befasst, mit gegebener Query
und den gegeben Orten mit Kopien der Daten und anderen Parametern einen besten (billigsten) Ausführungsplan zu finden. Jetzt befassen wir und mit Frage, wo die Kopien der Daten
in einem verteilten System platziert werden sollten, so dass das der gesamte Query workload
auf die günstigste und schnellst mögliche Art ausgeführt werden kann.
Traditionell wurden Daten statisch platziert (static data placement), d.h. ein System Administrator entschied wo Kopien der Daten gespeichert werden, indem er spekuliert was für Arten
von Queries ausgeführt werden könnten und dementsprechend die Orte auswählte. Static data
placement hat verschiedene Nachteile:
1. Der Query workload ist oft nicht vorhersehbar
2. Selbst, wenn der workload vorhergesagt werden kann, wird er möglicherweise ändern
und er kann so schnell ändern, dass der Systemadministrator die Datenplatzierung nicht
schnell genug anpassen kann.
3. Die Komplexität eines genügend genauen Modells für statische datenplatzierung ist zu
gross (Das Problem ist NP-complete)
Eine andere Möglichkeit ist die dynamische Datenplatzierung, die Statistiken über Query
Workloads behält und automatisch Daten verschiebt und Kopien der Daten in verschiedenen
Sites anlegt um die Datenplatzierung dem aktuellen Workload anzupassen.
Replication vs. Caching
Im Prinzip gibt es zwei verschieden Mechanismen Kopien von Daten auf verschiedenen Sites
anzulegen: Replication und Caching. Obwohl das Ziel das selbe ist (etablieren von Kopien der
Daten an verschiedenen Orten, um die Kommunikationskosten zu reduzieren und/oder die
Auslastung des Systems zu balancieren), gibt es einige Unterschiede. Tabelle 7.2 listet diese
Unterschiede auf.
Kapitel 8
Transaktionsmodelle und
Systeme
8.1
Transaktionen und Concurrency
Eine Transaktion ist ein ausführendes Programm das eine logische Einheit der Datenbankverarbeitung bildet. Eine Transaktion beinhaltet eine oder mehr Datenbankzugriffs Operationen,
die eine Datenbank von aktuellen konsistenten Datenbankzustand in einen neuen konsistenten
Zustand überführt. Eine Transaktion muss entweder vollständig erfolgreich beendet werden
(commit) und die Änderungen permanent gemacht werden, oder sie beendet unvollständig
(abort) und die bisherigen Effekte müssen rückgängig gemacht werden. Transaktionen die von
verschiedenen Benutzern gestartet wurden, laufen in einem Multiuser System unter Umständen “parallel” in Konkurrenz zu einander ab (concurrently) und greifen vielleicht auf die selben
Datenelemente zu. Wenn diese konkurrierende Ausführung von Transaktionen unkontrolliert
stattfindet, kann es zu Problemen, wie inkonsistenten Datenbanken führen.
8.1.1
Warum Concurrency Control?
Es können verschiedene Probleme auftauen wenn Transaktion konkurrieren zu einander ablaufen. Die verschiedenen Probleme werden anhand der zwei Transaktionen T1 und T2 die
Geld von einem Konto A zu einem Konto B bzw. von B nach C überweisen (Abbildung 8.1)
und einer dritten Transaktion die Kontostand aller Konten zusammenzählt aufgezeigt.
• Lost Update Problem: Dieses Problem taucht auf, wenn zwei Transaktionen, die auf
die selben Datenbankelemente zugreifen ihre Operationen so in einer wechselseitigen
Reihenfolge ausführen, dass der Wert gewisser Datenbankelemente unkorrekt wird. (Abbildung 8.2)
• Dirty Read (Temporary Update) Problem: Dieses Problem taucht auf, wenn eine
Transaktion ein Datenbankelement updated und dann die Transaktion aus irgendeinem
Grund scheitert. Auf das geänderte Datenelement wird von einer anderen Transaktion
zugegriffen bevor die Änderung zurückgenommen wurde (rollback ) (Abbildung 8.3).
• Incorrect Summary Problem: Wenn eine Transaktion eine Summary Funktion auf
einer Anzahl Records berechnet (in unserem Beispiel Transaktion T3 ), während andere
Transaktionen einige dieser Record Updaten, kann es sein, dass die Aggregatfunktion
gewisse Werte bevor sie geändert und gewisse Werte nach dem sie geändert wurden
berechnet (Abbildung 8.4).
91
92
KAPITEL 8. TRANSAKTIONSMODELLE UND SYSTEME
Transaktion T1 :
begin
read A.balance
read B.balance
A.balance:=A.balance-50
B.balance:=B.balance+50
write A.balance
write B.balance
end
Transaktion T2 :
begin
read B.balance
read C.balance
B.balance:=B.balance-100
C.balance:=C.balance+100
write B.balance
write C.balance
end
Transaktion T3 :
begin
total:=0
read A.balance
total:= total+A.balance
read B.balance
total:= total+B.balance
read C.balance
total:= total+C.balance
print "total =", total
end
Kontostand:
Initial
T1 T2
T1 T2
A
100
50
50
B
200
150
150
C
50
150
150
Total
350
350
350
Abbildung 8.1: Beispiel Transaktion die Geld zwischen Bankkonten überweisen
und den Totalfund berechnen
8.1. TRANSAKTIONEN UND CONCURRENCY
Zeit
1
2
3
4
5
6
7
8
9
10
11
12
Transaktion
T1
T1
T1
T2
T2
T1
T1
T2
T2
T1
T2
T2
Action
read A.balance
read B.balance
A.balance:=A.balance-50
read B.balance
read C.balance
B.balance:=B.balance+50
write A.balance
B.balance:=B.balance-100
C.balance:=C.balance+100
write B.balance
write B.balance
write C.balance
93
† Update von T1 verloren
Kontostand:
Initial
T1 T2
T1 T2
hier
A
100
50
50
50
B
200
150
150
100
C
50
150
150
150
Total
350
350
350
300
Abbildung 8.2: Transaktion Schedule mit Lost Update Problem
94
KAPITEL 8. TRANSAKTIONSMODELLE UND SYSTEME
T2 abort
Zeit
1
2
3
4
5
6
7
8
9
10
11
12
Transaktion
T1
T2
T2
T2
T2
T2
T1
T1
T2
T1
T1
T1
Action
read A.balance
read B.balance
read C.balance
B.balance:=B.balance-100
C.balance:=C.balance+100
write B.balance
read B.balance
A.balance:=A.balance-50
write C.balance
B.balance:=B.balance+50
write A.balance
write B.balance
† B stimmt nicht mehr
Kontostand:
Initial
T1 T2
T1 T2
hier
A
100
50
50
50
B
200
150
150
150
C
50
150
150
50
Total
350
350
350
200
Abbildung 8.3: Transaktion Schedule mit Dirty Read Problem
Im folgenden sind nur die read und write Operationen sind aufgeführt.
Zeit
1
2
3
4
5
6
7
Transaktion
T3
T1
T1
T1
T1
T3
T3
Action
read A.balance
read A.balance
read B.balance
write A.balance
write B.balance
write B.balance
write C.balance
† Update von A
Kontostand:
Initial
T1
T3 berechnet
A
100
50
100
B
200
250
250
C
50
50
50
Total
350
350
400
Abbildung 8.4: Transaktion Schedule mit Incorrect Sumary Problem
8.2. CONCURRENCY CONTROL TECHNIKEN
8.1.2
95
Transaktions Eigenschaften
Transaktionen sollten folgende Eigenschaften, oft ACID Properties aufweisen:
1. Atomicity: Eine Transaktion ist ein atomare Verarbeitungseinheit; sie wird entweder
als Ganzes oder gar nicht ausgeführt. all-or-nothing
Mechanismus: Commit/Abort Transaction
2. Consistency Preservation: Eine Transaktion ist consitency preserving, wenn ihre
volle Ausführung die Datenbank von einem konsistenten Zustand in einen anderen
überführt. integrity constraints satisfied
Mechanismus: Transaction Programmer/TP System
3. Isolation: ein Transaktion sollte erscheinen als ob sie isoliert von anderen Transaktionen ausgeführt würde. D.h. die Ausführung einer Transaktion sollte nicht mit irgendwelchen anderen konkurenzierend ausgeführten Transaktione interferieren. serialisability
Mechanismus: Locking
4. Durability oder permanency: Die Änderungen die eine Commited Transaktion in
einer Datenbank vorgenommen hat, müssen in der Datenbank persistent sein. Dies
Änderungen dürfen durch keine Art von Ausfällen verloren gehen. failure recovery
Mechanismus: log-based Recovery
8.1.3
Serialisierbarkeit
Eine Schedule S für n Transaktionen ist serialisierbar (serializable), wenn er equivalent zu
einem serial schedule der selben n Transaktionen ist. Ein Schedule ist serial, wenn die n
Transaktionen nacheinander (in irgendeiner Reihenfolge) ausgeführt werden, ohne dass die
Operationen verschiedener Transaktionen mit einander interferieren (also jede Transaktion ist
vollständig (commit oder abort), bevor die nächste beginnt). Mit folgendem Algorithmus, der
einen precedence Graph (oder serialization Graph) konstruiert, kann man einen Schedule S
mit n Transaktion Ti auf Serialisierbarkeit überprüfen (Abbildung 8.5 zeigt ein Beispiel):
1. Kreiere für jede Transaktion Ti die am Schedule beteiligt ist einen mit Ti bezeichneten
Knoten in Graphen.
2. Für jeden Fall in S indem Tj ein read_item(X ) ausführt nachdem eine Transaktion Ti
eine write_item(X ) ausführt, kreiere eine Kante (Ti → Tj ) im Graph.
3. Für jeden Fall in S indem Tj ein write_item(X ) ausführt nachdem eine Transaktion
Ti eine read_item(X ) ausführt, kreiere eine Kante (Ti → Tj ) im Graph.
4. Für jeden Fall in S indem Tj ein write_item(X ) ausführt nachdem eine Transaktion
Ti eine write_item(X ) ausführt, kreiere eine Kante (Ti → Tj ) im Graph.
5. Der Schedule S ist Serialisierbar, wenn und nur wenn der Graph keine Zyklen hat.
8.2
8.2.1
Concurrency Control Techniken
Locks
Wir haben die folgenden Shared/Exclusiv (oder Read/Write) Locking Operationen:
• read_lock(X ): read-locked Elemente werden oft auch share-locked genannt. Anderen
Transaktionen ist es erlaubt das gelockte Datenbankelement X zu lesen, nicht aber zu
Beschreiben
• write_lock(X ): write-locked Elemente werden auch exclusive-locked genannt. Eine
Transaktion hält exklusiv den Lock auf das Datenbankelemnt X , keiner anderen Transaktion ist es erlaubt das Element zu Lesen oder Beschreiben.
• unlock(X ): Unlock das Datenbankelement X
Eine Transaktion muss einen Lock für ein Element beantragen, bevor sie auf das Element
zugreifen kann.
Wir kommen auf das Locking später noch mal zurück (8.3).
96
KAPITEL 8. TRANSAKTIONSMODELLE UND SYSTEME
Schedule:
T1:read_item(X), T1:write_item(X), T2:read_item(X),
T3:read_item(Y), T2:read_item(Y), T2:write_item(Y),
T3:read_item(Z), T3:read_item(Z), T1:read_item(Z)
X
T1
Z
T2
Y
T3
Keine Zyklen ⇒ Serialisierbar
Serialization Order : T3 T1 T 2
Abbildung 8.5: Serialisierbarkeit eines Schedule
8.2.2
Two Phase Locking (2PL)
Eine Transaktion folgt dem two-phase locking Protokoll, wenn alle Locking Operationen
(read_lock, write_lock) vor der ersten Unlock Operation kommen. D.h. wenn einmal eine
Lock von einer Transaktion freigegeben wurde, können keine weite Locks auf Datenelemente
beantragt werden.
strict 2PL
Wenn eine Transaktione keine seiner exklusive-Locks freigibt bevor die Transaktion durch ein
commit oder abort beendet ist, spricht man von strict two phase locking
Obwohl kann gezeigt werden kann, dass wenn jede Transaktion in einem Schedule dem Two
Phase Locking Protokoll folgt, der Schedule garantiert serializable ist, erlaubt das 2PL nicht
alle möglichen serialisierbaren Schedules (d.h. gewisse serialisierbare Schedule werden durch
das 2PL nicht erlaubt sein).
Beide 2PL Techniken können zu zwei Problemen führen: Deadlocks und Starvation (siehe ??).
Locking kann vom Programmierer versteckt werden, indem ein Datenmanager die read oder
write Locks vor der Verarbeitung auf Daten setzt, die Transaktionen selber aber nur read und
write Operationen ausstellt. Eine Transaktion muss nur die Transaktion durch ein Anfang
und ein Ende bestimmen und der Datenmanager (data manager ) macht den Rest. Aus Performancegründen kann dem Programmierer gewisse Kontrolle über das Setzen und Freigeben
von Lock übergeben werden.
8.2.3
Timestamp Ordering
Ein Timestamp ist ein eindeutiger vom DBMS kreierter Identifier um Transaktionen zu identifizieren. Typischerweise werden Timestamp Wert ein der Reihenfolge in der die Transaktionen
beim DMBS eingereicht werden zugeordnet und kann als eine Art Startzeit betrachtet werden.
Concurrency Control Techniken, die auf dem Timestamp Ordering basieren benutzten keine
Locks und sind deshalb Deadlockfrei.
Timestamps können als einfacher Counter der für jede Transaktionen inkrementiert implementierte werden, oder sie benutzen die aktuelle Zeit und das Datum um einen Timestamp zu
kreieren und stellen sicher, dass keine zwei Timestamp Werte zum selben Clock tick generiert
werden.
8.2. CONCURRENCY CONTROL TECHNIKEN
97
Timestamp Ordering Algorithmus
Die Idee für dieses Schema ist es, die Transaktionen basierend auf ihrem Timestamp zu ordnen. Ein Schedule mit diesen Transaktionen ist dadurch serialisierbar und die äquivalente
Schedule hat die Transaktionen in der Reihenfolge deren Timestamp Werte. Der Algorithmus
muss sicher stellen, dass für jedes Element, auf das mit sich widersprechenden Operationen
im Schedule zugegriffen wird, die Reihenfolge in der die Transaktionen auf das Element zugreifen, nicht die serialisierbare Ordnung verletzt. Dazu verwendet der Algorithmus für jedes
Datenbankelement X zwei Timestamp (TS) Werte:
1. read_TS(X ): Der Read Timestamp von Element X ; dies ist der grösste Timestamp
unter allen Timestamps von Transaktionen, die erfolgreich das Element X gelesen haben, read_TS(X )=TS(T ), wobei TS(T ) den Timestamp der jüngsten Transaktion, die
X erfolgreich gelesen hat bezeichnet.
2. write_TS(X ): Der Write Timestamp von Element X ; dies ist der grösste Timestamp
unter allen Timestamps von Transaktionen, die erfolgreich das Element X beschrieben
haben, write_TS(X )=TS(T ), wobei TS(T ) den Timestamp der jüngsten Transaktion,
die X erfolgreich beschrieben hat bezeichnet.
Der Concurrency Control Algorithmus muss in den folgen zwei Fällen überprüfen, ob zwei
Konflikt-Operationen das Timestamp Ordering verletzen:
1. Transaktion T erteilt eine write_item(X ) Operation:
(a) Wenn read_TS(X )>TS(T ) oder wenn write_TS(X )>TS(T ), dann abort und rollback T und verwerfe die Operation. Dies sollte gemacht werden, weil eine jüngere
Transaktion mit einem Timestamp grösser als T bereits das Element gelesen oder
beschrieben hat, bevor T die Chance hatte auf X zu schreiben. Dadurch wird das
Timestamp Ordering verletzt.
(b) Tritt die Bedingung in (a) nicht auf, dann führe die Operation write_item(X )
durch und setzte write_TS(X )=TS(T )
2. Transaktion T erteilt eine read_item(X ) Operation:
(a) Wenn write_TS(X )>TS(T ), dann abort und rollback T und verwerfe die Operation. Dies sollte gemacht werden, weil eine jüngere Transaktion mit einem Timestamp grösser als T bereits das Element beschrieben hat, bevor T die Chance
hatte auf X zu schreiben. Dadurch wird das Timestamp Ordering ebenfalls verletzt.
(b) Wenn write_TS(X )≤TS(T ), dann führe die Operation read_item(X ) durch und
setzte read_TS(X ) zu dem grösseren der beiden Werte (TS(T ) oder read_TS(X )).
Also immer wenn der TO Algorithmus zwei Konflikt-Operationen die in der falschen Reihenfolge auftauchen detektiert wird die spätere der zwei Operationen durch den Abbruch der
Transaktion (abort) verworfen. Die verworfene Transaktionen wird dann neu an das System
übergeben und bekommt einen neuen Timestamp.
8.2.4
Multiversion Concurrency Control Techniken
Gewisse Concurrency Conrol Protokolle behalten die alten Werte von Daten wenn das Element updated wird, solche sind als Multiversion Concurrency Control bekannt. Wenn eine
Transaktionen einen Zugriff auf ein Element möchte, wird wenn mögliche eine passende Version gewählt um die Serialisierbarkeit der aktuellen Schedule aufrecht zu erhalten. Die Idee
besteht darin gewisse read Operationen, die in anderen Techniken (z.B. Timestamp Ordering) abgebrochen würden, trotzdem zu akzeptieren indem sie alte Versionen lesen können.
Ein Nachteil von Mutiversion Techniken ist offensichtlich der grössere Speicherbedarf, weil
alte Versionen gespeichert werden müssen. Jedoch müssen alte Versionen vielleicht sowieso
gehandhabt werden, z.B. für ein Recovery.
98
KAPITEL 8. TRANSAKTIONSMODELLE UND SYSTEME
Mutiversion Timestamping
Verschieden Versionen X1 , X2 , . . . , Xk des Datenbankelements X werden gehandhabt. Für jede
Version werden der Wert von Version Xi sowie folgende zwei Timestamps behalten:
1. read_TS(Xi ): Der Read Timestamp von Xi ist der grösste Timestamp unter allen Timestamps von Transaktionen, die erfolgreich die Version Xi gelesen haben.
2. write_TS(Xi ): Der Write Timestamp von X ist der grösste Timestamp unter allen
Timestamps von Transaktionen, die erfolgreich den Wert der Version Xi geschrieben
haben.
Wann immer einer Transaktion erlaubt ist eine write_item(X ) Operation auszuführen, wird
eine neue Version Xk+1 von X kreiert, mit write_TS(Xk+1 ) und read_TS(Xk+1 ) auf TS(T )
gesetzt. Wenn einer Transaktion erlaubt ist den Wert einer Version Xi zu lesen, wird der Wert
von read_TS(Xi ) auf den grösseren Timestamp Wert von dem aktuellen read_TS(Xi ) und
TS(T ) gesetzt.
Um die Serialisierbarkeit zu gewährleisten werden folgende Regeln benutzt:
1. Wenn Transaktion T eine write_item(X ) Operation erteilt und Version i von X den
höchsten write_TS(Xi ) von allen Versionen von X und dieser kleiner oder gleich TS(T )
und read_TS(Xi )>TS(T ) ist, dann abort und rollback die Transaktion T ; Andererseits,
wenn TS(T )=write_TS(Xi ) überschreibe den Wert von Version Xi , sonst kreiere eine
neue Version Xj von X mit read_TS(Xj )=write_TS(Xj )=TS(T ).
2. Wenn Transaktion T eine read_item(X ) Operation erteilt, finde die Version i von X ,
die den höchsten write_TS(Xi ) aller Versionen von X hat, aber kleiner oder gleich
TS(T ) ist; Dann gib den Wert von Xi an die Transaktion T zurück und setze den Wert
von read_TS(Xi ) auf den grösser Wert von TS(T ) und den aktuellen read_TS(Xi ).
Fall zwei ist wie man sieht immer erfolgreich, weil die passende Version Xi , basierend auf dem
write_TS der verschiedenen existierenden Versionen von X gelesen wird.
Beachte, dass wenn T zurück gerollt wird kann ein cascading rollback eintreten. Deshalb sollte,
um Recoverabilty zu gewährleisten, einer Transaktion nicht erlaubt sein zu commiten, bevor
nicht alle Transaktionen, die irgendwelche Versionen die T gelesen hat geschrieben haben
committed haben.
8.3
Locking Implementation
Locking kann einen bedeutenden Effekt auf die Performance in Transaktions Prozessing Systemen haben. Viel System offerieren deshalb Tuning Mechansimen an um die Performance
zu optimieren. Einige dieser Optimierungsmöglichkeiten können so weit gehen, dass sie die
Korrektheit verletzen. Es ist deshalb wichtig Locking zu verstehen um richtig einschätzen zu
können wann solche Optimierungen gemacht werden können, und was für Alternativen dazu
existieren.
Das Implementieren von Locking umfasst drei Aspekte:
• Das Setzen und Freigeben von Locks
• Das Implementieren eines Lock Managers
• Der Umgang mit Deadlocks
8.3.1
Lock Manager
Ein Lock Manager kann grundsätzlich drei Operationen ausführen:
1. Lock(transaction-id, data-item, lock-mode ): setzt für eine Transaktion transactionid einen Lock mit dem Modus lock-mode (read, write) auf das Datenbankelement dataitem.
8.4. MULTIGRANULARITY LOCKING
Daten
Element
X
Y
Z
99
Locks Hold
Pending Lock Requests
[tid1, read][tid2, read]
[tid2, write]
[tid,read]
[tid3, write]
[tid4, read][tid1,read]
Abbildung 8.6: Lock Tabelle
2. Unlock(transaction-id, data-iteme ): gibt den Lock von Transaktion transactionid auf das Datenbankelement data-item frei. item Unlock(transaction-id ): gibt alle
Locks der Transaktion transaction-id frei.
Die Locks können z.B. in einer Locktabelle im Arbeitsspeicher gespeichert werden, jeder Eintrag in der Tabelle beschreibt einen Lock auf ein Datenbankelement (siehe Abbildung 8.6).
Da im allgemeinen zur selben Zeit nur immer eine kleine Anzahl von Elementen gelockt sind,
kann zu Speicherung eine Hashtabelle mit dem Identifier der Datenelemente als Key benutzt
werde.
8.3.2
Lock Granularität
Höhere Ebenen des Data Managers wählen die Grösse der Datenbankelemente die gelockt werden. Man nennt dies auch die Locking Granularity. Ein Datenbankelement das man Locken
kann, kann z.B. ein Datenbank Record, ein einzelnes Feld eines Datenbank Records, ein ganzer
Diskblock, ein File oder sogar die ganze Datenbank sein. Verschiedene Tradeoffs muss man
beachten, wenn man Datenelement Grösse bestimmt. Je grösser die Datenelement Grösse, je
kleiner ist der Grad an erlaubter Concurrency, dafür hat der Daten Manager wenig Overhead.
Auf der anderen Seite, je kleiner Granularität ist, umso höher ist der Grad an Concurrency,
dafür müssen viele Locking Informationen gespeichert und verwaltet werden, was zu einem
grossen Overhead führt.
Was die beste Granularitätsgrösse ist, hängt vom den involvierten Transaktionen ab. Wenn
eine typische Transaktion auf eine kleine Anzahl von Records zugreift, ist es vorteilhaft die
Granularität klein zu halten, z.B. ein einzelnes Record. Wenn auf der anderen Seite eine typische Transaktionen aber auf viele Records im selben File zugreift ist es vielleicht besser Block
oder File Granularität zu haben, so dass die Transaktion all diese Records als ein (oder wenige) Datenelemente betrachtet. Es ist üblich auf die Kompromisse beim Locking auf Page und
File Granularität einzugehen, wenn hohe Transaktions Raten nicht erforderlich sind und/oder
wie erwähnt die Transaktionen auf viele Record pro Page zugreift, weil es die Implementierung eines Data Managers und den Recovery Algorithmus vereinfacht. High-Performance TP
Systeme erfordern aber ein Locking auf Record Granularität.
8.4
Multigranularity Locking
Da die beste Granularitätsgrösse von den gegebenen Transaktionen abhängt, scheint es angebracht, dass ein DBMS verschiedene Granularitätslevel unterstützt, die für verschiedene
Transaktionen unterschiedlich sind. Eine einfache Granularitätshierarchie zeigt z.B. Abbildung 8.7. Für Transaktionen die auf grosse Mengen von Daten (lange Transaktionen) zugreifen,
lockt der Data Manager grosse Einheiten (Coarse granularity), z.B. Files, Tabellen, während
er für andere Transaktionen (kurze) eine kleinere Granularität (fine Granularity) wählt. Der
Manager muss fähig sein widerstreitend Locks auf verschiedenen Granularitätsstufen zu erkennen. Um dies zu ermöglichen werden weitere Typen, Intention Locks genannt, gebraucht.
Es gibt drei Typen von Intention Locks:
1. Intention-Read (IR) (oder Intention-Shared (IS)): Indiziert, dass für Nachfolgerknoten
(descendant node(s) ein Shared-Lock beantragt wird.
100
KAPITEL 8. TRANSAKTIONSMODELLE UND SYSTEME
DB 1
Disk Area 1
......
File 1.1
Record 1.1.1
....
Disk Area 2
Record 1.1.i
File 1.n
Record 1.n.1
....
......
File 2.1
Record 1.n.j
Record 2.1.1
....
Record 2.1.i
File 2.m
Record 2.m.1
....
Record 2.m.j
Abbildung 8.7: Eine Beispiel für einen Garanularitätshierarchie Baum
R
W
IR
IW
RIW
R
ja
nein
ja
nein
nein
W
nein
nein
nein
nein
nein
IR
ja
nein
ja
ja
ja
IW
nein
nein
ja
ja
nein
RIW
nein
nein
ja
nein
nein
Tabelle 8.1: Kompatibilitäts Matrix für Multigranularity Locking
2. Intention-Write (IW) (oder Intention-Exclusiv (IX)): Indiziert, dass für Nachfolgerknoten (descendant node(s) ein Exclusive-Lock beantragt wird.
3. Read-Intention-Write (RIW) (oder Shared-Intention-Exclusive (SIX)): Indiziert, dass
der aktuelle Knote im Shared Mode gelockt ist, aber ein Exclusive-Lock wird für Nachfolger beantragt.
Ein Lock auf eine grobes Granulat x lockt explizit x und lock implizit alle Nachfolger von x.
Bevor eine Transaktionen ein Read oder Write Lock auf ein x setzen kann, muss sie zuerst
Intention Locks für alle Vorgänger anfordern. Das Setzen eines Intention Locks auf allen Vorgängern vo x stellt sicher, dass kein Write Lock implizit auf x einen Write Lock setzt.
Die Kompatibiliätstabelle der drei Intention und der andere zwei Locks zeigt Tabelle 8.1. Das
Multiple Granularity Locking (MGL) Protokol besteht aus folgenden Regeln:
1. Die Lock Compatibilität (Tabelle 8.1) muss eingehalten werden.
2. Der Root Node des Baumes muss in jedem Modus zuerst gelockt werden.
3. Ein Node N kann durch eine Transaktion T nur im R oder IR Modus gelockt werden,
wenn der Parent Node bereits im IR oder IW Modus gelockt ist.
4. Ein Node N kann durch eine Transaktion T nur im W, IW oder RIW Modus gelockt
werden, wenn der Parent Node bereits im IW oder RIW Modus gelockt ist. Ein Lock
auf N ist ein expliziter Lock für N , ein Lock auf eine Vorgänger von N ist ein impliziter
Lock für N
5. Um einen Node N zu lesen/beschreiben, muss T einen read/write Lock auf einem seiner
Vorfahren besitzen.
6. Eine Transaktion T kann einen Node nur locken, wenn sie nicht bereits einen Node vom
Lock befreit hat (2PL Protokoll)
7. Eine Transaktionen T kann einen Lock nur dann von einem Node N wegnehmen, wenn
keines der Children von N gerade von T gelockt ist.
MGL sichert nicht Serialisierbarkeit und muss deshalb in Verbindung mit 2PL benutzt werde.
2PL gibt die Regeln wann ein Lock zu setzen oder freizugeben ist und MGL spezifiziert wie
die Locks für Datenelemente verschiedener Granularität gesetzt und freigegeben werden. Die
Grundoperationen des Lock Managers sind wie zuvor:
8.5. DEADLOCK
requested
lock type
101
IR
IW
R
RIW
W
current lock type
IR
IW
R
IR
IW
R
IW
IW
IW
R
RIW
R
RIW RIW RIW
W
W
W
RIW
RIW
RIW
RIW
RIW
W
W
W
W
W
W
W
Tabelle 8.2: Lock Conversion
• Service verlangen einen Lock zu setzen oder freizugeben
• bei einem Lock Request, prüfe auf Konflikte
• wenn kein Konflikt vorhanden ist, setze den Lock
• wenn ein Konflikt vorhanden ist, wird die anfragende Transaktion blockiert, bis entweder der Lockrequest gewährt werden kann oder ein Deadlock zu einem Abbruch der
Transaktion zwingt (rejected und abort)
Um den Granularitätslevel für eine Transaktione anpassen zu können, kann ein Daten Manager escalation benutzen, d.h. er detektiert dynamisch wenn eine Transaktionen viele Lock
verlangt und entscheidet einen Intention Lock zu setzen. Das Transaktionen Locking Verhalten
kann zu lock escalation führen was grössere (coarser ) Granularität einführt. In gewissen Applikationen haben lock escalations grosse Wahrscheinlichkeiten in einen Deadlock zu führen,
weil es nötig ist Lock zu konvertieren. Wie Locks konvertiert werden müssen zeigt Tabelle 8.2.
Es kann die Möglichkeit geben eine Transaktion abzubrechen, wenn das Locking Verhalten als
zu fein granuliert detektiert wird. Eine Analyse während der Kompilation des Transaktions
Programms kann vielleicht dabei helfen eine passende Granularitätsstufe zu finden.
Man kann die Baumstruktur des Lock Type Graphen auch auf einen direkten azyklischen
Graphen (DAG) verallgemeinern, vorallem wenn man auch Indices in Betracht zieht. Da ein
Knoten jetzt mehrere Parent haben kann, müssen das MGL Protokoll angepasst werde:
1. Die Lock Kompatibilität (Tabelle 8.1) muss eingehalten werden.
2. Der Root Node des Baumes muss in jedem Modus zuerst gelockt werden.
3. Ein Node N kann durch eine Transaktion T nur im R oder IR Modus gelockt werden,
wenn einer seiner Parent Nodes bereits im IR oder IW Modus gelockt ist.
4. Ein Node N kann durch eine Transaktion T nur im W, IW oder RIW Modus gelockt
werden, wenn alle seiner Parent Nodes bereits im IW oder RIW Modus gelockt ist.
5. Um einen Node N zu lesen, muss T einen read oder write Lock auf einem Vorfahren
besitzen. Um auf N zu schreiben, muss T für jeden Pfad von der Wurzel des Graphen
bis zu N einen write Lock auf einem Vorgänger von N entlang dieses Pfades haben.
6. Eine Transaktion T kann einen Node nur locken, wenn sie nicht bereits einen Node vom
Lock befreit hat (2PL Protokoll)
7. Eine Transaktionen T kann einen Lock nur dann von einem Node N wegnehmen, wenn
keines der Children von N gerade von T gelockt ist.
8.5
Deadlock
Wenn zwei oder mehr Transaktionen blockiert sind, weil sie aufeinander warten bis ein Lock
freigegeben wird, spricht man von einer Deadlock Situation. Ein Beispiel zeigt Abbildung 8.8.
Deadlock bedeuten in 2PL nicht Serialisierbarkeit. Um einen Deadlock auflösen zu können
muss mindesten eine der beteiligten Transaktionen abgebrochen werden. Deadlock sind aber
äusserst selten (∼< 1% der Transaktionen), trotzdem sollten sie wenn möglich verhindert oder
zumindest detektiert und behandelt werden können.
102
KAPITEL 8. TRANSAKTIONSMODELLE UND SYSTEME
T1
data item
x
y
Locks held
[T1 , read]
[T2 , read]
T2
locks requested
[T2 , write]
[T1 , write]
Abbildung 8.8: Deadlock Situation mit korrespondierendem waits-for Graph
In Betriebssystemen können Deadlock verhindert werden, indem man niemals Locks erlaubt
die zu einem Deadlock führen könnten. In einem TP System könnte dies erreicht werden, indem
Transaktionen dazu gezwungen werden alle ihre Locks zu verlangen bevor die Ausführung
startet, dies ist aber für Transaction Processing zu restriktiv. Allgemein kann man sagen,
dass Deadlock Prävention in TP Systemen entweder die Concurrency begrenzt oder die Art
Transaktionen zu Programmieren begrenzt. Die meisten Systeme beschränken sich deshalb
auf Deadlock Detektion und Recovery anstelle von Prävention.
8.5.1
Deadlock Detection
Es gibt zwei Grundsätzliche Vorgehensweisen Deadlock zu detektieren:
1. Timeouts: Es wird eine Timeout Periode länger als die Typische Ausführungszeit einer
Transaktion verwendet um vermeintliche Deadlock zu detektieren. Diese Methode ist
einfach und leicht zu Implementieren und funktioniert auch in einem verteilten System,
aber kann dazu führen, dass Transaktionen unnötigerweise abgebrochen werden und
erlaubt vielleicht einem Deadlock unnötig lange zu bestehen.
2. Graph basierte Detektion: Ein transaction waits-for Graph wird periodisch auf
Deadlocks überprüft (Zyklus) im Graph. Diese Methode muss für verteilte Deadlock
angepasst werden, indem z.B. ein Node (im Netzwerk) für die Detektion von Deadlocks verantwortlich ist und die anderen Nodes im periodisch eine Kopie ihrer waitsfor Graphen schicken.
Wie bereits erwähnt können Lock Konvertierungen zu Deadlocks führen. Ein Deadlock kann
z.B. auftreten, wenn zwei Transaktionen einen Read Lock auf Datenelement x haben und
beide einen Write Lock auf x möchten. Solche Konvertierungen von Read zu Write Locks
sind recht häufig und sie können verhindert werden, indem Transaktionen bereits beim ersten
Zugriff auf ein Datenelement einen Write Lock verlangen. Damit der Daten Manager erkennen
kann ob er besser ein Write Lock anstelle eines Read Locks setzt, muss die Transaktion dem
Datenmanager beim Lesen von x einen Hinweis geben. Transaktionen geben ihre Anfragen in
höheren Sprachen aus und so kann die Lock Konvertierung angepasst werden (z.B. update in
SQL).
8.6
8.6.1
Performance Issues und Bottlenecks
Lock Trashing
Das Blockieren von Transaktionen durch das Warten auf Lock Anfragen, kann die Performance eines TP System beträchtlich beeinträchtigen. Wenn der Transaktions Load steigt, kann
er einen Punkt erreichen indem eine grosse Anzahl Transaktionen blockiert werden und der
throughput fällt. An diesem Punkt spricht man von Lock Trashing.
Designer von Daten Managern, Datenbanken und Applikationen benutzen verschiedene Techniken um das Blocken von Transaktionen zu minimieren. Wenn man annimmt, dass eine
Transaktion einen Write Lock L für t Sekunden hält, dann ist die maximale Transaktionsrate
für Transaktionen die L setzen 1/t (eine Transaktion pro t Sekunden). Die meisten Techniken
versuchen diese Zeit, die eine Transaktionen einen Lock hält zu reduzieren. Man kann z.B.
die Applikation so anpassen, dass sie Locks später in der Transaktionsausführung setzt, z.B.
8.6. PERFORMANCE ISSUES UND BOTTLENECKS
103
indem Updates bis zum Commit in lokalen Variablen gehalten werden. Eine andere Möglichkeit besteht darin, die Ausführungszeit von Transaktionen zu reduzieren, indem die Anzahl
Instruktionen reduziert werden, Daten effektiv gebuffert werden um Disk Zugriffe zu vermindern und/oder indem die Benutzung von Ressourcen wie Kommunikation optimiert werden.
8.6.2
Hot Spots
Die Lock Granularität beeinflusst die Performance, Konflikte können durch feiner-granulare
Locks reduziert werden. Durch gutes Datenbank Design kann die Performance gesteigert werden. Nehmen wir an, dass ein File mit Records einige stark frequentierte Felder, so genannte
“hot spots”, und einige kaum besuchte Felder (“cold spots”) hat, dann könnte man das File vertikal teilen und hot spots in dem einen File und cold spots in dem anderen Speichern, dadurch
werden Transaktionen die auf cold spots zugreifen nicht mehr länger durch Transaktionen die
auf hot spots zugreifen blockiert. Auf gewisse Datenelemente wird so oft zugegriffen (selbst
auf kleiner Granularitäts Stufe), dass sie zu einem Flaschenhals werden. Solche Elemente sind
als Hot Spots bekannt und beinhalten typischerweise:
- Summary Informationen, wie z.B. der Geldbetrag in einer Bank Zweigstelle
- end-of-file Marker in Files, die als Datenbank Entry benutzt werden.
- die nächste Seriennummer die sequentiell vergeben wird, wie z.B. Bestellnummer oder
Transaktionsnummer
Um Hot Spot Flaschenhälse zu entlasten, können z.B. “Heisse” Daten im Arbeitsspeicher
gehalten werden, Operationen auf Hot Spots bis kurz vor den Commit hinausgezögert werden,
Read Operationen durch Verifikationen ersetzt werden die bis zum Commit herausgezogen
werden oder Operationen in Private Batches Gruppiert werden, die periodisch zum Einsatz
gelangen.
8.6.3
Query Update Problem
Queries die Lesezugriff auf viele Daten benötigen (z.B. für Reporte oder Entscheidungs Unterstützung) können auch zu Concurrency Botlenecks führen, weil sie z.B. im 2PL viele Locks
setzen und diese für lange Zeit halten. Viele Systeme geben die Serialisierbarkeit von Queries auf und benutzen schwächere Regeln für Queries als 2PL. Man Unterscheidet in solchen
Systemen drei Isolationsgrade (siehe Tabelle 8.3):
• Cursor Stability (2. Isolationsgrad): Der Daten Manager hält nur einen Read
Lock währen der Zeit in der die Query tatsächlich die Daten liest und gibt
den Lock frei sobald alle Daten gelesen sind. Es wird garantiert, dass die
Query nur Daten liest die von commited Transaktionen produziert wurden,
sichert aber nicht sie Serialisierbarkeit.
• Dirty Reads (1. Isolationsgrad: Queries können ebenfalls Daten die noch uncommited sind lesen.
Update Transaktionen bleiben immer noch Serialisierbar, wenn sie 2PL benutzen. Viele DBMS
bieten Optionen um Transaktionen in verschiedenen Isolationsgraden auszuführen, was die
Performance steigern, aber auch falsche Resultate liefern kann. Die vier Isolationsgrade die in
SQL unterstütze werden zeigt Abbildung 8.9.
104
KAPITEL 8. TRANSAKTIONSMODELLE UND SYSTEME
Degree of Isolation
1
2
3
Technical Term
Dirty Reads
Cursor Stability
Repeatable Reads
Behaviour
Setze keine Read Locks
Lese nur commited Daten
serialisierbar
Tabelle 8.3: Die drei Isolationsgrade in DBMS
SET
SET
SET
SET
TRANSACTION
TRANSACTION
TRANSACTION
TRANSACTION
ISOLATION
ISOLATION
ISOLATION
ISOLATION
LEVEL
LEVEL
LEVEL
LEVEL
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
Dirty Reads
Cursor Stability
(siehe den Kommentar unten †)
Default
† Wenn man eine Query zweimal ausführt, kriegt man bei der zweiten
Ausführung mindestens die selben Daten wie bei er ersten, aber vielleicht auch
mehr. (Superset von 1. Query Ausführung)
Für jeden Level ist der Default, dass die Transaktion READ WRITE ist, man kann
aber für jeden Level READ ONLY spezifizieren.
Abbildung 8.9: SQL Isolations Levels
Literaturverzeichnis
[1] S. Abiteboul, D. Quass, J. McHugh, J. Widom, J. Wiener: “The Lorel query language
for semistructured data”,
Journal of Digital Libraries, November 1996,
(http://www-db.stanford.edu/lore/pubs/lorel96.pdf)
[2] Alfred V. Aho, Ravi Sethi, Jeffrex D. Ullman: “Compilers Principles, Techniques,
and Tools”, Pearson Education (Singapore) Pte. Ltd., Indian Branche, Delhi, India,
Fifteenth Indian Reprint (2004), ISBN 81-7808-046-X
[3] S. Alagic: “The ODMG object model: does it make sense?”,
Proceeding of the 12th ACM SIGPLAN conference on Object-oriented programming,
system, languages, and applications, Volume32 Issue10, (1997), Atlanta, Georgia, United
States, Pages: 253-270,
(http://doi.acm.org/10.1145/263698.2637461 )
[4] A. Albano, L. Cardelli, R. Orsini: “Galileo: A Strongly Typed, Interactive Conceptual Language”, (1985)
(http://www.di.unipi.it/˜albano/galileo/documentation/Galileo.pdf)
[5] A. Albano, G. Antognoni, G. Baratti, G. Ghelli, R. Orsini: “Galileo97 Reference Manual. Version 2.0”,
September 1998,
(http://www.di.unipi.it/˜albano/galileo/documentation/index.html)
[6] T. Lougenia Anderson, Earl F. Ecklund Jr., David Maier: “PROTEUS: objectifying
the DBMS user interface”,
Proceedings on the 1986 international workshop on Object-oriented database systems,
Pacific Grove, California, United States, Pages: 133 - 145, (1986), IEEE Computer Society
Press, Los Alamitos, CA, USA, ISBN: 0-8186-0734-3
[7] M. P. Atkinson, P. J. Bailey, K. J. Chisholm, W. P. Cockshott, R.’Morrison: “An Approach to Persistent Programming”,
computer Journal 26, 4 (1983) pp 360-365,
(http://www.dcs.st-and.ac.uk/research/ publications/download/ABC+83a.pdf)
[8] M. P. Atkinson, P. J. Bailey, K. J. Chisholm, W. P. Cockshott, R.’Morrison: “PS-algol:
A Language for Persistent Programming”,
In Proc. 10th Australian National Computer Conference, Melbourne, Australia (1983)
pp 70-79,
(http://www.dcs.st-and.ac.uk/research/publications/download/ABC+83b.pdf)
[9] Malcolm P. Atkinson, O. Peter Buneman : “Types and persistence in database
programming languages”,
ACM Computing Surveys (CSUR), Volume 19 , Issue 2 (June 1987), Pages: 105 - 170,
(http://doi.acm.org/10.1145/62070.45066)
[10] Malcolm P. Atkinson,François Bancilhon, David DeWitt, Klaus Dittrich, David Maier,
Stanley Zdonik: “The Object-Oriented Database System Manifesto”,
Proceedings of the First International Conference on Deductive and Object-Oriented
Databases, Kyoto, Japan, Pages: 223-240, 1989,
(http://www.cl.cam.ac.uk/Teaching/2003/Databases/oo-manifesto.pdf)
1 die PDFs, welche bei ACM zu finden sind, können nur geöffnet oder abgespeichert werden,
wenn man sich registriert hat, oder wenn man mit einer ETH IP darauf zugreifft
105
106
LITERATURVERZEICHNIS
[11] Malcolm P. Atkinson: “Orthogonally persistent object systems”,
The International Journal on Very Large Data Bases, Volume 4 , Issue 3 (July 1995),
Pages: 319 - 402
[12] M. P. Atkinson, L. Daynes, M. Jordan, T. Printezis, S. Spence: “An Orthogonally
Persistent JavaTM ”,
ACM SIGMOD Record, Volume 25, Number 4, December 1996,
(http://research.sun.com/forest/COM.Sun.Labs.Forest.doc.sigmod 96.paper pdf.pdf)
[13] Philip A. Bernstein: “Transaction processing monitors”,
Communications of the ACM, Volume 33, Issue 11, 1990, Pages: 75 - 86
(http://doi.acm.org/10.1145/92755.92767)
[14] Sergey Brin, Lawrence Page: “The anatomy of a large-scale hypertextual Web
search engine”,
Proceedings of the 7th international conference on World Wide Web, Brisbane, Australia,
1998, Pages: 107 - 117
(http://www-db.stanford.edu/pub/papers/google.pdf)
[15] Rick Cattell: “Experience with the ODMG standard”,
StandardView, Volume 3, Issue 3 (September 1995), Pages: 90 - 95, ACM Press,
(http://doi.acm.org/10.1145/226191.226197)
[16] D. Chamberlin: “XQuery: An XML query language”,
IBM SYSTEMS JOURNAL, VOL 41, No 4, 2002,
(http://www.research.ibm.com/journal/sj/414/chamberlin.pdf)
[17] R. L. Cooper, M. P. Atkinson, A. Dearle, D. Abderrahmane: “Constructing Database
Systems in a Persitent Enviroment,
Proceedings of the 13th VLDB Conference, Brighton 1987,
(http://www.vldb.org/conf/1987/P117.PDF)
[18] Devanshu Dhyani, Wee Keong Ng, Sourav S. Bhowmick: “A survey of Web metrics”,
ACM Computing Surveys, Volume 34, Issue 4 (December 2002), Pages: 469 - 503
(http://doi.acm.org/10.1145/592642.592645)
[19] Beverly A. Dutton, Kathryn C. Kinsley: “Design and implementation of RAQUEL
II: a relational query language”,
Proceedings of the 16th annual Southeast regional conference, Atlanta, Georgia, Pages:
327 - 333,
(http://doi.acm.org/10.1145/503643.503714)
[20] Andrew Eisenberg, Jim Melton: “SQL/XML and the SQLX Informal Group of
Companies”,
(http://www.acm.org/sigmod/record/issues/0109/standards.pdf)
[21] Andrew Eisenberg, Jim Melton: “SQL/XML is Making Good Progress”,
(http://www.acm.org/sigmod/record/issues/0206/standard.pdf)
[22] Ramez Elmasri, Shamkant B. Navathe: “Fundamentals of DATABASE SYSTEMS”,
Pearson Education (Singapore) Pte. Ltd., Indian Branche, Delhi, India, Fourth Edition,
First Indian Reprint (2004), ISBN 81-297-0228-2
[23] David Flanagan, Jim Farley, William Crawford: “Java Enterprise in a Nutshell”,
O’Reilly, second edition, 2002, ISBN 0-596-00152-5
[24] IBM: “Overview of DB2’s XML Capabilities: An introduction to SQL/XML
functions in DB2 UDB and the DB2 XML Extender”
(http://www-106.ibm.com/developerworks/db2/library/techarticle/dm0311wong/index.html)
[25] IBM Internal Report on the Contents of a Sample of Programs Surveyed,
San Jose, CA, 1978
[26] Mick Jordan: “Early Experiences with Persistent JavaTM ”, The First International
Workshop on Persistence and Java (PJ1), Glasgow, Scotland, 16-18 September 1996.
(http://research.sun.com/forest/COM.Sun.Labs.Forest.doc.eepjava.paper pdf.pdf)
[27] Mick Jordan, M. P. Atkinson: “Orthogonal Persistence for JavaTM — A Mid–term
Report”,
Third International Workshop on Persistence and Java, Tiburon, CA, Sep 1-3, 1998,
(http://research.sun.com/forest/COM.Sun.Labs.Forest.doc.opjmidterm.paper pdf.pdf)
LITERATURVERZEICHNIS
107
[28] Donald Kossmann: “The state of the art in distributed query processing”,
ACM Computing Surveys (CSUR), Volume 32, Issue 4 (December 2000), Pages: 422 469,
(http://doi.acm.org/10.1145/371578.371598)
[29] Donald Kossmann, Konrad Stocker: “Iterative dynamic programming: a new class
of query optimization algorithms”,
ACM Transactions on Database Systems, Volume 25, Issue 1, March 2000, Pages: 43 82
(http://doi.acm.org/10.1145/352958.352982)
[30] Charles Lamb, Gordon Landis, Jack Orenstein, Dan Weinreb: “The ObjectStore database system”,
Communications of the ACM archive, Volume 34, Issue 10 (October 1991), Pages: 50 63,
(http://doi.acm.org/10.1145/125223.125244)
[31] C. Lecluse, P. Richard, F. Velez: “O2, an object-oriented data model”,
Proceedings of the 1988 ACM SIGMOD international conference on Management of data,
Chicago, Illinois, United States, Pages: 424 - 433,
(http://doi.acm.org/10.1145/50202.50253)
[32] Y. E. Lien: “Design and implementation of a relational database on a minicomputer”,
Proceedings of the 1977 annual conference, Pages: 16 - 22
[33] F. Matthes, J. W. Schmidt: “Definition of the Tycoon Language Tl – A Preliminary Report”,
Informatik Fachbericht FBI-HH-B-160/92, Fachbereich Informatik, Universität Hamburg,
Germany, October 1992. (Revised 17-aug-1995),
(http://www.sts.tu-harburg.de/papers/1992/MaSc92/paper.pdf)
[34] F. Matthes, S. Müssig, J. W. Schmidt: “Persistent Polymorphic Programming in
Tycoon: An Introduction”,
FIDE Technical Report Series FIDE/94/106, FIDE Project Coordinator, Department of
Computing Sciences, University of Glasgow, Glasgow G128QQ, August 1994,
(http://www.sts.tu-harburg.de/papers/1994/MMS94/paper.pdf)
[35] David C. J. Matthews: “Poly Manual”,
ACM SIGPLAN Notice, Volume 20, Issue 9 (August 1985), Pages: 52-76),
(http://doi.acm.org/10.1145/988364.988371)
[36] B. Mathiske, F. Matthes, S. Müssig: “The Tycoon System and Library Manual”,
DBIS Tycoon Report 212-93, Fachbereich Informatik, Universität Hamburg, Germany,
December 1993. (Revised 19-jan-1996),
(http://www.sts.tu-harburg.de/papers/1993/MMM93/paper.pdf)
[37] Jason McHugh, Serge Abiteboul, Roy Goldman, Dallan Quass, Jennifer Widom: “Lore:
A Database Management System for Semistructured Data”,
SIGMOND Record, 1997,
(http://citeseer.ist.psu.edu/mchugh97lore.html)
[38] Ron Morrison, Fred Brown, Richard Connor, Al Dearle: “The Napier88 Reference
Manual”,
A. Universities of Glasgow and St Andrews Report PPRR-77-89, (1989),
(http://www.dcs.st-and.ac.uk/research/publications/download/MBC+89a.pdf)
[39] NeoCore White Paper: “What is NeoCore XML Management System?”,
(NeoCore Inc, Release 1.0 June 29, 2001, http://www.dmreview.com/whitepaper/wid327.pdf)
[40] “PSE Pro for Java Tutorial”,
PSE Pro for Java Release 6.1 Service Pack 2 for all platforms, March 2004, Progress
Software Corporation
[41] Joel E. Richardson, Michael J. Carey, Daniel T. Carey: “The design of the E programming language”,
ACM Transactions on Programming Languages and Systems (TOPLAS), Volume 15 ,
Issue 3 (July 1993), Pages: 494-534,
(http://doi.acm.org/10.1145/169683.174157)
108
LITERATURVERZEICHNIS
[42] David W. Shipman: “The functional data model and the data language
DAPLEX”,
Proceedings of the 1979 ACM SIGMOD international conference on Management of data, Boston, Massachusetts, Pages: 59 - 59, (1979),
(http://doi.acm.org/10.1145/582095.582105)
[43] A. R. Schmidt, M. L. Kersten, M. A. Windhouwer, F. Waas:“Efficient Relational
Storage and Retrieval of XML Documents”,
International Workshop on the Web and Databases (In conjunction with ACM SIGMOD),
Dallas, TX, USA, May 2000, Pages: 47-52,
(http://www.cwi.nl/themes/ins1/publications/docs/ScKeWiWa:WEBDB:00.pdf)
[44] Joachim W. Schmidt: “Query processing strategies in the PASCAL/R relational
database management system”,
Proceedings of the 1982 ACM SIGMOD international conference on Management of data,
Orlando, Florida, Pages: 256-264,
(http://doi.acm.org/10.1145/582353.582402)
[45] Sun microsystems: “JDBCTM API Documentation”,
(http://java.sun.com/j2se/1.4.2/docs/guide/jdbc/index.html)
[46] Christian Ullenboom: “Java ist auch eine Insel”,
Galileo Computing <openbook>
(www.galileocomputing.de/openbook/javainsel4/)
[47] Michael L. Van de Vanter: “PJama: Orthogonal
JavaTM Platform”,
The Forest project, sun Microsystems Laboratories, 1999,
(http://www.jugs.ch/html/events/1999/persistence.pdf)
persistence
for
the
[48] Andrew E. Wade: “Object query standards”,
ACM SIGMOD Record archive, Volume 25 , Issue 1 (March 1996), Pages: 87 - 92,
(http://doi.acm.org/10.1145/381854.381895)
[49] W3C: Scott Boag, Don Chamberlin, Mary Ferndandez, Daniela Florescu, Jonathan Robie,
Jérôme Siméon:“XQuery 1.0: An XML Query Language”,
W3C Working Draft 29 October 2004,
(http://www.w3.org/TR/xquery/)
[50] W3C: James Clark, Steve DeRose: “XML Path Language (XPath) Version 1.0”,
W3C Recommendation 16 November 1999,
(http://www.w3.org/TR/xpath)
[51] W3C: Mary Ferndandez, Ashok Malhotra, Jonathan Marsh, Norman Walsh: “XQuery
1.0 and XPath 2.0 Data Model”, W3C Working Draft 29 October 2004,
(http://www.w3.org/TR/xpath-datamodel/)
[52] W3school: Online DTD Tutorial,
(http://www.w3schools.com/dtd)
R
[53] Xpriori Technical Paper: “A Description of NeoCoreXMS
versus Traditional
Database Management Systems”,
Xpriori, LLC, Release 1.0, 11/8/2004,
(http://www.xpriori.com/library/White Paper-XMS vs RDBMS.pdf)
Herunterladen