
| Oracle Large Object (LOB) Datentyp zur Aufnahme großer Datenmengen. |
| Binary Large Object (BLOB) Datentyp zur Aufnahme binärer Daten innerhalb der Datenbank (z. B. Programme, Grafiken etc.). |
| Character Large Object (CLOB) Datentyp zur Aufnahme von Textdaten innerhalb der Datenbank. |
| National Character Large Object (NCLOB) Datentyp zur Aufnahme von Textdaten im nationalen Zeichensatz innerhalb der Datenbank. |
| Binary Large Object (BFILE) Datentyp für ein LOB, das nicht in der Datenbank gespeichert wird. |
Mit den Datentypen BFILE, BLOB, CLOB und NCLOB lassen sich unstrukturierte Daten, z. B. Text, Grafiken/Bilder, Videoclips und Musikdateien, mit bis zu 128 Terabytes speichern.
Früher wurden LONG und RAW als Datentypen benutzt. Diese besitzen eine geringere Speicherkapazität von maximal 2 Gigabyte. Oracle operiert mit LOB durch einen so genannten "Locator". Dabei handelt es sich um einen Zeiger auf den aktuellen Speicherort des LOB.
Jeder Datensatz bekommt seinen eigenen "Locator". Dieser Zeiger ist nichts anderes als eine Variable mit einem bestimmten Wert, die einen einzelnen LOB-Wert im Datenbankrechner abbildet.
LOB-Zeiger wurden entwickelt, um Benutzer mit einem Mechanismus auszustatten, durch den sie sehr große Objekte in den Anwendungsprogrammen leicht verändern können, ohne dass es erforderlich ist, das tatsächliche LOB ständig zwischen Datenbankserver und Client, auf dem das Anwendungsprogramm läuft, zu kopieren.
Es wird einmal unterschieden in interne LOB (BLOB, CLOB, NCLOB) und externe LOB (BFILE). Eine weitere Möglichkeit der Unterscheidung ist die Einteilung in persistente LOB und temporäre LOB. Dieser Unterschied wird im Folgenden näher beschrieben.
Interne LOB werden innerhalb der Datenbank in Tablespaces gespeichert, wobei Platz und Zugriff optimiert sind. Interne LOB können im Falle des Transaktions-/Systemabbruchs zurückgewonnen werden.
Sie fallen unter das Transaktionskonzept. Das heißt COMMIT und ROLLBACK können durchgeführt werden. Ebenfalls ist ein RECOVERY bei Systemfehlern möglich.
Der BLOB dient der Aufnahme von binären, unstrukturierten, großen Objekten. Der CLOB wird für die Speicherung von großen Textdaten sinnvoll eingesetzt. Der NCLOB nimmt die Textdaten im nationalen Zeichensatz auf.
Der externe LOB wird durch das BFILE abgebildet. Dieser Datentyp ist im Dateisystem des Betriebsystems gespeichert. Eine BFILE-Spalte/-Variable speichert eine BFILE-"Rechnervariable", die als Zeiger auf eine binäre Datei auf dem Server dient.
Die BFILEs sind nur lesbar. Sie können also (über die Datenbank) nicht geändert werden. Die Größe eines BFILEs ist vom Betriebssystem abhängig. Der Datenbankadministrator muss sicherstellen, dass die Datei existiert und dass die Oracle-Prozesse im Betriebssystem Leserechte besitzen.
Die Höchstzahl an geöffneten BFILEs wird über den Parameter SESSION_MAX_OPEN_FILES eingestellt und ist systemabhängig. Sie unterliegen nicht dem Transaktionsprinzip und damit können COMMIT, ROLLBACK und RECOVER nicht genutzt werden.
Abgesehen von herkömmlichen Fremdspeichern wie Festplatten, können sich BFILEs auf Speichermedien wie CD-ROMs, Photo CDs und DVDs befinden. Oracle greift auf solche BFILEs auch über das zugrunde liegende Zugriffssystem des Betriebssystems (OS) zu.
Die Daten, die in einem LOB gespeichert sind, werden auch als "Wert" des LOB bezeichnet. Der Wert eines internen LOB kann (!) mit den anderen Werten des Datensatzes "in-line" gespeichert werden, d. h. innerhalb des zugeordneten Speicherbereiches innerhalb der Datenbank.
Wenn der Parameter DISABLE STORAGE IN ROW nicht eingestellt ist und der interne LOB-Wert kleiner als 4.000 Bytes ist, dann wird der Wert "in-line" gespeichert. Andernfalls wird er außerhalb des Datensatzes ("out-of-line") im LOB-Tablespace gespeichert.
Da LOB große Objekte sein sollen, ist eine in-line-Ablage nur sinnvoll, wenn die Anwendung kleine und große LOB mischt. Der LOB-Wert wird automatisch aus dem Datensatz verschoben ("out-of-line"), sobald er die 4.000 Byte Größe überschreitet.
Unabhängig davon, wo der Wert des internen LOB gespeichert ist, wird ein Locator in dem Datensatz gespeichert. Der LOB-Locator ist ein Zeiger zur tatsächlichen Position des LOB-Wertes. Ein LOB-Locator ist ein Zeiger zu einem internen LOB, während ein BFILE-Locator ein Zeiger zu einem externen LOB ist.
Für interne LOB speichert die LOB-Spalte einen eindeutigen Locator zum Wert des LOB, der in einem Datenbank Tablespace abgelegt ist. Jede LOB-Spalte/Attribut hat für einen gegebenen Datensatz seinen eigenen eindeutigen LOB-Locator.
Externe LOB (BFILEs) speichern die LOB-Spalte in einem BFILE-Locator zur externen Datei. Jede BFILE-Spalte/Attribut besitzt für einen gegebenen Datensatz seinen eigenen BFILE-Locator. Jedoch können zwei unterschiedliche Datensätze einen BFILE-Locator besitzen, der auf die gleiche Datei verweist.
Bevor aus einem Programm (PL/SQL, OCI, OCCI, Pro*C/C++, Pro*COBOL, Java oder OLE) in einem internen LOB geschrieben werden kann, muss der LOB mit dem Spaltenattribut NOT NULL gebildet werden. D. h. er muss einen LOCATOR enthalten. Dies wird mit den Funktionen EMPTY_BLOB() für BLOB oder EMPTY_CLOB() für CLOB und NCLOB erreicht.
Bevor in einen externen LOB-Wert (BFILE) mit den programmgestützten Schnittstellen geschrieben werden kann, muss das BFILE ebenfalls mit dem Spaltenattribut NOT NULL gebildet werden. Die BFILE-Spalte kann mit der BFILENAME()-Funktion initialisiert werden, um auf eine Datei im Betriebssystem zu verweisen.
Temporäre LOB (BLOB, CLOB, NCLOB) werden nicht, wie andere Daten, dauerhaft in der Datenbank gespeichert. Sie werden in den temporären Tablespaces gespeichert und nicht in Tabellen abgelegt.
Das heißt, dass ein interner, temporärer LOB auf dem Server erstellt (CREATE) werden kann. Allerdings ist er von jeder Tabelle unabhängig und kann somit auch nicht gespeichert werden. Durch die Verwendung von temporären LOB werden die Systemressourcen geschont.
Die temporären LOB sind nicht mit einer Tabelle verbunden und somit gibt es auch keinerlei Entsprechung zu den Bezeichnungen "in-line" und "out-of-line", wie dies für die anderen LOB-Typen üblich ist.
Mit diversen Funktionen lassen sich Manipulationen an den einzelnen LOB-Typen vornehmen (siehe Abbildung 1). Es ist dabei zu beachten, dass nicht alle Funktionen für die externen LOB gültig sind, weil sie teilweise nur lesbar und nicht veränderbar sind. Detaillierte Beispiele finden Sie in Abbildung 2.
| APPEND() | Hängt den Inhalt eines LOB an einen anderen LOB. |
| COPY() | Kopiert einen Teil oder alles von einem LOB in einen anderen. |
| ERASE() | Löscht einen Teil von einem LOB ab einer bestimmten Position. |
| LOADFROMFILE() | Lädt Daten von einem externen in einen internen LOB. |
| TRIM() | Verkleinert den LOB in eine bestimmte Größe. |
| WRITE() | Schreibt Daten in einen LOB an einer bestimmten Stelle. |
| WRITEAPPEND() | Schreibt Daten an das Ende eines LOB. |
| READ | Lesen der Werte eines LOB. |
-- Löschen einer eventuell schon bestehenden Tabelle test_clob.
DROP TABLE test_clob;
-- Erstellen der Tabelle test_clob.
CREATE TABLE test_clob
(
product_id NUMBER(6),
ID NUMBER(6),
sourcetext_1 CLOB,
sourcetext_2 CLOB
);
-- Einfügen von Datensätzen in die Tabelle.
INSERT INTO test_clob VALUES (1, 1, 'abcd', 'tttt');
INSERT INTO test_clob VALUES (2, 2, 'hijk', 'llll');
COMMIT ;
-- Ausführen eines anonymen PL/SQL Blocks.
DECLARE
-- Deklarationsteil
num_var INTEGER;
clob_sel_1 CLOB;
clob_sel_2 CLOB;
read_amount INTEGER;
read_offset INTEGER;
write_amount INTEGER;
write_offset INTEGER;
src_pos INTEGER := 5;
buffer VARCHAR2 (20);
erase_amount INTEGER := 2;
BEGIN
-- Einlesen der Daten.
SELECT sourcetext_1
INTO clob_sel_1
FROM test_clob
WHERE product_id = 1
FOR UPDATE;
SELECT sourcetext_2
INTO clob_sel_2
FROM test_clob
WHERE product_id = 2
FOR UPDATE;
read_amount := 15;
read_offset := 1;
-- Lesen des LOB-Wertes aus der Variablen und Ausgabe.
DBMS_LOB.READ (clob_sel_1, read_amount, read_offset, buffer);
DBMS_OUTPUT.put_line ('Read clob_sel_1: ' || buffer);
write_amount := 3;
write_offset := 5;
buffer := 'efg';
-- Schreiben in die Variable clob_sel_2.
DBMS_LOB.WRITE (clob_sel_2, write_amount, write_offset, buffer);
read_amount := 15;
-- Lesen des LOB-Wertes aus der Variablen und Ausgabe.
DBMS_LOB.READ (clob_sel_2, read_amount, read_offset, buffer);
DBMS_OUTPUT.put_line ('Write clob_sel_2: ' || buffer);
write_amount := 10;
write_offset := 5;
src_pos := 1;
-- Kopieren des Wertes von clob_sel_2, nach clob_sel_1.
DBMS_LOB.COPY
(clob_sel_1, clob_sel_2, write_amount, write_offset, src_pos);
read_amount := 15;
-- Lesen des LOB-Wertes aus der Variablen und Ausgabe.
DBMS_LOB.READ (clob_sel_1, read_amount, read_offset, buffer);
DBMS_OUTPUT.put_line ('copy clob_sel_1: ' || buffer);
-- Formatieren des Variablen Wertes auf 6 Zeichen.
DBMS_LOB.TRIM (clob_sel_1, 6);
-- Lesen des LOB-Wertes aus der Variablen und Ausgabe.
DBMS_LOB.READ (clob_sel_1, read_amount, read_offset, buffer);
DBMS_OUTPUT.put_line ('trim clob_sel_1: ' || buffer);
-- Löschen eines Teilstrings aus der Variablen clob_sel2.
DBMS_LOB.ERASE(clob_sel_2, erase_amount, 3);
read_amount := 15;
-- Lesen des LOB-Wertes aus der Variablen und Ausgabe.
DBMS_LOB.READ (clob_sel_2, read_amount, read_offset, buffer);
DBMS_OUTPUT.put_line ('erase clob_sel_2: ' || buffer);
END;
/
|
In Abbildung 3 und 4 a - f wird die Beziehung zwischen der Lesekonsistenz des Locators und der Aktualisierung des LOB-Wertes durch einen zweiten Locator in einem Beispiel dargestellt. Ebenso wird ein Teil der oben aufgeführten Funktionen integriert.
CREATE TABLE test_clob ( product_id NUMBER(6), id NUMBER(6), sourcetext CLOB, fltextn NCLOB, foto BLOB ); INSERT INTO test_clob VALUES (1,1,'abcd',EMPTY_CLOB(),EMPTY_BLOB()); COMMIT; |
Mit der Tabelle test_clob werden drei CLOB als mögliche Locatoren betrachtet:
Im Zeitpunkt t1 (siehe Abbildung 4a) ist das SELECT INTO (t1) über den Wert der Variablen sourcetext mit dem Locator clob_selected verknüpft.
DECLARE
num_var INTEGER;
clob_selected CLOB;
clob_updated CLOB;
clob_copied CLOB;
read_amount INTEGER;
read_offset INTEGER;
write_amount INTEGER;
write_offset INTEGER;
buffer VARCHAR2(20);
BEGIN
-- t1
SELECT sourcetext
INTO clob_selected
FROM test_clob
WHERE product_id = 1;
|
Im Zeitpunkt t2 (siehe Abbildung 4b) ist der Wert der Variablen sourcetext mit dem Locator clob_updated verbunden.
-- t2
SELECT sourcetext
INTO clob_updated
FROM test_clob
WHERE product_id = 1
FOR UPDATE;
|
Da es keine Änderungen im Wert der Variablen gegeben hat, ist die Lesekonsistenz in den Zeitpunkten t1 und t2 für clob_selected und clob_updated gewahrt.
Im Zeitpunkt t3 (siehe Abbildung 4c) wird der Wert von clob_selected nach clob_copied übertragen. In diesem Zeitpunkt besitzen alle drei Locatoren den gleichen Wert. Das Beispiel zeigt dieses mit einer Reihe von DBMS_LOB.READ() Aufrufen.
-- t3
clob_copied := clob_selected;
-- Ausgabe der einzelnen Locator Inhalte.
read_amount := 10;
read_offset := 1;
dbms_lob.read(clob_selected, read_amount, read_offset,buffer);
dbms_output.put_line('t3 - clob_selected value: ' || buffer);
-- Ausgabe: 'abcd'
dbms_lob.read(clob_copied, read_amount, read_offset, buffer);
dbms_output.put_line('t3 - clob_copied value: ' || buffer);
-- Ausgabe 'abcd':
dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
dbms_output.put_line('t3 - clob_updated value: ' || buffer);
-- Ausgabe :'abcd'
|
Im Zeitpunkt t4 (siehe Abbildung 4d) verwendet das Programm DBMS_LOB.WRITE(), um den Wert von clob_updated zu ändern und durch DBMS_LOB.READ() wird der neue Wert angezeigt.
-- t4
write_amount := 3;
write_offset := 5;
buffer := 'efg';
dbms_lob.write(clob_updated, write_amount, write_offset,buffer);
read_amount := 10;
dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
dbms_output.put_line('t4 - clob_updated value: ' || buffer);
-- Ausgabe : 'abcdefg'
|
Jedoch zeigt ein DBMS_LOB.READ() auf clob_selected den gleichen Wert im Zeitpunkt t5 (siehe Abbildung 4e) an, so dass erkennbar ist, dass der Locator weiterhin so existiert, wie er beim SELECT eingelesen wurde. Ebenso zeigt ein DBMS_LOB.READ() auf clob_copied den gleichen Wert im Zeitpunkt t6 (siehe Abbildung 4f) an, wie er in clob_selected steht.
-- t5
-- trotz Änderung des LOB-Wertes.
dbms_lob.read(clob_selected, read_amount, read_offset,buffer);
dbms_output.put_line('t5 - clob_selected value: ' || buffer);
-- Ausgabe: 'abcd'
|
-- t6
-- trotz Änderung des LOB-Wertes.
dbms_lob.read(clob_copied, read_amount, read_offset, buffer);
dbms_output.put_line('t6 - clob_copied value: ' || buffer);
-- Ausgabe: 'abcd'
END;
/
|
Seit der Oracle Datenbankversion 10g spricht Oracle ab einer Größe von mehr als vier Gigabyte von den so genannten "Terabyte LOB".
Die maximale Dateigröße wird dabei einerseits durch das Betriebssystem vorgegeben. Andererseits ist für BFILEs das Maximum grundsätzlich auf 264 - 1 Bytes beschränkt!
Zur Zeit werden folgende Umgebungen von Oracle unterstützt:
Nicht unterstützt werden:
Operiert man mit sehr großen LOB sollte der Parameter PCT_INCREASE möglichst auf 0 gesetzt werden. Bei der schrittweisen Erweiterung des LOB wird sonst jedes Mal ein Extent entsprechend des Parameters PCT_INCREASE benutzt. Das ist Speicherplatzverschwendung und bei entsprechender Größe auch schwer zu handhaben.
Eine andere Möglichkeit ist die Verwendung von locally managed Tablespaces. Der Parameter MAXEXTENTS ist möglichst auf UNLIMITED zu setzen: Hierdurch erfolgt dann keine Beschränkung beim Befüllen der LOB-Spalte durch die Anzahl der Extents.
Es sollten möglichst große Extentgrößen verwendet werden. Oracle generiert beim Erstellen eines Extents jedes Mal UNDO-Informationen und weitere Metadaten. Dies kann dazu führen, dass das Maximum des Rollback-Segmentes erreicht wird. Eine Extentgröße von zur Zeit 100 Megabyte oder das häufigere Absetzen eines COMMIT Kommandos zum Beenden der Transaktion kann Abhilfe schaffen (siehe Abbildung 5).
CREATE TABLESPACE lobtbs1 DATAFILE '/eigenes/verzeichnis/lobtbs_1.dbf' SIZE 3000M REUSE ONLINE NOLOGGING DEFAULT STORAGE(MAXEXTENTS UNLIMITED); ALTER TABLESPACE lobtbs1 ADD DATAFILE '/eigenes/verzeichnis/lobtbs_2.dbf' SIZE 2000M REUSE; CREATE TABLE test_clob ( product_id NUMBER(6), id NUMBER(6), sourcetext CLOB, ad_fltextn NCLOB, foto BLOB ) LOB(sourcetext) STORE AS ( TABLESPACE lobtbs1 CHUNK 32768 PCTVERSION 0 NOCACHE NOLOGGING STORAGE(INITIAL 100M NEXT 100M MAXEXTENTS UNLIMITED PCTINCREASE 0)); |
Die Storage Klausel in diesem Beispiel ist im CREATE TABLESPACE Statement eingebettet. Alternativ kann sie auch in der CREATE TABLE Klausel erfolgen. Dabei ist zu beachten, dass die Storage Klausel in einem CREATE TEMPORARY TABLESPACE Statement nicht erlaubt ist.
Der LOB ist inzwischen ein würdiger Nachfolger der alten LONG und RAW Datentypen geworden. Mit jedem neuen Release seit Oracle 8 wurden die Einsatzmöglichkeiten erweitert.
Für die Zukunft empfiehlt sich, auf die Datentypen LONG und RAW generell zu verzichten, da diese im Gegensatz zu LOB-Datentypen nur eingeschränkt nutzbar sind. Somit empfiehlt es sich auch, wenn möglich, eine Migration auf die LOB-Datentypen vorzunehmen. Mit diesem Thema beschäftigen wir uns in einer der nächsten Ausgaben.
Klaus Günther (info@ordix.de).