oracle数值类型--LOB+ROWID/UROWID

Oracle中支持4种类型的LOB:
CLOB:字符LOB。存储大量的文本信息,如XML或者只是纯文本。这个数据类型需要进行字符集转换,也就是说,在获取时,这个字段中的字符会从数据库的字符集转换为客户的字符集,而在修改时会从客户的字符集转换为数据库的字符集。
NCLOB:这是另一种类型的字符LOB。存储在这一列中的数据所采用的字符集是数据库的国家字符集,而不是数据库的默认字符集。
BLOB:二进制LOB。存储大量的二进制信息,如字处理文档,图像换。应用向BLOB中写入什么位和字节,BLOB就会返回什么为和字节。
BFILE:二进制文件LOB。带BFILE列的数据库中存储的只是操作系统中某个文件的一个指针。这个文件在数据库之外维护,根本不是数据库的一部分。BFILE提供了文件内容的只读访问。

1 内部LOB

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列为我们透明地实现了这种主/明细结构。

1. LOB表空间

从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(从磁盘直接读,或者向磁盘直接写)

LOB可能是内联的(inline),或者存储在表中。在这种情况下,LOB数据会被缓存,但是这只适用于小于4,000字节的LOB。

lobindex和lobsegment总是会在同一个表空间中。实际上,lobindex的所有存储特征都是从lobsegment继承的。

2. IN ROW子句

前面的DBMS_METADATA返回的CREATE TABLE语句还包括以下内容:

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设置就是一个不错的想法。

3. CHUNK子句

前面的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大小。

4. PCTVERSION子句

控制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中为实现数据版本化所用的空间大小。

5. RETENTION子句

前面的DBMS_METADATA返回的CREATE TABLE语句包括以下内容:

LOB ("TXT") STORE AS BASICFILE (... RETENTION ...

这个子句与PCTVERSION子句是互斥的,如果数据库中使用自动undo管理,就可以使用这个子句。RETENTION子句在lobsegment中使用基于时间的机制来保留数据。数据库会设置参数UNDO_RETENTION,指定要把undo信息保留多长时间来保证一致读。在这种情况下,这个参数也适用于LOB数据。
不能使用这个子句来指定保留时间;而要从数据库的UNDO_RETENTION设置来继承它。

6. CACHE子句

前面的DBMS_METADATA返回的CREATE TABLE语句包括以下内容:

LOB ("TXT") STORE AS BASICFILE (... NOCACHE ...

除了NOCACHE,这个选项还可以是CACHECACHE 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数据,而且不影响系统中现有数据的缓存。

7. LOB STORAGE子句

它有一个完整的存储子句,可以用来控制物理存储特征。这个存储子句同样适用于lobsegment和lobindex,对一个段的设置也可以用于另一个段。假设有一个本地管理的表空间,LOB的相关设置将是FREELISTS、FREELIST GROUPS和BUFFER_POOL。FREELISTS和FREELIST GROUPS与表段的关系 同样适用于lobindex段,因为lobindex与其他索引段的管理是一样的。如果需要高度并发地修改LOB,可能最好在索引段上设置多个FREELISTS。
对LOB段使用保持池或回收池可能是一个很有用的技术,这样就能缓存LOB数据,而且不会“破坏”现有的默认缓冲区缓存。并不是将LOB与常规表一同放在块缓冲区中,可以在SGA中专门为这些LOB对象预留一段专用的内存。BUFFER_POOL子句可以达到这个目的。

2. BFILE

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/UROWID类型

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还是有用的。

转载于:https://my.oschina.net/u/1862478/blog/1936561

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值