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