oracle數值類型--LOB+ROWID/UROWID

Oracle中支持4種類型的LOB:
CLOB:字符LOB。存儲大量的文本信息,如XML或者只是純文本。這個數據類型須要進行字符集轉換,也就是說,在獲取時,這個字段中的字符會從數據庫的字符集轉換爲客戶的字符集,而在修改時會從客戶的字符集轉換爲數據庫的字符集。
NCLOB:這是另外一種類型的字符LOB。存儲在這一列中的數據所採用的字符集是數據庫的國家字符集,而不是數據庫的默認字符集。
BLOB:二進制LOB。存儲大量的二進制信息,如字處理文檔,圖像換。應用向BLOB中寫入什麼位和字節,BLOB就會返回什麼爲和字節。
BFILE:二進制文件LOB。帶BFILE列的數據庫中存儲的只是操做系統中某個文件的一個指針。這個文件在數據庫以外維護,根本不是數據庫的一部分。BFILE提供了文件內容的只讀訪問。sql

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列爲咱們透明地實現了這種主/明細結構。session

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(從磁盤直接讀,或者向磁盤直接寫)app

LOB多是內聯的(inline),或者存儲在表中。在這種狀況下,LOB數據會被緩存,可是這隻適用於小於4,000字節的LOB。async

lobindex和lobsegment老是會在同一個表空間中。實際上,lobindex的全部存儲特徵都是從lobsegment繼承的。函數

2. IN ROW子句

前面的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設置就是一個不錯的想法。

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

相關文章
相關標籤/搜索