Informationssysteme IS-K WS 2003/2004 ¨Ubung 8 Beispiellösung

Werbung
Institut für Informationssysteme
Prof. Dr. H.-J. Schek
F. Akal, K. Jauslin
K. Haller, M. Mlivoncic
Eidgenössische Technische Hochschule Zürich
Swiss Federal Institute of Technology Zurich
Informationssysteme IS-K WS 2003/2004
Übung 8
Beispiellösung
Aufgabe 1: Servermodelle
In den Teilaufgaben a) – d) sind vier Anwendungsbeispiele für den Einsatz von Datenbanken gegeben.
Überlegen Sie, welche der in der Vorlesung vorgestellten Servermodelle im jeweiligen Fall verwendet werden
sollten.
a) Ein Softwaresystem für betriebswirtschaftliche Anwendungen soll so gestaltet werden, dass eine möglichst
grosse Anzahl von Benutzern effizient unterstützt wird. Für die Datenhaltung sind dabei sehr leistungsstarke Rechner vorgesehen, die aber nicht noch zusätzlich durch Anwendungsfunktionalität belastet
werden sollen. Die Benutzer sollen mit Hilfe ihres PCs am Arbeitsplatz Zugriff auf das gesamte System bekommen. Diese PCs sind dabei lediglich für die Realisierung der grafischen Benutzeroberfläche
zuständig, aber ebenfalls nicht für die Anwendungsfunktionalität.
In diesem Falle bietet sich eine Drei–Stufen–Architektur an. Auf der ersten Stufe (Präsentationsdienste)
befinden sich dabei die PCs an den Arbeitsplätzen als Präsentationsserver. Die zweite Stufe (Anwendungsdienste) besteht aus mehreren Applikationsservern. Damit können die einzelnen Anwendungs–Module auf
diese Server verteilt werden. Auf der dritten Stufe (Datenbankdienste) schliesslich befinden sich die Datenbankserver. So wird gewährleistet, dass Datenhaltung, Anwendungsfunktionalität und die Realisierung
der grafischen Oberfläche voneinander getrennt werden können. Im Falle eines einzigen, zentralen Datenbankservers entspricht diese Architektur beispielsweise dem Software–System SAP R/3, das eine Vielzahl
von betriebswirtschaftlichen und organisatorischen Aufgaben innerhalb eines Unternehmens abdeckt.
b) Ein Weinliebhaber möchte seinen Weinkeller (ca. 300 Flaschen) mit Hilfe einer Datenbank verwalten,
damit er jederzeit über den aktuellen Bestand informiert ist und rechtzeitig neue Bestellungen aufgeben
kann.
Für die Weinkeller-Datenbank, bei der lediglich ein Benutzer auf eine nicht allzu grosse Datenmenge
zugreift, ist eine Ein–Stufen–Architektur eines Single User DBMS die geeignete Architekturform. Als
mögliches Beispiel hierfür kann u.a. MS Access genannt werden.
c) In einem Bibliotheks–Informationssystem stehen den Kunden im Lesesaal mehrere, schon recht veraltete
PCs zur Verfügung, um Literatur-Recherchen auf der Bibliotheks-Datenbank durchzuführen. Wo sollten
in diesem Fall sinnvollerweise die Benutzer–Anfragen ausgeführt werden?
In diesem Fall bietet sich eine Zwei–Stufen–Architektur, bei der die PCs als Clients mit dem Datenbank–
Server verbunden sind, an. Da die Server–Prozesse verschiedener Clients nicht notwendigerweise voneinander geschützt sein müssen, ist eine symmetrische Multiple–Server–Architektur zwar möglich, jedoch
nicht unbedingt nötig. Ebenfalls geeignet sind monolithische, multithreaded Server–Architekturen, da
die Gefahr fehlerhafter Clients bei einfachen Anfragen nicht sonderlich gross ist. Eine asymmetrische
Multiple–Server–Architektur scheint jedoch hier am besten geeignet, da der Overhead durch die Verwaltung der Warteschlangen bei einfachen, kurzen Anfragen und wenigen Benutzern nicht allzu stark ins
Gewicht fällt.
In diesem Szenario, bei dem sehr einfache und langsame Clients mit einem leistungsfähigen Server
verbunden sind, bietet es sich an, die gesamte Anfrageauswertung auf dem Server durchzuführen, und
die Clients nur zur Realisierung der grafischen Oberfläche zu verwenden.
d) In einer grossen Bank müssen die täglich anfallenden Daten transaktionssicher und effizient in eine
Datenbank eingefügt werden bzw. Anfragen effizient beantwortet werden. Insbesondere soll dabei eine
hohe Verfügbarkeit des Systems garantiert werden (z.B. darf natürlich eine Fehlbedienung an einem
Terminal nicht den kompletten Betrieb beeinträchtigen).
Für die Tagesgeschäfte der Bank eignet sich beispielsweise ein grosser Mainframe–Rechner (Ein–Stufen–
Architektur, Multi User DBMS), auf dem mehrere Prozesse parallel ablaufen können. Eine solche Architektur besitzt den Vorteil, dass ein grosses Mass an Betriebssicherheit gewährleistet werden kann. Da
1
sämtliche Dienste zentral an einem Ort definiert sind, lässt sich so der physische Zugang zum System
einfach kontrollieren.
Allerdings besteht auch die Möglichkeit, eine Zwei–Stufen–Architektur anzuwenden. Aus Sicherheitsgründen sollte dies jedoch keine Multithreaded Server–Architektur sein, da hier u.U. die geforderte hohe
Verfügbarkeit nicht garantiert werden kann (Ausfall eines Clients kann gesamtes System lahmlegen).
Da dies bei einer symmetrischen Multiple–Server–Architektur nicht der Fall ist, kann hier eine grössere
Verfügbarkeit realisiert werden. Auch die asymmetrische Multiple–Server–Architektur, die eine geschützte Ausführung der Clients ermöglicht, eignet sich für diese Anforderungen.
Aufgabe 2: Client-Server Architektur von Datenbanksystemen
/RJ
:ULWHU
3URFHVV
$3
&OLHQW
DA 1
6HUYHU
3URFHVV
'DWDEDVH%XIIHU
&/
%HWULHEVV\VWHP
%HWULHEVV\VWHPQ
1HW]ZHUN
Abbildung 1: Dedicated Server Architektur
a) In einer Datenbank existiere eine Tabelle gemäss folgender DDL:
CREATE TABLE MY_TERMS
(
ident NUMBER
NOT NULL,
term VARCHAR2(50)
NOT NULL,
PRIMARY KEY (ident, term),
UNIQUE (term),
CHECK (IDENT > 0)
);
Gegeben sei dazu dieses Embedded SQL/C-Programm:
#include <string.h>
#include <string>
#include <iostream>
/* Declare DB variables. */
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR
username[20]; // *** line 1
VARCHAR
password[20]; // *** line 2
VARCHAR
db_string[20]; // *** line 3
EXEC SQL END DECLARE SECTION;
EXEC SQL DECLARE DB_NAME DATABASE;
/* Include the SQL Communications Area */
#include <sqlca.h>
/* Declare prototype for ORA error handler */
2
void sql_error(char* msg);
int main()
{
EXEC SQL BEGIN DECLARE SECTION;
char
oldterm[256];
// *** line 4
char
newterm[256];
// *** line 5
unsigned int ident;
// *** line 6
EXEC SQL END DECLARE SECTION;
const std::string MyOldTerm("ISK macht Spass");
const std::string MyNewTerm("ISK ist doof!!!");
strcpy((char *) db_string.arr, "STUD");
// *** line 7
db_string.len = strlen((char *) db_string.arr);
strcpy((char *) username.arr, "*****");
// *** line 8
username.len = strlen((char *) username.arr);
strcpy((char *) password.arr, "********");
// *** line 9
password.len = strlen((char *) password.arr);
/* Register sql_error() as the error handler. */
EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n");
EXEC SQL CONNECT :username IDENTIFIED BY :password
AT DB_NAME USING :db_string;
// *** line 10
ident = 0;
strcpy( oldterm, MyOldTerm.c_str() );
// *** line 11
EXEC SQL AT DB_NAME SELECT IDENT INTO :ident FROM MY_TERMS
WHERE TERM = :oldterm FOR UPDATE;
// *** line 12
if (ident > 0) // we have found a positive identifier for our string
{
strcpy( newterm, MyNewTerm.c_str());
// *** line 13
EXEC SQL AT DB_NAME UPDATE MY_TERMS SET TERM = :newterm
WHERE TERM = :oldterm;
// *** line 14
EXEC SQL AT DB_NAME COMMIT;
// *** line 15
return 0;
}
else
{
EXEC SQL AT DB_NAME ROLLBACK;
// *** line 14a
return -1;
}
return 0;
}
void sql_error(char *msg)
{
EXEC SQL WHENEVER SQLERROR CONTINUE;
std::cerr << "error: %s" << msg
<< std::endl;
std::cerr << sqlca.sqlerrm.sqlerrmc << std::endl;
EXEC SQL AT DB_NAME ROLLBACK;
3
return;
}
Dieses Programm wird auf der in Abbildung 1 dargestellten Architektur durchgeführt. Wichtige Ereignisse mit Auswirkungen auf den DBMS-Server sind mit Nummern in den Kommentaren hinter den
Code-Zeilen gekennzeichnet. Benutzen Sie diese Nummern, um den Ablauf des Programmes ausgehend
von der vorgegebenen Darstellung in einer Folge von Skizzen möglichst genau zu veranschaulichen. Vergessen Sie nicht, Ihre Darstellungen zu erläutern.
RXU
$3
FOLHQW
&OLHQW
DA 1 our DA
&/
/RJ
:ULWHU
3URFHVV
6HUYHU
3URFHVV
'DWDEDVH%XIIHU
&/
%HWULHEVV\VWHP
%HWULHEVV\VWHPQ
1HW]ZHUN
username:“*****“
password:“********“
db_string:“STUD“
oldterm:
newterm:
ident:
Abbildung 2: Dedicated Server Architektur: line 1 bis line 9
Nach dem Start des Client werden zunächst die Host-Variablen des Client-Programmes im Speicherbereich des Client angelegt. Dort werden sie auch in den Zeilen 7 bis 9 initialisiert, siehe Abbildung 2.
In den Speicherbereich zum Datenaustausch mit der Client-Library werden sie erst mit dem CONNECTStatement aus Zeile 10 weitergegeben (by reference), wie in Abbildung 3 dargestellt. Über einen Aufruf
des Client-Interface (OCI) werden die Daten und das Statement an den Datenbank-Server über ein proprietäres Protokoll kommuniziert. Dort wird dann auch gemäss der Dedicated-Server-Architektur ein neuer Server-Prozess für den Client gestartet. In Abbildung 4 ist wieder das gleiche Prinzip veranschaulicht:
Zuerst werden die Host-Variablen im Speicherbereich des Client verändert, und mit dem ESQL-Statement
werden sie auch in den Speicherbereich der Client Library gemappt. Die Abbildung zeigt weiterhin, dass
die Seite, die das von uns angefragt Tupel enthält, in den Datenbank-Puffer geladen wird. Durch die FOR
UPDATE Klausel wird sie auch gleich exklusiv für unseren Client und dessen Transaktion gesperrt. Die Verarbeitung des UPDATE ist dann in Abbildung 5 dargestellt. Dabei wird nur die Seite im Datenbank-Puffer
verändert, die Änderungen sind bislang noch nicht auf Plattenspeicher geschrieben worden. Lediglich der
Log-Writer Prozess hat ein Before-Image dieser Datenbankseite zu Recovery-Zwecken auf dem Log hinterlegt. Mit dem COMMIT-Statement aus Zeile 15 wird der Log-Writer Prozess erneut aktiviert, der dann
die durch die Transaktion vorgenommenen Änderungen zusammen mit dem COMMIT-Marker in das
REDO-Log schreibt.
b) Inwieweit würde sich der von Ihnen geschilderte Ablauf ändern, wenn das Client-Programm von einem
asymmetrischen DBMS-Server verarbeitet würde?
Die asymmetrische Server-Architektur zeichnet sich durch den zusätzlichen Dispatcher-Prozess und eine
fest vorgegebene Anzahl an meist schon vorab gestarteten Server-Prozessen aus. Der Dispatcher ordnet für
jedes an das DBMS geschickte SQL-Statement dem Client und seiner Transaktion einen der verfügbaren
Server-Prozesse zu, die dann die Anfrage verarbeiten. Dies heisst auch, das verschiedene Anfragen aus
der gleichen Transaktion des selben Clients von verschiedenen Server-Prozessen bedient werden können.
4
RXU
$3
FOLHQW
&OLHQW
DA 1 our DA
&/
/RJ
:ULWHU
3URFHVV
6HUYHU
2XU
3URFHVV 6HUYHU
3URFHVV
'DWDEDVH%XIIHU
&/
%HWULHEVV\VWHP
%HWULHEVV\VWHPQ
1HW]ZHUN
&211(&7
:username:“*****“
:password:“********“
:db_string:“STUD“
username:“*****“
password:“********“
db_string:“STUD“
oldterm:
newterm:
ident:
Abbildung 3: Dedicated Server Architektur 2: line 10
RXU
$3
FOLHQW
&OLHQW
DA 1 our DA
&/
&/
/RJ
:ULWHU
3URFHVV
6HUYHU
2XU
3URFHVV 6HUYHU
3URFHVV
'DWDEDVH%XIIHU
RXU[ORFNHG
'%SDJH
OLQH
%HWULHEVV\VWHP
%HWULHEVV\VWHPQ
1HW]ZHUN
6(/(&7
:username:“*****“
:password:“********“
:db_string:“STUD“
:oldterm:“ISK…“ //line 12
:ident:0
//line 12
username:“*****“
password:“********“
db_string:“STUD“
oldterm:“ISK…“
//line 11
newterm:
ident:0
//line 11
Abbildung 4: Dedicated Server Architektur: line 11 und line 12
5
RXU
$3
FOLHQW
&OLHQW
DA 1 our DA
&/
&/
/RJ
:ULWHU
3URFHVV
6HUYHU
2XU
3URFHVV 6HUYHU
3URFHVV
'DWDEDVH%XIIHU
RXU[ORFNHG
'%SDJH
Å,6.«6SDVV´
%HWULHEVV\VWHP
%HWULHEVV\VWHPQ
1HW]ZHUN
83'$7(
:username:“*****“
:password:“********“
:db_string:“STUD“
:oldterm:“ISK…“
:newterm:“…doof“ //line 14
:ident:1
//result!
username:“*****“
password:“********“
db_string:“STUD“
oldterm:“ISK…“
newterm:“…doof“ //line 13
ident:1
//result!
Abbildung 5: Dedicated Server Architektur: line 13 und line 14
6
RXU
$3
FOLHQW
&OLHQW
DA 1 our DA
&/
&/
/RJ
:ULWHU
3URFHVV
6HUYHU
2XU
&RPPLW
3URFHVV 6HUYHU
3URFHVV
'DWDEDVH%XIIHU
RXU[ORFNHG
'%SDJH
Å,6.«GRRI´
%HWULHEVV\VWHP
%HWULHEVV\VWHPQ
1HW]ZHUN
&200,7OLQH
:username:“*****“
:password:“********“
:db_string:“STUD“
:oldterm:“ISK…“
:newterm:“…doof“
:ident:1
username:“*****“
password:“********“
db_string:“STUD“
oldterm:“ISK…“
newterm:“…doof“
ident:1
Abbildung 6: Dedicated Server Architektur: line 15
7
Im konkreten Fall des uns hier vorliegenden Client-Programmes bedeutet dies, dass für jeden EXEC SQL AT
DB NAME-Aufruf die Anfrage zunächst an den Dispatcher-Prozess geschickt wird und dieser der Anfrage
dann einen Server-Prozess zuordnet. In der nächsten Anfrage kann sich der Dispatcher durchaus auch
für einen anderen Server-Prozess entscheiden. Der sonstige Ablauf bleibt unverändert.
Aufgabe 3: Rechnerübung Thick-Client: JDBC-Anbindung
Unter Ihrer IS-K-Kennung finden Sie eine ORACLE-Tabelle demo test mit Messwerten der Luftreinhaltung.
Jedes Tupel der Tabelle enthält in den Attributen a0 bis a4 die 5 Messwerte, die innerhalb eines Tages
zu bestimmten Zeiten t0 bis t4 erhoben werden. Ein Messwert ist dabei als eine Fliesskommazahl in der
Datenbanktabelle hinterlegt. Ein grösserer Wert bedeute eine höhere Luftverschmutzung.
Implementieren Sie von Ihrem IS-K Account aus einen Java-Client, der in einem einfachen Histogramm die
Verteilung der maximalen Messwerte der Tage über die Messzeitpunkte abträgt. Läge beispielsweise an 10
Tagen der maximale Messwert bei der Zeit t0 vor, so soll im Histogramm an der Stelle t0 der Wert 10 stehen.
Als Lösung der Aufgabe ist der Java-Code sowie das berechnete Histogramm abzugeben.
Tipp: Folgende Routinen und Datenstrukturen können zur erfolgreichen Bearbeitung der Aufgabe nützlich
sein:
• eine Verbindung zur IS-K-Datenbank erzeugen Sie mit folgendem Aufruf:
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
Connection conn =
DriverManager.getConnection
("jdbc:oracle:thin:@xibalba49.ethz.ch:1521:STUD49",
"<isk-db-kennung>", "<isk-db-passwort>");
• Klasse Connection mit der Routine createStatement,
• Klasse Statement,
• Klasse ResultSet.
Bitte informieren Sie sich auf dem IS-K-Webserver weiter über die oben genannten Konstrukte. Der Verbindungsaufbau zur Datenbank ist in der ORACLE JDBC -Dokumentation beschrieben; die Beschreibung der
drei genannten Klassen befindet sich in der Java JDK JDBC -Dokumentation. Insbesondere das Beispielprogramm in der ORACLE-Dokumentation zeigt den Umgang mit Statements und ResultSets.
Die folgenden Befehle helfen Ihnen bei der Erstellung des Programms auf den Rechnern:
Compilieren: javac < classname >.java
Aufruf: java < classname >
Umgebung einrichten:
1. Auf dem Windows Cluster im Raum IFW C31 ist bereits Java installiert.
2. Verbinden von Laufwerk U: mit dem Netzwerk–Share \\dbs.ethz.ch\sqlplus.
3. Öffnen Sie zunächst eine DOS Eingabeaufforderung. Man muss dann lediglich noch die Umgebungsvariablen ’Path’ und ’Classpath’ anpassen bzw. setzen.
Dazu wurde ein Batch-File vorbereitet (U:\JDBC\setenv.bat). Nach dem Ausführen dieses Batch–Files
können Sie mit den üblichen Java–Programmen ’javac’ resp. ’java’ arbeiten.
Achtung : sobald Sie eine neue DOS-Eingabeaufforderung öffnen, müssen Sie das Batch-File wieder
ausführen.
8
Die Ausführung des unten angegebenen Programmes ergibt folgendes Histogramm:
t0
0
t1
0
t2
58
t3
0
t4
0
// You need to import the java.sql package to use JDBC
import java.sql.*;
class Luft
{
public static int MaxPos(float f0, float f1, float f2, float f3, float f4)
{
float max = f0;
int pos
= 0;
if (f1 > max)
{
pos = 1;
max = f1;
}
if (f2 > max)
{
pos = 2;
max = f2;
}
if (f3 > max)
{
pos = 3;
max = f3;
}
if (f4 > max)
{
pos = 4;
max = f4;
}
return pos;
}
public static void main (String args [])
throws SQLException
{
// Load the Oracle JDBC driver
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
// Connect to the database
// You must put a database name after the @ sign in the connection URL.
// You can use either the fully specified SQL*net syntax or a short cut
// syntax as <host>:<port>:<sid>. The example uses the short cut syntax.
Connection conn =
DriverManager.getConnection ("jdbc:oracle:thin:@xibalba49.ethz.ch:1521:STUD49",
"iskXX", "*****");
conn.setAutoCommit(false);
9
// Create a Statement
Statement stmt = conn.createStatement ();
// Select the complete table
ResultSet rset = stmt.executeQuery ("SELECT a0, a1, a2, a3, a4 FROM demo_test");
// Iterate through the result and collect sums
int Pos;
int PosCount[] = new int[5];
Pos = 0;
while (rset.next ())
{
Pos = MaxPos(Float.valueOf(rset.getString("a0")).floatValue(),
Float.valueOf(rset.getString("a1")).floatValue(),
Float.valueOf(rset.getString("a2")).floatValue(),
Float.valueOf(rset.getString("a3")).floatValue(),
Float.valueOf(rset.getString("a4")).floatValue()
);
PosCount[Pos] = PosCount[Pos] + 1;
}
System.out.println("Ergebnis: ");
System.out.println(PosCount[0]);
System.out.println(PosCount[1]);
System.out.println(PosCount[2]);
System.out.println(PosCount[3]);
System.out.println(PosCount[4]);
conn.commit();
}
}
10
Herunterladen