LOB浅析(CLOB/BCLOB/NCLOB)

--LOB浅析(CLOB/BCLOB/NCLOB)

--Oracle中支持4种类型的LOB
/* 
CLOB:字符LOB,用于存储大量的文本信息。
NCLOB:另一种类型的字符LOB。
BCLOB:二进制LOB,用于存储大量的二进制信息。
BFILE:二进制文件LOB,一个指针。
 */
 
 --Oracle 11g中引入了一种称为SecureFile的新LOB架构,在此之前的称为BasicFile。
 
 --创建SecureFile
EODA@PROD1> set echo on
EODA@PROD1> 
EODA@PROD1> create table t
  2    ( id int primary key,
  3  	 txt clob
  4    )
  5  segment creation immediate
  6  /

Table created.

EODA@PROD1> 
EODA@PROD1> column column_name form a12
EODA@PROD1> column securefile form a12
EODA@PROD1> 
EODA@PROD1> select column_name, securefile from user_lobs where table_name='T';   --这个SQL验证创建的LOB是否是SecureFile

COLUMN_NAME  SECUREFILE
------------ ------------
TXT	     NO

EODA@PROD1> show parameter securefile

NAME				     TYPE	 VALUE
------------------------------------ ----------- ------------------------------
db_securefile			     string	 PERMITTED

/* 
DB_SECUREFILE specifies whether or not to treat LOB files as SecureFiles.
PERMITTED LOBs are allowed to be created as SecureFiles. 
并没有找到原因所以设置成FORCE,11g可能默认设置为BasicFile
*/

EODA@PROD1> alter session set db_securefile=FORCE;  

Session altered.

EODA@PROD1> drop table t purge;

Table dropped.

EODA@PROD1> create table t
  2    ( id int primary key,
  3  	 txt clob
  4    )
  5  segment creation immediate
  6  /

Table created.

EODA@PROD1> 
EODA@PROD1> column column_name form a12
EODA@PROD1> column securefile form a12
EODA@PROD1> 
EODA@PROD1> select column_name, securefile from user_lobs where table_name='T';

COLUMN_NAME  SECUREFILE
------------ ------------
TXT	     YES                    --此处创建成功

EODA@PROD1> 
EODA@PROD1> set long 10000
EODA@PROD1> select dbms_metadata.get_ddl( 'TABLE', 'T' )  from dual;  --查看LOB设置的选项

DBMS_METADATA.GET_DDL('TABLE','T')
--------------------------------------------------------------------------------

  CREATE TABLE "EODA"."T"
   (	"ID" NUMBER(*,0),
	"TXT" CLOB,
	 PRIMARY KEY ("ID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS"  ENABLE
   ) SEGMENT CREATION IMMEDIATE
  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS"
 LOB ("TXT") STORE AS SECUREFILE (
  TABLESPACE "USERS" ENABLE STORAGE IN ROW CHUNK 8192
  NOCACHE LOGGING  NOCOMPRESS  KEEP_DUPLICATES
  STORAGE(INITIAL 106496 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT))

--创建BasicFile
EODA@PROD1> show parameter secure

NAME				     TYPE	 VALUE
------------------------------------ ----------- ------------------------------
db_securefile			     string	 PERMITTED
EODA@PROD1> create table t
  2    ( id int primary key,
  3  	 txt clob
  4    )
  5  segment creation immediate
  6  /

Table created.

EODA@PROD1> 
EODA@PROD1> column column_name form a12
EODA@PROD1> column securefile form a12
EODA@PROD1> 
EODA@PROD1> select column_name, securefile from user_lobs where table_name='T';

COLUMN_NAME  SECUREFILE
------------ ------------
TXT	     NO

EODA@PROD1> 
EODA@PROD1> set long 10000
EODA@PROD1> select dbms_metadata.get_ddl( 'TABLE', 'T' )  from dual;

DBMS_METADATA.GET_DDL('TABLE','T')
--------------------------------------------------------------------------------

  CREATE TABLE "EODA"."T"
   (	"ID" NUMBER(*,0),
	"TXT" CLOB,
	 PRIMARY KEY ("ID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS"  ENABLE
   ) SEGMENT CREATION IMMEDIATE
  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS"
 LOB ("TXT") STORE AS BASICFILE (
  TABLESPACE "USERS" ENABLE STORAGE IN ROW CHUNK 8192 RETENTION
  NOCACHE LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT))

--查看LOB对象的段,通常被称为多段对象
EODA@PROD1> select segment_name, segment_type from user_segments;

SEGMENT_NAME		       SEGMENT_TY
------------------------------ ----------
T			       TABLE
SYS_C0018115		       INDEX 		   --支持主键约束
SYS_IL0000085725C00002$$       LOBINDEX	   --LOBINDEX用于从LOB找出数据的某些片段
SYS_LOB0000085725C00002$$      LOBSEGMENT  --实际的lob数据就存储在LOBSEGMENT中

--参数介绍
--1.TABLESPACE
LOB ("TXT") STORE AS BASICFILE ( TABLESPACE "USERS" ENABLE STORAGE IN ROW CHUNK 8192) RETENTION
这里的tablespace是用来存储LOBSEGMENT和LOBINDEX的表空间。
那么为什么要把LOB数据放到另一个表空间呢?因为LOB数据通常是一种规模很大的信息,如果表有百万行,每行有一个很大的LOB那么LOB就会非常庞大。
										 此时考虑到备份恢复以及空间管理,将表与LOB分离就是一个明智的选择。

--2.IN ROW子句
这控制了LOB数据是否总与表分开存储(LOBSEGMENT中)
如果设置了ENABLE STORAGE IN ROW,那么小LOB(最多4000字节)就会像VARCHAR2一样存储在表本身,超过则存放在LOBSEGMENT中。
如果设置了DISABLE STORAGE IN ROW则不会存储在表中。

--实验查看两种设置的性能差别
EODA@PROD1> set echo on
EODA@PROD1> 
EODA@PROD1> 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 )  --设置为enable
  7    lob (out_row) store as ( disable storage in row )  --设置为disable
  8  /

