Oracle中支持4种类型的LOB:
CLOB:字符LOB。存储大量的文本信息,如XML或者只是纯文本。这个数据类型须要进行字符集转换,也就是说,在获取时,这个字段中的字符会从数据库的字符集转换为客户的字符集,而在修改时会从客户的字符集转换为数据库的字符集。
NCLOB:这是另外一种类型的字符LOB。存储在这一列中的数据所采用的字符集是数据库的国家字符集,而不是数据库的默认字符集。
BLOB:二进制LOB。存储大量的二进制信息,如字处理文档,图像换。应用向BLOB中写入什么位和字节,BLOB就会返回什么为和字节。
BFILE:二进制文件LOB。带BFILE列的数据库中存储的只是操做系统中某个文件的一个指针。这个文件在数据库以外维护,根本不是数据库的一部分。BFILE提供了文件内容的只读访问。sql
scott@ORCL>create table t 2 ( id int primary key, 3 txt clob 4 ) 5 / 表已建立。 scott@ORCL>select dbms_metadata.get_ddl( 'TABLE', 'T' ) 2 from dual; DBMS_METADATA.GET_DDL('TABLE','T') -------------------------------------------------------------------------------- CREATE TABLE "SCOTT"."T" ( "ID" NUMBER(*,0), "TXT" CLOB, PRIMARY KEY ("ID") USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 NOCOMPRESS LOGGING TABLESPACE "TOOLS" ENABLE ) SEGMENT CREATION DEFERRED PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING TABLESPACE "TOOLS" LOB ("TXT") STORE AS BASICFILE ( TABLESPACE "TOOLS" ENABLE STORAGE IN ROW CHUNK 8192 RETENTION NOCACHE LOGGING )
LOB显然有如下属性:
一个表空间(这个例子中即为TOOLS)
ENABLE STORAGE IN ROW做为一个默认属性
CHUNK 8192
RETENTION
NOCACHE
LOB列老是会带来一种多段对象(multisegment object),这个表会使用多个物理段。数据库
实际LOB数据存储在lobsegment中,lobindex用于执行LOB的导航,来找出其中的某些部分。建立一个LOB列时,通常来讲,存储在行中的这是一个指针(pointer),或LOB定位器(LOB locator)。应用所获取的就是这个LOB定位器。当请求获得LOB的“1,2000~2,000字节”时,将对lobindex使用LOB定位器来找出这些字节存储在哪里,而后再访问lobsegment。能够用lobindex很容易地找到LOB的各个部分。能够把LOB想成是一种主/明细关系。LOB按“块”(chunk)或(piece)来存储,每一个片断均可以访问。例如,若是咱们使用表来实现一个LOB,能够以下作到这一点:缓存
Create table parent ( id int primary key, other-data... ); Create table lob ( id references parent on delete cascade, chunk_number int, data <datatype>(n), primary key (id,chunk_number) );
从概念上讲,LOB的存储与之很是类似,建立这两个表时,在LOB表的ID.CHUNK_NUMBER上要有一个主键(这对应于Oracle建立的lobindex),并且要有一个LOB表来存储数据块(对应于lobsegment)。LOB列为咱们透明地实现了这种主/明细结构。session
从DBMS_METADATA返回的CREATE TABLE语句包括如下内容:并发
LOB ("TXT") STORE AS BASICFILE (TABLESPACE "TOOLS"....
TABLESPACE 存储lobsegment和lobindex表空间,这可能与表自己所在的表空间不一样。也就是说,保存LOB数据的表空间可能不一样于保存实际表数据的表空间。从管理的角度看,LOB数据类型表示一种规模很大的信息。若是表有数百万行,而每行有一个很大的LOB,那么LOB就会极为庞大。为LOB数据单独使用一个表空间有利于备份和恢复以及空间管理,将表与LOB数据分离就颇有意义。例如,LOB数据使用另一个统一的区段大小,而不是普通表数据所用的区段大小。另外一个缘由则出于I/O性能的考虑。默认状况下,LOB不在缓冲区缓存中进行缓存。所以,默认状况下,对于每一个LOB访问,不管是读仍是写,都会带来一个物理I/O(从磁盘直接读,或者向磁盘直接写)。app
LOB多是内联的(inline),或者存储在表中。在这种状况下,LOB数据会被缓存,可是这只适用于小于4,000字节的LOB。async
lobindex和lobsegment老是会在同一个表空间中。实际上,lobindex的全部存储特征都是从lobsegment继承的。函数
前面的DBMS_METADATA返回的CREATE TABLE语句还包括如下内容:oop
LOB ("TXT") STORE AS BASICFILE (...ENABLE STORAGE IN ROW ...
这控制了LOB数据是否总与表分开存储(存储在lobsegment中),或是有时能够与表一同存储,而不用单独放在lobsegment中。若是设置了ENABLE STORAGE IN ROW,而不是DISABLE STORAGE IN ROW,小LOB(最多4,000字节)就会像VARCHAR2同样存储在表自己中。只有当LOB超过了4,000字节时,才会“移出”到lobsegment中。性能
默认行为是启用行内存储(ENABLE STORAGE IN ROW),并且通常来说,若是知道LOB老是能在表自己中放下,就应该采用这种默认行为,这样既能避免单独存储的开销,又能避免获取LOB时所需的物理I/O。
咱们将建立包括有两个LOB的表,其中一个LOB能够在行内存储数据,而另外一个LOB禁用了行内存储:
scott@ORCL>create table t 2 ( id int primary key, 3 in_row clob, 4 out_row clob 5 ) 6 lob (in_row) store as ( enable storage in row ) 7 lob (out_row) store as ( disable storage in row ) 8 / 表已建立。
在这个表中,咱们将插入一些串数据,全部这些串的长度都不超过4,000字节:
scott@ORCL>insert into t 2 select rownum, 3 owner || ' ' || object_name || ' ' || object_type || ' ' || status, 4 owner || ' ' || object_name || ' ' || object_type || ' ' || status 5 from all_objects 6 / 已建立72081行。 scott@ORCL>commit; 提交完成。
如今,想读取每一行,在此使用了DBMS_MONITOR包,并启用了SQL_TRACE(具体参见 tkprof 性能分析 ),执行这个工做时,能够看到这两个表获取数据时的性能:
SELECT IN_ROW FROM T WHERE ID = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 72081 1.09 1.28 0 0 0 0 Fetch 72081 1.18 1.11 0 216243 0 72081 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 144163 2.27 2.40 0 216243 0 72081 Misses in library cache during parse: 0 Optimizer mode: ALL_ROWS Parsing user id: 84 (recursive depth: 1) Rows Row Source Operation ------- --------------------------------------------------- 1 TABLE ACCESS BY INDEX ROWID T (cr=3 pr=0 pw=0 time=0 us cost=1 size=2015 card=1) 1 INDEX UNIQUE SCAN SYS_C0020634 (cr=2 pr=0 pw=0 time=0 us cost=1 size=0 card=1)(object id 85254) ******************************************************************************** SELECT OUT_ROW FROM T WHERE ID = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 72081 1.13 0.88 0 0 0 0 Fetch 72081 7.47 26.76 72081 504567 0 72081 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 144163 8.61 27.65 72081 504567 0 72081 Misses in library cache during parse: 0 Optimizer mode: ALL_ROWS Parsing user id: 84 (recursive depth: 1) Rows Row Source Operation ------- --------------------------------------------------- 1 TABLE ACCESS BY INDEX ROWID T (cr=3 pr=0 pw=0 time=0 us cost=1 size=253 card=1) 1 INDEX UNIQUE SCAN SYS_C0020634 (cr=2 pr=0 pw=0 time=0 us cost=1 size=0 card=1)(object id 85254) Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ Disk file operations I/O 1 0.00 0.00 direct path read 72008 0.18 18.79
获取IN_ROW列显著地快得多,并且所占用的资源也远远少于OUT_ROW列。能够看到,它使用了216,243次逻辑I/O(查询模式获取),而OUT_ROW列使用的逻辑I/O次数是它的两倍。这些额外的逻辑I/O 是对lobindex段的I/O(为了找到LOB的各个部分)。
另外,能够看到,对于OUT_ROW列,获取72081行会带来72081次物理I/O,而这会致使一样数目的“直接路径读”I/O等待。这些都是对非缓存LOB数据的读取。在这种状况下,经过启用LOB数据的缓存,能够缓解这个问题,可是这样一来,咱们又必须确保为此要有足够多的额外的缓冲区缓存。另外,若是确实有很是大的LOB,咱们可能并不但愿缓存这些数据。
这种行内/行外存储设置不只会影响读,还会影响修改。若是咱们要用小串更新前100行,并用小串插入100个新行,再使用一样的技术查看性能,会观察到:
scott@ORCL>alter system set timed_statistics=true scope=both; 系统已更改。 scott@ORCL>alter session set sql_trace = true; 会话已更改。 scott@ORCL>create sequence s start with 100000; 序列已建立。 scott@ORCL>declare 2 l_cnt number; 3 l_data varchar2(32765); 4 begin 5 dbms_monitor.session_trace_enable; 6 for i in 1 .. 100 7 loop 8 update t set in_row = 9 to_char(sysdate,'dd-mon-yyyy hh24:mi:ss') where id = i; 10 update t set out_row = 11 to_char(sysdate,'dd-mon-yyyy hh24:mi:ss') where id = i; 12 insert into t (id, in_row) values ( s.nextval, 'Hello World' ); 13 insert into t (id,out_row) values ( s.nextval, 'Hello World' ); 14 end loop; 15 end; 16 / PL/SQL 过程已成功完成。 scott@ORCL>show parameter user_dump_dest; NAME TYPE VALUE ------------------------ ------ ------------------------------------ user_dump_dest\diag\rdbm string d:\app\administrators\orcl\orcl\trace scott@ORCL> select username,sid,serial# from v$session where username='SCOTT'; USERNAME SID SERIAL# -------- ----- -------- SCOTT 11 108 scott@ORCL>select 'orcl_ora_'||spid||'.trc' from v$process where addr = (select paddr from v$session where sid=11); 'ORCL_ORA_'||SPID||'.TRC' -------------------------------------------------------------------------- orcl_ora_4000.trc scott@ORCL>alter session set sql_trace = false; 会话已更改。 scott@ORCL>exit 从 Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production With the Partitioning, OLAP, Data Mining and Real Application Testing options 断 开 C:\Users\Administrator>d: D:\>cd app\Administrator\diag\rdbms\orcl\orcl\trace D:\app\Administrator\diag\rdbms\orcl\orcl\trace>tkprof orcl_ora_4000.trc f:\2018 0906.txt TKPROF: Release 11.2.0.1.0 - Development on 星期四 9月 6 14:32:55 2018 Copyright (c) 1982, 2009, Oracle and/or its affiliates. All rights reserved. D:\app\Administrator\diag\rdbms\orcl\orcl\trace>
在获得的TKPROF报告中能够观察到相似的结果:
UPDATE T SET IN_ROW = TO_CHAR(SYSDATE,'dd-mon-yyyy hh24:mi:ss') WHERE ID = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 100 0.01 0.04 0 201 208 100 Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 101 0.01 0.04 0 201 208 100 Misses in library cache during parse: 1 Misses in library cache during execute: 1 Optimizer mode: ALL_ROWS Parsing user id: 84 (recursive depth: 1) Rows Row Source Operation ------- --------------------------------------------------- 0 UPDATE T (cr=3 pr=0 pw=0 time=0 us) 1 INDEX UNIQUE SCAN SYS_C0020634 (cr=2 pr=0 pw=0 time=0 us cost=1 size=2015 card=1)(object id 85254) Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ direct path write 84 0.00 0.02 asynch descriptor resize 163 0.00 0.00 ******************************************************************************** UPDATE T SET OUT_ROW = TO_CHAR(SYSDATE,'dd-mon-yyyy hh24:mi:ss') WHERE ID = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 100 0.01 0.11 3 1657 2606 100 Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 101 0.01 0.11 3 1657 2606 100 Misses in library cache during parse: 1 Misses in library cache during execute: 1 Optimizer mode: ALL_ROWS Parsing user id: 84 (recursive depth: 1) Rows Row Source Operation ------- --------------------------------------------------- 0 UPDATE T (cr=12 pr=1 pw=1 time=0 us) 1 INDEX UNIQUE SCAN SYS_C0020634 (cr=2 pr=0 pw=0 time=0 us cost=1 size=253 card=1)(object id 85254) Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ Disk file operations I/O 2 0.00 0.00 db file sequential read 1 0.02 0.02 direct path write 10 0.00 0.00 asynch descriptor resize 18 0.00 0.00
能够看到,行外LOB的更新占用了更多的资源。它要花必定的时间完成直接路径写(物理I/O),并执行更多的当前模式获取以及查询模式获取。这些都源于一点,即除了维护表自己外,还必须维护lobindex和lobsegment。INSERT操做也显示出了一样的差别:
INSERT INTO T (ID, IN_ROW) VALUES ( S.NEXTVAL, 'Hello World' ) call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 100 0.00 0.01 1 3 324 100 Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 101 0.00 0.01 1 3 324 100 Misses in library cache during parse: 1 Optimizer mode: ALL_ROWS Parsing user id: 84 (recursive depth: 1) Rows Row Source Operation ------- --------------------------------------------------- 0 LOAD TABLE CONVENTIONAL (cr=2 pr=1 pw=0 time=0 us) 1 SEQUENCE S (cr=1 pr=0 pw=0 time=0 us) Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ db file sequential read 3 0.01 0.01 direct path write 92 0.00 0.02 asynch descriptor resize 178 0.00 0.00 ******************************************************************************** INSERT INTO T (ID,OUT_ROW) VALUES ( S.NEXTVAL, 'Hello World' ) call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 100 0.03 0.10 1 1188 1947 100 Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 101 0.03 0.10 1 1188 1947 100 Misses in library cache during parse: 1 Optimizer mode: ALL_ROWS Parsing user id: 84 (recursive depth: 1) Rows Row Source Operation ------- --------------------------------------------------- 0 LOAD TABLE CONVENTIONAL (cr=11 pr=0 pw=1 time=0 us) 1 SEQUENCE S (cr=0 pr=0 pw=0 time=0 us) Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ direct path write 1 0.00 0.00
注意读和写使用的I/O都有所增长。总之,由此显示出,若是使用一个CLOB,并且不少串都能在“行内”放下(也就是说,小于4,000字节),那么使用默认的ENABLE STORAGE IN ROW设置就是一个不错的想法。
前面的DBMS_METADATA返回的CREATE TABLE语句包括如下内容:
LOB ("TXT") STORE AS BASICFILE (...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平均有7KB,而你使用的CHUNK大小为32KB,对于每一个LOB实例你都会“浪费”大约25KB的空间,另外一方面,假若使用8KB的CHUNK,就能使浪费减至最少。
还须要注意要让每一个LOB实例相应的CHUNK数减至最少。有一个lobindex用于指向各个块,块越多,索引就越大。若是有一个4MB的LOB,并使用8KB的CHUNK,你就至少须要512个CHUNK来存储这个消息。这也说明,至少须要512个lobindex条目指向这些CHUNK。另外,这还会影响获取性能,由于与读取更少但更大的CHUNK相比,如今要花更长的数据来读取和管理许多小CHUNK。咱们最终的目标是:使用一个能使“浪费”最少,同时又能高效存储数据的CHUNK大小。
控制LOB的读一致性。lobsegment并不使用undo来记录其修改;而是直接在lobsegment自己中维护信息的版本。lobindex会像其余段同样生成undo,可是lobsegment不会。相反,修改一个LOB时,Oracle会分配一个新的CHUNK,而且仍保留原来的CHUNK。若是回滚了事务,对LOB索引所作的修改会回滚,索引将再次指向原来的CHUNK。所以,undo维护会在LOB段自己中执行。修改数据时,原来的数据库保持不动,此外会建立新数据。
读LOB数据时这也很重要。LOB是读一致的,这与全部其余段同样。若是你在上午9:00获取一个LOB定位器,你从中获取的LOB数据就是“上午9:00那个时刻的数据”。这就像是你在上午9:00打开了一个游标(一个结果集)同样,所生成的行就是那个时间点的数据行。与结果集相似,即便别人后来修改了LOB数据。在此,Oracle会使用lobsegment,并使用logindex的读一致视图来撤销对LOB的修改,从而提取获取LOB定位器当时的LOB数据。它不会使用logsegment的undo信息,由于根本不会为logsegment自己生成undo信息。
能够很容易地展现LOB是读一致的,考虑如下这个小表,其中有一个行外LOB(存储在logsegment中):
scott@ORCL>create table t 2 ( id int primary key, 3 txt clob 4 ) 5 lob( txt) store as ( disable storage in row ) 6 / 表已建立。 scott@ORCL>insert into t values ( 1, 'hello world' ); 已建立 1 行。 scott@ORCL>commit; 提交完成。
若是取出LOB定位器,并在这个表上打开一个游标,以下:
scott@ORCL>declare 2 l_clob clob; 3 4 cursor c is select id from t; 5 l_id number; 6 7 begin 8 select txt into l_clob from t; ## 取出LOB定位器 9 open c; ## 并在这个表上打开一个游标 10 11 update t set id = 2, txt = 'Goodbye'; ## 而后修改行,并提交 12 commit; 13 #经过使用LOB定位器和打开的游标,会提供“获取LOB定位器或打开游标那个时间点”的数据 14 dbms_output.put_line( dbms_lob.substr( l_clob, 100, 1 ) ); 15 fetch c into l_id; 16 dbms_output.put_line( 'id = ' || l_id ); 17 close c; 18 end; 19 / hello world id = 1 PL/SQL 过程已成功完成。
可是数据库中的数据极可能已经更新/修改:
scott@ORCL>select * from t; ID TXT ---------- -------- 2 Goodbye
游标C的读一致映像来自undo段,而LOB的读一致映像则来自LOB段自己。
PCTVERSION控制着用于实现LOB数据版本化的已分配LOB空间的百分比(这些数据库块由某个时间点的LOB所用,并处在lobsegment的HWM如下)。对于许多使用状况来讲,默认设置12%就足够了,由于在不少状况下,只是要INSERT和获取LOB(一般不会执行LOB的更新;LOB每每会插入一次,而获取屡次)。所以,没必要为LOB版本化预留太多的空间(甚至能够没有)。
不过,若是应用确实常常修改LOB,假若频繁地读LOB,与此同时另外某个会话正在修改这些LOB,12%可能就过小了。若是处理LOB时遇到一个ORA-22924错误,解决方案不是增长undo表空间的大小,也不是增长undo保留时间(UNDO_RETENTION),若是你在使用手动undo管理,那么增长更多RBS空间也不能解决这个问题。而是应该使用如下命令:
ALTER TABLE tabname MODIFY LOB (lobname) ( PCTVERSION n );
并增长lobsegment中为实现数据版本化所用的空间大小。
前面的DBMS_METADATA返回的CREATE TABLE语句包括如下内容:
LOB ("TXT") STORE AS BASICFILE (... RETENTION ...
这个子句与PCTVERSION子句是互斥的,若是数据库中使用自动undo管理,就可使用这个子句。RETENTION子句在lobsegment中使用基于时间的机制来保留数据。数据库会设置参数UNDO_RETENTION,指定要把undo信息保留多长时间来保证一致读。在这种状况下,这个参数也适用于LOB数据。
不能使用这个子句来指定保留时间;而要从数据库的UNDO_RETENTION设置来继承它。
前面的DBMS_METADATA返回的CREATE TABLE语句包括如下内容:
LOB ("TXT") STORE AS BASICFILE (... NOCACHE ...
除了NOCACHE,这个选项还能够是CACHE或CACHE READS。这个子句控制了lobsegment数据是否存储在缓冲区缓存中。默认的NOCACHE指示,每一个访问都是从磁盘的一个直接读,相似地,每一个写/修改都是对大盘的一个直接写。CACHE READS容许缓存从磁盘读的LOB数据,可是LOB数据的写操做必须直接写至磁盘。CACHE则容许读和写时都能缓存LOB数据。
若是只有小规模或中等规模的LOB(例如,使用LOB来存储只有几KB的描述性字段),对其缓存就颇有意义。若是不缓存,当用户更新描述字段时,还必须等待I/O将数据写指磁盘(将执行一个CHUNK大小的I/O,并且用户要等待这个I/O完成)。若是你在执行多个LOB的加载,那么加载每一行时都必须等待这个I/O完成。因此启用执行LOB缓存很合理。能够打开和关闭缓存,来看看会有什么影响:
ALTER TABLE tabname MODIFY LOB (lobname) ( CACHE ); ALTER TABLE tabname MODIFY LOB (lobname) ( NOCACHE );
对于一个规模不少的初始加载,启用LOB的缓存颇有意义,这容许DBWR在后台将LOB数据写至磁盘,而客户应用能够继续加载更多的数据。对于频繁访问或修改的小到中等规模的LOB,缓存就很合理,能够部门让最终用户实时等待物理I/O完成。不过,对于一个大小为50MB的LOB,把它放在缓存中就没有道理了。
此时能够充分使用Keep池或回收池。并不是在默认缓存中将lobsegment数据与全部“常规”数据一同缓存,可使用保持池或回收池将其分开缓存。采用这种方式,既能缓存LOB数据,并且不影响系统中现有数据的缓存。
它有一个完整的存储子句,能够用来控制物理存储特征。这个存储子句一样适用于lobsegment和lobindex,对一个段的设置也能够用于另外一个段。假设有一个本地管理的表空间,LOB的相关设置将是FREELISTS、FREELIST GROUPS和BUFFER_POOL。FREELISTS和FREELIST GROUPS与表段的关系 一样适用于lobindex段,由于lobindex与其余索引段的管理是同样的。若是须要高度并发地修改LOB,可能最好在索引段上设置多个FREELISTS。
对LOB段使用保持池或回收池多是一个颇有用的技术,这样就能缓存LOB数据,并且不会“破坏”现有的默认缓冲区缓存。并非将LOB与常规表一同放在块缓冲区中,能够在SGA中专门为这些LOB对象预留一段专用的内存。BUFFER_POOL子句能够达到这个目的。
BFILE类型只是操做系统上一个文件的指针。它用于为这些操做系统文件提供只读访问。
使用BFILE时,还有使用一个Oracle DIRECTORY对象。DIRECTORY对象只是将一个操做系统目录映射至数据库中的一个“串”或一个名称(以提供可移植性;你可能想使用BFILE中的一个串,而不是操做系统特定的文件名约定)。做为一个小例子,下面建立一个带BFILE列的表,并建立一个DIRECTORY对象,再插入一行,其中引用了文件系统中的一个文件:
scott@ORCL>create table t 2 ( id int primary key, 3 os_file bfile 4 ) 5 / 表已建立。 scott@ORCL>create or replace directory my_dir as '/tmp/' 2 / 目录已建立。 scott@ORCL>insert into t values ( 1, bfilename( 'MY_DIR', 'test.dbf' ) ); 已建立 1 行。
如今,就能够把BFILE当成一个LOB来处理,由于它就是一个LOB。例如,能够作下面的工做:
scott@ORCL>create or replace directory MY_DIR as 'd:/test'; 目录已建立。 scott@ORCL>insert into t values ( 1, bfilename( 'MY_DIR', '11.txt')); 已建立 1 行。 scott@ORCL>select dbms_lob.getlength(os_file) from t; DBMS_LOB.GETLENGTH(OS_FILE) --------------------------- 28
能够看到所指定的文件大小为28kb。若是使用混合大小写或小写,会获得如下错误:
scott@ORCL>update t set os_file = bfilename( 'my_dir', '11.txt' ); 已更新 1 行。 scott@ORCL>select dbms_lob.getlength(os_file) from t; select dbms_lob.getlength(os_file) from t * 第 1 行出现错误: ORA-22285: 对不存在的目录或文件进行 GETLENGTH 操做 ORA-06512: 在 "SYS.DBMS_LOB", line 787
Oracle中的DIRECTORY对象是标识符,而默认状况下标识符都以大写形式存储。BFILENAME内置函数接受一个串,这个串的大小写必须与数据字典中存储的DIRECTORY对象的大小写彻底匹配。因此,必须在BFILENAME函数中使用大写,或者在建立DIRECTORY对象时使用加引号的标识符:
scott@ORCL>create or replace directory "my_dir" as 'd:/test'; 目录已建立。 scott@ORCL>select dbms_lob.getlength(os_file) from t; DBMS_LOB.GETLENGTH(OS_FILE) --------------------------- 28
不建议使用加引号的标识符;而倾向于在BFILENAME调用中使用大写。加引号的标识符属于“异类”,可能会在之后致使混淆。
BFILE在磁盘上占用的空间不定,这取决于DIRECTORY对象名的文件名的长度。
与其余LOB数据不一样,BFILE数据不是“读一致”的。因为BFILE在数据库以外管理,对BFILE解除引用时,不论文件上发生了什么,都会反映到获得的结果中。因此,若是反复读同一个BFILE,可能会产生不一样的结果,这与对CLOB、BLOB或NCLOB使用LOB定位器不一样。
ROWID是数据库中一行的地址。ROWID中编入了足够多的信息,足以在磁盘上找到行,以及标识ROWID所指向的对象(表等)。ROWID有一个“近亲”UROWID,它用于表,如IOT和经过异构数据库网关访问的没有固定ROWID表。UROWID是行主键值的一个表示,所以,其大小不定,这取决于它指向的对象。
每一个表中的每一行都有一个与之关联的ROWID或UROWID。从表中获取时,把它们看做为伪列(pseudo column),这说明它们并不真正存储在行中,而是行的一个推导属性。ROWID基于行的物理位置生成;它并不随行存储。UROWID基于行的主键生成,因此从某种意义上讲,好像它是随行存储的,可是事实上并不是如此,由于UROWID并不做为一个单独的列存在,而只是做为现有列的一个函数。
之前ROWID是不可变的。插入一行时,会为之关联一个ROWID(一个地址),并且这个ROWID会一直与该行关联,直到这一行被删除(被物理地从数据库删除)。可是,后来状况发生了变化,由于如今有些操做可能会致使行的ROWID改变,例如:
在分区表中更新一行的分区键,使这一行必须从一个分区移至另外一个分区。
使用FLASHBACK TABLE命令将一个数据库表恢复到之前的每一个时间点。
执行MOVE操做以及许多分区操做,如分解或合并分区。
使用ALTER TABLE SHRINK SPACE命令执行段收缩。
现在,因为ROWID可能过一段时间会改变(由于它再也不是不可变的),因此不建议把它们做为单独的列物理地存储在数据库表中。也就是说,使用ROWID做为一个数据库列的数据类型被认为是一种很差的实践作法。应当避免这种作法,而应使用行的主键(这应该是不可变的),另外引用完整性能够确保数据的完整性。对此用ROWID类型是作不到的,不能用ROWID建立从子表到一个父表的外键,并且不能保证跨表的完整性。你必须使用主键约束。
ROWID做为行的一个物理地址,要访问任何表中的某一行,这是最快的方法。若是应用从数据库读出数据并将其提供给最终用户,它试图更新这一行时就可使用ROWID。应用这种方式,只需最少的工做就能够更新当前行(例如,不须要索引查找再次寻找行),并经过验证行值未被修改来确保这一行与最初读出的行是同一行。因此,在采用乐观锁定的应用中ROWID仍是有用的。