Oracle LOB

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

//这个地方我没有查出来,不知道是什么原因

  1. ANQING@anqing1(rac1)> selectsegment_name, segment_type from user_segments;  
  2.    
  3. SEGMENT_NAME                        SEGMENT_TYPE  
  4. -----------------------------------------------------  
  5. SYS_C007307                         INDEX  
  6. SYS_IL0000056466C00002$            LOBINDEX  
  7. SYS_LOB0000056466C00002$           LOBSEGMENT  
  8. 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,可以如下做到这一点:

  1. ANQING@anqing1(rac1)> selectsegment_name, segment_type from user_segments;  
  2.    
  3. SEGMENT_NAME                        SEGMENT_TYPE  
  4. -----------------------------------------------------  
  5. SYS_C007307                         INDEX  
  6. SYS_IL0000056466C00002$            LOBINDEX  
  7. SYS_LOB0000056466C00002$           LOBSEGMENT  
  8. TL                                  TABLE  
从概念上讲,LOB的存储与之非常相似,创建这两个表时,在LOB表的id.chunk_number上要有一个主键(这对应于oracle创建的lobindex),而且只要有一个LOB表来存储数据块(对应于lobsegment)。LOB列为我们透明地实现了这种主/明细结构。

为了得到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。

  1. SQL> select table_name, column_name,pctversion, retention from user_lobs;  
  2. TABLE_NAME COLUMN_NAMPCTVERSION RETENTION  
  3. ---------- ---------- ---------- ----------  
  4. T         CL                          900 
LOB在lobsegment中保留某个百分比的空间来实现LOB的版本变化,直接在lobsegment本身中维护信息的版本。lobindex会像其他段一样生成undo,但是lobsegment不会。

相反,修改一个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语句还包括以下内容:

  1. LOB ("TXT")  STORE AS   (…  
  2.    STORAGE(INITIAL65536  
  3.            NEXT1048576  
  4.            MINEXTENTS1  
  5.            MAXEXTENTS2147483645  
  6.            PCTINCREASE0  
  7.            FREELISTS1  
  8.            FREELISTGROUPS1  
  9.             BUFFER_POOLDEFAULT))  
也就是说,它有一个完成的存储子句,可以用来控制物理存储特征。需要指出,这个存储子句同样适用于lobsegment和lobindex,对一个段的设置也可以用于另一个段。假设有一个本地管理的表空间,LOB的相关设置将是freelists、freelist groups和buffer_pool。

对LOB段使用保持池或回收池可能是一个很有用的技术,这样就能缓存LOB数据,而且不会“破坏”现有的默认缓冲区缓存,并不是将LOB与常规表一同放在块缓冲区中,可以在SGA中专门为这些LOB对象预留一段专用的内存。buffer_pool子句可以达到这个目的。

2.3 bfile

bfile类型只是操作系统上一个文件的指针,它用于为这些操作系统文件提供只读访问。

注意:内置包UTL_FILE也为操作系统文件提供了读写访问。不过它没有使用bfile类型。

使用bfile时,还有使用一个oracle directory对象。directory对象只是将一个操作系统目录映射至数据库中的一个“串”或一个名称(以提供可移植性;你可能想使用bfile中的一个串,而不是操作系统特定的文件名约定)。

作为一个小例子,下面创建一个带bfile列的表,并创建一个directory对象,在插入一行,其中引用了文件系统中的一个文件:

  1. SQL> create table t ( id int primarykey,os_file bfile);  
  2. Table created.  
  3. SQL> create or replace directory my_diras '/tmp/';  
  4. Directory created.  
  5. SQL> insert into t values ( 1,bfilename( 'MY_DIR''test.dbf' ) );  
  6. 12.row created.  
现在,就可以把BFILE 当成一个LOB 来处理,因为它就是一个LOB。例如,我们可以做下面的工作:

  1. SQL> select dbms_lob.getlength(os_file)from t;  
  2. DBMS_LOB.GETLENGTH(OS_FILE)  
  3. ---------------------------  
  4. 1056768  

可以看到所指定的文件大小为1MB。注意,这里故意在INSERT语句中使用了MY_DIR。如果使用混合大小写或小写,会得到以下错误:

  1. SQL> update t set os_file = bfilename('my_dir''test.dbf' );  
  2. 12.row updated.  
  3. SQL> select dbms_lob.getlength(os_file)from t;  
  4. select dbms_lob.getlength(os_file) from t  
  5. *  
  6. ERROR at line 1:  
  7. ORA-22285: non-existent directory or filefor GETLENGTH operation  
  8. ORA-06512: at "SYS.DBMS_LOB",line 566

这个例子只是说明:Oracle 中的DIRECTORY 对象是标识符,而默认情况下标识符都以大写形式存储。

BFILENAME 内置函数接受一个串,这个串的大小写必须与数据字典中存储的DIRECTORY对象的大小写完全匹配。所以,我们必须在BFILENAME 函数中使用大写,或者在创建DIRECTORY 对象时使用加引号的标识符:

  1. SQL> create or replace directory"my_dir" as '/tmp/';  
  2. Directory created.  
  3. SQL> select dbms_lob.getlength(os_file)from t;  
  4. DBMS_LOB.GETLENGTH(OS_FILE)  
  5. ---------------------------  
  6. 1056768  
不建议使用加引号的标识符;而倾向于在bfilename调用中使用大写。加引号的标识符属于“异类”,可能会在以后导致混淆。

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值