Table created.

EODA@PROD1> 
EODA@PROD1> insert into t   --插入一些字符串数据,不超过4000字节的数据
  2    select rownum,
  3  	      owner || ' ' || object_name || ' ' || object_type || ' ' || status,
  4  	      owner || ' ' || object_name || ' ' || object_type || ' ' || status
  5  	 from all_objects
  6  /

72931 rows created.

EODA@PROD1> 
EODA@PROD1> commit;

Commit complete.

EODA@PROD1> 
EODA@PROD1> alter session set tracefile_identifier='tk';

Session altered.

EODA@PROD1> EXEC DBMS_MONITOR.session_trace_enable;

PL/SQL procedure successfully completed.

EODA@PROD1> 
EODA@PROD1> declare        --分别逐行读取查看性能
  2  	       l_cnt	number;
  3  	       l_data	varchar2(32765);
  4  begin
  5  	       select count(*)
  6  		 into l_cnt
  7  		 from t;
  8  
  9  	       dbms_monitor.session_trace_enable;
 10  	       for i in 1 .. l_cnt
 11  	       loop
 12  		       select in_row  into l_data from t where id = i;  
 13  		       select out_row into l_data from t where id = i;
 14  	       end loop;
 15  end;
 16  /

PL/SQL procedure successfully completed.

EODA@PROD1> 
EODA@PROD1> EXEC DBMS_MONITOR.session_trace_disable;

PL/SQL procedure successfully completed.
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  72931      0.67       0.91          0          0          0           0
Fetch    72931      0.73       1.03          0     218793          0       72931
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total   145863      1.40       1.95          0     218793          0       72931

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  72931      0.56       0.78          0          0          0           0
Fetch    72931      9.50      13.67      72931     510520          0       72931
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total   145863     10.07      14.45      72931     510520          0       72931

--明显IN_ROW速度快很多,而且占用的资源也远远小于OUT_ROW列

--3.CHUNK子句
LOB数据存储在CHUNK中,LOB索引实际会指向CHUNK。CHUNK是逻辑上连续上连续的一组数据库块,是LOB最小分配单元。
**CHUNK自己只适用与BasicFile,虽然SecureFile也有同样语法不过仅用于向后兼容。
比如一个4MB的LOB实例,在使用8KB的CHUNK时,需要512个CHUNK来存储。

--4.RETENTION子句
securefile lob中没有RETENTION子句因为是自动设置的,而basicfile lob需要手动设置。
RETENTION子句用于LOB数据的读一致性。

/* 
LOB实现读一致性的方式与普通数据不同,LOBSEGMENT并不使用UNDO段来记录其修改,而是直接在LOBSEGMENT本身中维护信息的不同版本。
LOBINDEX会像其他段一样生成UNDO,但是LOBSEGMENT并不会。
当我们修改一个LOB时,Oracle会分配一个新的CHUNK,修改的数据写入新的CHUNK,但是原来的CHUNK还会保留。
如果你回滚了事务,那么LOB索引上的修改会回滚,这样索引就会再次指向原来的CHUNK,所以LOBSEGMENT的UNDO维护是自身执行的。 
*/

那么问题来了,如果不用UNDO段来存储回滚LOB所需要的信息,那么怎么避免ORA-1555?
这就是RENTENTION/PCTVERSION子句所起到的作用!
/*BasicFile*/如果我们给LOB设置了RETENTION子句,那么被修改的数据在LOB段中的保留期限就会采用参数UNDO_RETENTION的设置。
/*SecureFile*/默认设置持久的RETENTION子句
如果我们给LOB设置了PCTVERSION子句,这可以控制版本的LOB信息能占用多少现有空间(就是LOBSEGMENT的高水位线)。
**修改PCTVERSION alter table tablename modify lob(lobname) (pctversion n);

--5.CACHE子句
这个子句控制了LOBSEGMENT数据是否会存储在缓冲区缓存中,默认设置为NOCACHE。
如果你的LOB不大,那么默认的设置可能并不合适。
***对于LOB数据的缓存是使用传冲去KEEP池或RECYCLE池的一个绝佳机会,可以与其他常规数据个离开。

--参考来源《Oracle编程艺术深入理解 数据库 体系结构(第三版)》

你可能感兴趣的:(Oracle,Basic,Principle)