LOB说明
1、LOB分类:
LOB大对象主要是用来存储大量数据的数据库字段,在Oracle9iR2中LOB的最大容量是4G,Oracle10g 最大8T,Oracle11g最大128T。具体取决于blocksize的大小。
Oracle中支持的四种类型的LOB:
CLOB:字符LOB。这种类型用于存储大量的文本信息,如XML或者只是纯文本。这个数据类型需要进行字符集转换,也就是说,在获取时,,这个字段中的字符将会从数据库的字符集转换为客户的字符集,而在修改时会从客户的字符集转换为数据库的字符集。
NCLOB:这是另一种类型的字符LOB。存储在这一列中的数据所采用的字符集是数据库的国家字符集,而不是数据库的默认字符集。
BLOB:二进制LOB。这种类型用于存储大量的二进制信息,如字处理文档。图像和你能想象到的任何其他数据。它不会执行字符集转换。应用向BLOB中写入什么位和字节,BLOB就返回什么位和字节。
BFILE:二进制文件LOB。这与其说是一个数据库存储实体,不如说是一个指针。带BFILE列的数据库库中存储的只是操作系统中某个文件的一个指针。这个文件在数据库之外维护,根本不是数据库的一部分。BFILE提供了文件内容的只读访问。
2、LOB数据类型分类:
1)按存储数据的类型分:
(1)字符类型:
CLOB:存储大量单字节字符数据
NLOB:存储定宽多字节字符数据
(2)二进制类型:
BLOB:存储较大无结构的二进制数据。
(3)二进制文件类型:
BFILE:将二进制文件存储在数据库外部的操作系统中。存放问加你路径。
2)按存储方式分:
(1)存储在内部表空间(内部LOB)
CLOB,NLOB和BLOB
(2)指向外部操作系统文件(外部LOB)
BFILE
2.2 内部LOB
例子:
SQL>create user sh identified by sh;
SQL>grant resource,connect,dba to sh;
SQL>conn sh/sh;
SQL>create table t1(id number primary key,txt clob);
table created
//这个地方我没有查出来,不知道是什么原因
- ANQING@anqing1(rac1)> selectsegment_name, segment_type from user_segments;
- SEGMENT_NAME SEGMENT_TYPE
- -----------------------------------------------------
- SYS_C007307 INDEX
- SYS_IL0000056466C00002$ LOBINDEX
- SYS_LOB0000056466C00002$ LOBSEGMENT
- TL TABLE
SYS_C007307索引用来支持主键约束,lobindex和lobsegment是为了支持我们的LOB列。我们的实际LOB数据就存储在lobsegment中(确实,LOB数据也有可能存储在表T中,不过稍后讨论ENABLE STORAGE IN ROW子句时还会更详细地说明这个内容)。lobindex用于执行LOB的导航,来找出其中的某些部分。创建一个LOB列时,一般来说,存储在行中的是一个指针(pointer),或LOB定位器(LOB locator)。我们的应用所获取的就是这个LOB定位器。
当请求得到LOB的‘1200-2000字节’时,将对lobindex使用LOB定位器来找出这些字节存储在哪里,然后再访问lobsegment。可以用lobindex很容易地找到LOB的各个部分。由此说来,可以把LOB想成一种主/明细关系。
LOB按“块”(chunk)或(piece)来存储,每个片段都可以访问。例如,如果我们使用表来实现一个LOB,可以如下做到这一点:
- ANQING@anqing1(rac1)> selectsegment_name, segment_type from user_segments;
- SEGMENT_NAME SEGMENT_TYPE
- -----------------------------------------------------
- SYS_C007307 INDEX
- SYS_IL0000056466C00002$ LOBINDEX
- SYS_LOB0000056466C00002$ LOBSEGMENT
- TL TABLE
为了得到LOB中的N~M字节,要对表中的指针(LOB定位器)解除引用,遍历lobindex结构来找到所需的数据库(chunk),然后按顺序访问。这使得随机访问LOB的任何部分都能同样迅速,你可以用同样快的速度得到LOB的最前面、中间、或最后面的部分,因为无需从头开始遍历LOB。
2.2.1 LOB表空间
create table语句包括以下内容:
LOB(TXT)STOREAS(TABLESPACEUSERS…
这里指定的tablespace是将存储lobsegment和lobindex表空间,这可能与表本身所在的表空间不同,也就是说,保存LOB数据的表空间可能不同于保存实际表数据的表空间 。
为什么考虑为LOB数据使用另外一个表空间(而不用表数据所在的表空间)呢?
注意原因与管理和性能有关。从管理的角度看,LOB数据类型表示一种规模很大的信息。如果表有数百万行,而每行有一个很大的LOB,那么LOB就会极为庞大。为LOB数据单独使用一个表空间有利于备份和恢复以及空间管理,单从这一点考虑,将表与LOB数据分离就很有意义。例如,你可能希望LOB数据使用另外一个统一的区段大小,而不是普通表数据所用的区段大小。
另一个原因处于I/O性能的考虑,默认情况下,LOB不在缓冲区缓存中进行缓存。因此,默认情况下,对于每个LOB访问,不论是读还是写,都会带来一个物理I/O(从磁盘直接读,或者向磁盘直接写)。
注意:LOB可能是内联(inline),或者存储在表中。在这种情况下,LOB数据会被缓存,但是这只适用于小于4000字节的LOB。
由于每个访问都是一个物理I/O,所以如果你很清楚在实际中(当用户访问时)有些对象会比大多数其他对象经历更多的物理I/O,那么将这些对象分离到他们自己的磁盘上也就很有意义。
需要说明lobindex和lobsegment总是会在同一个表空间中。不能讲lobindex和lobsegment放在不同的表空间中。在Oracle的更早版本中,允许为lobindex和lobsegment分别放在单独的表空间中,但在8i以后,就不在允许为lobindex和logsegment指定不同的表空间。实际上,lobindex的所有存储特征都是从lobsegment继承的。
2.2.2 in row子句
create table语句还包括以下内容:
LOB(TXT)STOREAS(…ENABLE STORAGEINROW……
这控制了LOB数据是否总与表分开存储(存储在lobsegment中),或是有时可以与表一同存储,而不用单独放在logsegment中。如果设置了enable storage in row,而不是disable STORAGE in row,小LOB(最多4000字节)就会像varchar2一样存储在表本身中。只有当LOB超过了4000字节时,才会‘移出’到obsegment中。
默认行为是启用行内存储(enable STORAGE ROW),而且一般来讲,如果你知道LOB总是能在表本身中放下,就应该采用这种默认行为.例如,你的应用更可能有一个某种类型的description字段。这个description可以存储0~32KB的数据(或者可能更多,但大多数情况下是小于或等于32K)。已知很多描述都是很简短,只有几百个字符。如果把他们单独存储,并在每次获取时都通过索引来访问,就会存在很大的开销,你完全可以将他们内联存储,即放在表本身中,这就能避免单独存储的开销。不仅如此,如果LOB还能避免获取LOB时所需要的物理I/O。
一般来说,OUT ROW,即将数据存储在segment里,在这种情况下不会在buffer cache中进行缓存,这样每次都会产生物理I/O。同时对out row进行读写操作时。虽然有lobindex的存在,但DML操作需要同时维护lobindex和lobsegment。采用out row会增加逻辑IO和物理IO,所以默认启用in row。对lob进行缓存,减小IO成本。
2.2.3 chunk子句
create table语句包括以下内容:
LOB(“TXT”)store as (…chunk 8192…)
LOB存储在块(chunk)中,指向LOB数据的索引会指向各个数据块。块(chunk)是逻辑上连续的一组数据块(block),这也是LOB的最小分配单元,而通常数据块的最小分配三元是数据库块。chunk大小必须是Oracle 块大小的整数倍,只有这样才是合法值。
从两个角度看,选择chunk大小时必须当心。首先,每个LOB实例(每个行外存储的LOB值)会占用至少一个chunk。一个chunk有一个LOB值使用。如果一个表有100行,而每行有一个包含7KB数据的LOB,你就会分配100个chunk,如果将chunk大小设置成32kb,就会分配100个32kb的chunk。如果将chunk大小设置为8kb,则(可能)分配100个8kb的chunk。关键是,一个chunk只能有一个lob使用(两个lob不会使用同一个chunk)。如果选择了一个chunk大小,但不符合你期望的LOB大小,最后就会浪费大量的空间。
还需要注意要让每个LOB实例对应得chunk数减至最少。前面已经看到了,有一个lobindex用户指向各个块,块越多,索引就越大。如果有一个4MB的LOB,并使用8KB的chunk,你就至少需要512的chunk来存储这个消息。这也说明,至少需要512个lobindex条目指向这些chunk。听上去好像也没有什么,但是,对于每个LOB个数的512倍。另外,这会影响性能,因为与读取更少但更大的chunk相比,现在要花更长的时间来读取和管理许多小chunk。我们最终的目标是:使用一个能使“浪费”最少,同时又能高效存储数据的chunk大小。
2.2.4.pctversion子句
dbms_metadata返回的create table语句中包含以下内容:
LOB(“TXT”)store as (…pctversion 10…)
多版本一致读、当前读是oracle数据库具有的独特属性,也是其最重要的特性之一。借助undo表空间的前镜像数据保存,oracle server process可以访问到一些特定时间点SCN的数据,作为一致性读取、免于仓数据。
但对于lob类型而言,一致读问题同样存在。Oracle需要一种保留LOB数据镜像的机制,保存一系列的old version。目前,oracle提供了两种维持机制来进行控制:基于时间的版本保留retention和基于空间的版本保留pctversion。
- SQL> select table_name, column_name,pctversion, retention from user_lobs;
- TABLE_NAME COLUMN_NAMPCTVERSION RETENTION
- ---------- ---------- ---------- ----------
- T CL 900
相反,修改一个LOB时,oracle会分配一个新的chunk,并且仍保留原来的chunk。如果回滚了事物,对lob索引所做的修改会回滚,索引将再次指向原来的chunk。因此,undo维护会在lob段本身中执行。修改数据时,原来的数据库保持不动,此外会创建新数据。读LOB数据时这也很重要。LOB是读一致的,这与所有其他段一样。如果你在上午9:00获取了一个LOb定位器,你从中获取的LOB数据就是“上午9:00那个时刻的数据”。这就像是你在上午9点打开了一个游标(一个结果集)一样,所生成的行就是那个时间点的数据行。与结果集类似,即使别人后来修改了LOB数据。在此,Oracle会使用lobsegment,并使用logindex的读一致视图来撤销对LOB的修改,从而提取获取LOB定位器当时的LOB数据。它不会使用Logsegment的undo信息,因为根本不会为ligsegment本身产生undo信息。
PCTVERSION控制着用于实现LOB数据版本化的已分配LOB空间的百分比(这些数据块由某个时间点的LOB所用,并处在lobsegment的HWM以下)。对于许多使用情况来说,默认设置10%就足够了,因为在很多情况下,你只是要insert和获取LOB(通常不会执行LOB的更新;LOB旺旺会插入一次,而获取多次)。因此,不补为LOB版本化预留太多的空间(甚至可以没有)。
不过,如果你的应用确实经常修改LOB,倘若你频繁地读LOB,与此同时另外某个回话正在修改这些LOB,10%可能就太小了。如果处理LOB时遇到一个ORA-22924错误,解决方案不是增加UNDO表空间的大小,也不是增加UNDO保留时间(undo_retention),如果你在使用手动undo管理,那么增加更多RBS空间也不能解决这个问题,而是应该使用一下命令:
alter table tablename mofify lob (lobname)(pctversion n);
并增加lobsegment中为实现数据版本化所用的空间大小。
2.2.5 retention子句
retention是表示采用基于时间版本保留策略。简单的说,就是尽量保证保留一个时间段倍的数据lob版本不会清除掉,即多长时间内来保证一致读。在数据库版本的兼容性版本设置在9.2.0.0以上,并且undo_management参数值为true时,lob是默认直接使用retetion设置的。
需要注意,不能使用这个子句来指定保留时间:而要从数据库的undo_retention设置来继承它。
这个子句与pctversion子句是互斥的,即retention和pctversion只能设置一个,不能两个都设置。
2.2.6 cache子句
前面的dbms_metadata返回的create table语句包括以下内容:
LOB(“TXT”)STORE as (…nocache…)
除了nocache,这个选项还可以是cache或cache reads。这个子句控制了lobsegment数据是是否存储在缓冲区缓存中。默认的nocache指示,每个访问都是从磁盘的一个直接读,类似地,每个写/修改都是对大盘的一个直接写。cache reads允许缓存从磁盘读的LOB数据,但是LOB数据的写操作必须直接写至磁盘。chche则允许读和写时都能缓存lob数据。
在许多情况下,默认设置可能对我们并不合适。如果只有小规模或者中等规模的LOB(例如,使用LOB来存储只有几KB的描述性字段),对其缓存就很有意义。如果不缓存,当用户更新描述字段时,还必须等待I/O将数据写入磁盘(将执行一个chunk大小的I/O,而且用户要等待这个I/O完成)。如果你在执行多个LOB的加载,那么加载每一行时都必须等待这个I/O完成。所以启用执行LOB缓存很合理、你可以打开和关闭缓存,开看看会有什么影响:
ALTER table tablename modify lob (lobname)(cache);
alter table tablename modify lob (lobname)(nocache);
对于一个规模很多的额初始加载,启用lob的缓存很有意义,这允许dbwr在后台将LOB数据写至磁盘,而你的客户应用可以继续加载更多的数据。对于频繁访问或修改的小到中等规模的LOB,缓存就很合理,可以部门让最终用户实时等待物理I/O完成。不过,对于一个大小为50MB的LOB,把它放在缓存中就不好了。
要记住,此时可以充分使用keep池或回收池。并非在默认缓存中将lobsegment数据与所有“常规”数据一同缓存,可以使用保持池或回收池将其分开缓存。采用这种方式,既能缓存LOB数据,而且不影响系统中现有数据的缓存。
2.2.7 LOB STORAGE子句:
dbms_metadata返回的create table语句还包括以下内容:
- LOB ("TXT") STORE AS (…
- STORAGE(INITIAL65536
- NEXT1048576
- MINEXTENTS1
- MAXEXTENTS2147483645
- PCTINCREASE0
- FREELISTS1
- FREELISTGROUPS1
- BUFFER_POOLDEFAULT))
对LOB段使用保持池或回收池可能是一个很有用的技术,这样就能缓存LOB数据,而且不会“破坏”现有的默认缓冲区缓存,并不是将LOB与常规表一同放在块缓冲区中,可以在SGA中专门为这些LOB对象预留一段专用的内存。buffer_pool子句可以达到这个目的。
2.3 bfile
bfile类型只是操作系统上一个文件的指针,它用于为这些操作系统文件提供只读访问。
注意:内置包UTL_FILE也为操作系统文件提供了读写访问。不过它没有使用bfile类型。
使用bfile时,还有使用一个oracle directory对象。directory对象只是将一个操作系统目录映射至数据库中的一个“串”或一个名称(以提供可移植性;你可能想使用bfile中的一个串,而不是操作系统特定的文件名约定)。
作为一个小例子,下面创建一个带bfile列的表,并创建一个directory对象,在插入一行,其中引用了文件系统中的一个文件:
- SQL> create table t ( id int primarykey,os_file bfile);
- Table created.
- SQL> create or replace directory my_diras '/tmp/';
- Directory created.
- SQL> insert into t values ( 1,bfilename( 'MY_DIR', 'test.dbf' ) );
- 12.row created.
- SQL> select dbms_lob.getlength(os_file)from t;
- DBMS_LOB.GETLENGTH(OS_FILE)
- ---------------------------
- 1056768
可以看到所指定的文件大小为1MB。注意,这里故意在INSERT语句中使用了MY_DIR。如果使用混合大小写或小写,会得到以下错误:
- SQL> update t set os_file = bfilename('my_dir', 'test.dbf' );
- 12.row updated.
- SQL> select dbms_lob.getlength(os_file)from t;
- select dbms_lob.getlength(os_file) from t
- *
- ERROR at line 1:
- ORA-22285: non-existent directory or filefor GETLENGTH operation
- ORA-06512: at "SYS.DBMS_LOB",line 566
这个例子只是说明:Oracle 中的DIRECTORY 对象是标识符,而默认情况下标识符都以大写形式存储。
BFILENAME 内置函数接受一个串,这个串的大小写必须与数据字典中存储的DIRECTORY对象的大小写完全匹配。所以,我们必须在BFILENAME 函数中使用大写,或者在创建DIRECTORY 对象时使用加引号的标识符:
- SQL> create or replace directory"my_dir" as '/tmp/';
- Directory created.
- SQL> select dbms_lob.getlength(os_file)from t;
- DBMS_LOB.GETLENGTH(OS_FILE)
- ---------------------------
- 1056768
bfile在磁盘上占用的空间不定,这取决于directory对象名的文件名的长度。在前面的例子中,所得到的bfile长度大约为35字节。一般来说,bfile会占用大约20字节的开销,再加上directory 对象的长度以及文件名本身的长度。
与其他LOB数据不同,bfile数据不是“读一致”的。由于bfile在数据库之外管理,对bfile解除引用时,不论文件上发生了什么,都会反映到你得到的结构中。所以,如果反复读同一个bfile,可能会产生不同的结果,这与对clob、blob或bclob使用lob定位器不同。
三.Move table与LOB
提到解决高水位的一种方法就是move table,如果我们的表里有lob字段,那么我们在move的时候就需要注意一下。
在建立含有lob字段的表时,oracle会自动为lob字段建立两个单独的segment,一个用来存放数据(segment_type=lobsegment),另一个用来存放索引(segment_type=lobindex)。
我们队标move时,LOG类型字段和该字段的索引不会跟着move,必须要单独来进行move,语法如下:
SQL>alter table owner.table_name move tablespace tablespace_name lob(lob_column) store as (tablespace tablespace_name);
也可以分2部走:
SQL>alter table owner.table_name move tablespace tablespace_name;
SQL>alter table owner.table_name move lob(lob column) store as (tablespace tablespace_name);
move操作会导致表上的索引失效,操作结束后我们需要对索引进行rebuild。
本文转自:http://blog.csdn.net/tianlesoftware/article/details/6905406#t49