oracle t44,SecureFiles LOBs基础知识之存储篇
SecureFiles LOBs相比于BasicFiles
LOBs具有加密(encryption)、去重(deduplicaiton)、壓縮(compression)等新功能,pctversion,chunksize等參數也僅僅為了向后兼容而保留,因此SecureFiles LOBs的自適應能力更強,在管理上更為簡化,成為了clob、blob等大對象使用的首選,上面的這些功能描述可以參考官方文檔來獲得。
我們今天要討論的是SecureFiles LOBs里與存儲相關的知識,當你在使用SecureFiles LOBs的時候你是否了解它在磁盤上是如何存儲的,在創建SecureFiles LOBs時對于存儲有關的參數設定有何要求,lob如何管理自己的undo等問題,我們都將會通過實驗來為大家解答這些問題。
?Securefile LOBs字段所在數據塊的存儲結構
--建立測試用表為dump作準備
create table lobt1 (id number,c1
clob) lob(c1) store as securefile lobt1_c1(disable storage in row);
insert into lobt1
values(1,lpad('A',10,'A'));
insert into lobt1
values(2,lpad('B',10,'B'));
commit;
alter system flush buffer_cache;
select
dbms_rowid.rowid_to_absolute_fno(rowid,'SCOTT','LOBT1')
absfno,dbms_rowid.rowid_block_number(rowid) blkno,count(1) from LOBT1 group by
dbms_rowid.rowid_to_absolute_fno(rowid,'SCOTT','LOBT1'),dbms_rowid.rowid_block_number(rowid);
ABSFNO????? BLKNO
COUNT(1)
---------- ---------- ----------
131
723110????????? 2
alter system dump datafile 131 block 723110;
-- lobt1表dump結果節選:
tab 0, row 0, @0x1f6c
tl: 44 fb: --H-FL-- lb: 0x1? cc: 2
col
0: [ 2]? c1 02
col
1: [37]
00 54 00 01 02 0c 80
80 00 0200 00 00 01 00 04 29 37 3d a5 00 11 40 90 00
0b20 00 14 01 00 00 20 cb 0f 2f 01
LOB
Locator:
Length:??????? 84(37)
Version:??????? 1
Byte Length:??? 2
LobID: 00.00.00.01.00.04.29.37.3d.a5
Flags[ 0x02 0x0c 0x80 0x80 ]:
Type: CLOB
Storage: SecureFile
Characterset Format: IMPLICIT
Partitioned Table: No
Options: VaringWidthReadWrite
SecureFile Header:
Length:?? 17
Old Flag: 0x40 [ SecureFile ]
Flag 0:?? 0x90 [ INODE Valid ]
Layers:
Lengths Array: INODE:11
INODE:
20 00 14 01 00 00 20
cb 0f 2f 01
02 0c 80 80:字段類型是clob,如果是01 0c 00 80則為blob
00 00 00 01 00 04 29 37 3d a5:每一行都有一個唯一的一個lobid
00 11:securefile
header的長度為17,從00 11開始到行尾紅17
bytes
40:表示disable
storage in row securefile,如果是48表示enable storage in row securefile
00 0b:inode長度
14:lob字段中數據所占的字節數為,0x14代表20bytes,我們插入的10個字母占用20 bytes(使用AL16UTF16字符集)
20 cb 0f 2f:chunk所在的data block
address
01:以上述chunk地址為起始地址,所包含多少個連續的chunk
?SecureFiles LOBs對表與表空間存儲參數上的要求
// 1、Maxsize的最小值測試//
--創建一個表空間extent size設置為128K
create
tablespace ts128k1 datafile '/oradata06/ts128k1.dbf' size 128M extent
management local uniform size 128K segment space management auto;
--創建帶有lob字段的表,并把lob segment的maxsize設置為512k,結果報錯
create table tlob33
(id number, t33col2 clob) lob(t33col2) store as securefile tlob_tmp (tablespace
ts128k1 storage(maxsize 512K) disable storage in
row retention max);
ERROR at line 1:
ORA-60014:
invalid MAXSIZE storage option value
create table tlob33
(id number, t33col2 clob) lob(t33col2) store as securefile tlob_tmp (tablespace
ts128k1 storage(maxsize 768K) disable storage in row retention max);
ERROR at line 1:
ORA-60014:
invalid MAXSIZE storage option value
--逐級增加maxsize的大小,來測試create語句能否成功,直到maxsize=1024k才成功
create table
tlob33 (id number,t33col2 clob) lob(t33col2) store as securefile tlob_33
(tablespace ts128k1 storage(maxsize 1024K) disable storage in row retention
max);
Table created.
--記錄lob segment的大小等信息
select
bytes,segment_name from dba_segments where segment_name='TLOB_33';
BYTES SEGMENT_NAME
----------
---------------------------------------------------------------------------------
131072 TLOB_33
col segment_name
format a20
col segment_type
format a10
col
tablespace_name format a15
set linesize 150
select
segment_name,segment_type,tablespace_name,extent_id,file_id,relative_fno,block_id,blocks,bytes
from dba_extents where segment_name='TLOB_33' and tablespace_name='TS128K1';
SEGMENT_NAME??? ?????SEGMENT_TY TABLESPACE_NAME? EXTENT_ID
FILE_ID RELATIVE_FNO?? BLOCK_ID???? BLOCKS
BYTES
--------------------
---------- --------------- ---------- ---------- ------------ ----------
---------- ----------
TLOB_33????????????? LOBSEGMENT TS128K1 ?????????????????0?????? 1036?????????? 13??????? 128???????? 16
131072
// 2、表空間extent_size的最小值測試//
--新創建一個表空間uniform
size縮小至64k,觀察一下securefile對extent size大小是否有要求
create
tablespace ts128k2 datafile '/oradata06/ts128k2.dbf' size 128M extent
management local uniform size 64K segment space management auto;
--下面的錯誤表明Secure
file對于表空間的extent size要求至少為112k(14*8k),而實際Extent size只有64k(8*8k),創建不成功
create table
tlob44 (id number,t44col2 clob) lob(t44col2) store as securefile tlob_44 (tablespace
ts128k2 storage(maxsize 1024K) disable storage in row retention max);
ERROR at line 1:
ORA-60019:
Creating initial extent of size 14 in tablespace of extent size 8
--新建一個表空間uniform
size設置為112k,觀察表空間的extent_size最小設為多少
create
tablespace ts128k3 datafile '/oradata06/ts128k3.dbf' size 128M extent
management local uniform size 112K segment space management auto;
--這回雖然112K了,但報了個ORA-00600錯誤
create table
tlob55 (id number,t55col2 clob) lob(t55col2) store as securefile tlob_55
(tablespace ts128k3 storage(maxsize 2128K) disable storage in row retention
max);
SQL> create
table tlob55 (id number,t55col2 clob) lob(t55col2) store as securefile tlob_55
(tablespace ts128k3 storage(maxsize 2128K) disable storage in row retention
max);
create table
tlob55 (id number,t55col2 clob) lob(t55col2) store as securefile tlob_55
(tablespace ts128k3 storage(maxsize 2128K) disable storage in row retention
max)
*
ERROR at line 1:
ORA-00600:
internal error code, arguments: [ktsladdfcb-bsz], [3], [], [], [],
[], [], [], [], [],
[], []
--測試下來最小的Extent
size必須是8k*14+1=114689,因為一定要是8k的整數倍,實際就是120K(8k*15)作為最小的extent size
drop tablespace
ts128k3 including contents and datafiles;
create
tablespace ts128k3 datafile '/oradata06/ts128k3.dbf' size 128M extent
management local uniform size 114689 segment space management auto;
Tablespace
created.
drop tablespace
ts128k3 including contents and datafiles;
create
tablespace ts128k3 datafile '/oradata06/ts128k3.dbf' size 128M extent
management local uniform size 120k segment space
management auto;
Tablespace
created.
drop table
tlob55;
create table
tlob55 (id number,t55col2 clob) lob(t55col2) store as securefile tlob_55
(tablespace ts128k3 storage(maxsize 2128K) disable storage in row retention
max);
Table created.
--下面來看一下為何oracle對存放lob
segment的extent size大小有最低要求,以第一個測試中建立的TLOB_33這個segment為例,dump一下
alter system
dump datafile 1036 block min 128 block max 143;
--從dump出來的內容里過濾出每個block的用途發現其中16個blocks中有11個是存放metadata的,我們知道lob是自己管理undo的所以這些都是存儲上花費的開銷,還有5個是存放數據用的
frmt: 0x02
chkval: 0x798b type: 0x45=NGLOB: Lob Extent Header
frmt: 0x02
chkval: 0xa7e5 type: 0x3f=NGLOB: Segment Header
frmt: 0x02
chkval: 0x798f type: 0x3d=NGLOB: Hash Bucket
frmt: 0x02
chkval: 0x798e type: 0x3d=NGLOB: Hash Bucket
frmt: 0x02
chkval: 0x798b type: 0x3d=NGLOB: Hash Bucket
frmt: 0x02
chkval: 0x07f8 type: 0x3d=NGLOB: Hash Bucket
frmt: 0x02
chkval: 0x798b type: 0x3d=NGLOB: Hash Bucket
frmt: 0x02
chkval: 0x798b type: 0x3d=NGLOB: Hash Bucket
frmt: 0x02
chkval: 0x7983 type: 0x3d=NGLOB: Hash Bucket
frmt: 0x02
chkval: 0x7983 type: 0x3d=NGLOB: Hash Bucket
frmt: 0x02
chkval: 0x7983 type: 0x3d=NGLOB: Hash Bucket
frmt: 0x02
chkval: 0x012d type: 0x00=unknown
frmt: 0x02
chkval: 0x012a type: 0x00=unknown
frmt: 0x02
chkval: 0x012b type: 0x00=unknown
frmt: 0x02 chkval:
0x0128 type: 0x00=unknown
frmt: 0x02
chkval: 0x0129 type: 0x00=unknown
結論1:securefiles LOBs的最小大小必須>=1024k,其所在表空間的最小extent
size為15個blocks,對于blocksize=8k來說,1個extent size就是120k
?SecureFiles LOBs在磁盤上的存儲方式
Securefiles LOBs存儲方式和BasicFiles LOBs一樣有兩種,一種是inline
storage,另一種是out-of-line storage。我們知道對于BasicFiles LOBs來說如果lob字段長度<=3964個字節是和表存儲在一起的,稱為inline storage;>3964字節時會遷移到lob segment里,即out-of-line storage。對于SecureFiles LOBs來說這個值是多少?我們下面測試一下。
在測試之前有個概念需要明確一下,對于CLOB類型的字段其存儲時所用的字符集有可能與數據庫本身的字符集不一致,對于使用US7ASCII、WE8ISO8859P1等定長字符集的數據庫來說clob字段存儲時使用的字符集和數據庫字符集一致;對于使用ZHS16GBK、UTF8等變長字符集的數據庫clob字段存儲時使用的字符集為UCS2(9i及以下版本)或者AL16UTF16(10g及以上版本),UCS2、AL16UTF16都是定長的,長度為2bytes,舉個例子對于字符A來說存儲到varchar2字段占用1個字節,存儲到clob字段時就會占用2個字節。以下測試數據庫使用的是ZHS16GBK字符集
//
3、SecureFiles inline storage &out-of-line storage界限測試//
--創建測試表
create table tsec_lob (id number,secol2 clob) lob(secol2)
store as securefile lob_tsec (retention);
--先插入1982個字符,每個字符兩個字節,總共占用是3964 bytes
insert into tsec_lob values(1,lpad('D',1982,'D'));
commit;
--dump出數據塊的內容
select table_name,segment_name,securefile from dba_lobs
where table_name='TSEC_LOB';
TABLE_NAME???????????????????? SEGMENT_NAME?????????????????? SEC
------------------------------
------------------------------ ---
TSEC_LOB?????????????????????? LOB_TSEC?????????????????????? YES
select
dbms_rowid.rowid_block_number(rowid),dbms_rowid.rowid_relative_fno(rowid) from
tsec_lob;
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID)
------------------------------------
------------------------------------
4284
302
select * from v$diag_info where name='Default Trace
File';?? --shzw1_ora_54592144.trc
alter system flush buffer_cache;
alter system dump datafile 302 block 4284;
--dump節選,目前字段總長度為3995bytes,其中前面31個bytes為metadata
block_row_dump:
tab 0, row 0, @0xff4
tl: 4004 fb: --H-FL-- lb: 0x1
cc: 2
col? 0: [ 2]? c1 02
col? 1: [3995]
00 54 00 01 02 0c 80 80 00 02 00 00 00 01 00 04 28
c9 b5 05 0f 87 48 90 0f
81 01 00 0f 7c 0100 44 00
44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00
44 00 44 00 44 00 44
00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44
。。。。。。此處略去,一共有1982個”00 44”
--根據上面的推斷4000-31=3969,如果字段里真正數據占用的長度達到3969 bytes時就會遷移到lob segments上去,在原來基礎上增加兩個字符的長度,總長度達到3999 bytes,其中數據部分為3968 bytes,Byte Length: 2表示一個字符占據2 bytes
update tsec_lob set
secol2=lpad('D',1984,'D');
commit;
alter system flush buffer_cache;
select * from v$diag_info where
name='Default Trace File';
--shzw1_ora_24903952.trc
alter system dump datafile 302 block 4284;
--shzw1_ora_24903952.trc內容
data_block_dump,data header at 0x11085d264
===============
tsiz: 0x1f98
hsiz: 0x14
pbl: 0x11085d264
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x4c
avsp=0xfdc
tosp=0xfdc
0xe:pti[0]????? nrow=1
offs=0
0x12:pri[0]???? offs=0x4c
block_row_dump:
tab 0, row 0, @0x4c
tl: 4008 fb: --H-FL-- lb: 0x2? cc: 2
col
0: [ 2]? c1 02
col
1: [3999]
00
54 00 01 02 0c 80 80 00 02 00 00 00 01 00 04 28 c9 b5 9b 0f 8b 48 90 0f
85
01 00 0f 80 01 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00
44
00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44
。。。省略
00
44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44 00 44
LOB
Locator:
Length:??????? 84(3999)
Version:??????? 1
Byte Length:??? 2
LobID: 00.00.00.01.00.04.28.c9.b5.9b
Flags[ 0x02 0x0c 0x80 0x80 ]:
Type: CLOB
Storage: SecureFile
Characterset Format: IMPLICIT
Partitioned Table: No
Options: VaringWidthReadWrite
SecureFile Header:
Length:?? 3979
Old Flag: 0x48 [ DataInRow SecureFile ]
Flag 0:?? 0x90 [ INODE Valid ]
Layers:
Lengths Array: INODE:3973
INODE:
--再增加一個字符的長度,發現字段會挪出table,放到數據dba:0x4b80b56f中,由此判斷securefile字節數超過3969bytes時就會從in-line storage =>out-of-line storage
update tsec_lob set
secol2=lpad('D',1985,'D');
commit;
alter system flush buffer_cache;
alter system dump datafile 302 block
4284;?? --shzw1_ora_24903952.trc
--shzw1_ora_24903952.trc內容
tab 0, row 0, @0x1f
tl: 45 fb: --H-FL-- lb: 0x1? cc: 2
col
0: [ 2]? c1 02
col
1:[38]
00
54 00 01 02 0c 80 80 00 02 00 00 00 01 00 04 28 c9 b5 9c 00 12 40 90 00
0c
21 00 0f 82 01 00 01 4b 80 b5 6f 01
LOB
Locator:
Length:??????? 84(38)
Version:??????? 1
Byte Length:??? 2
LobID: 00.00.00.01.00.04.28.c9.b5.9c
Flags[ 0x02 0x0c 0x80 0x80 ]:
Type: CLOB
Storage: SecureFile
Characterset Format: IMPLICIT
Partitioned Table: No
Options: VaringWidthReadWrite
SecureFile Header:
Length:?? 18
Old Flag: 0x40 [ SecureFile ]
Flag 0:?? 0x90 [ INODE Valid ]
Layers:
Lengths Array: INODE:12
INODE:
21 00 0f 82 01 00 01 4b 80 b5 6f 01
--將inode中的4b 80 b5 6f轉換成rdba地址,dump數據塊內容
select
dbms_utility.data_block_address_File(to_number(replace('4b 80 b5 6f','
'),'xxxxxxxx')) rfno
,dbms_utility.data_block_address_block(to_number(replace('4b 80 b5 6f','
'),'xxxxxxxx')) blkno from dual;
RFNO????? BLKNO
---------- ??----------
302????? 46447
select * from v$diag_info where
name='Default Trace File';
--shzw1_ora_3146348.trc
alter system flush buffer_cache;
alter system dump datafile 302 block
46447;
--shzw1_ora_3146348.trc內容,證明lob字段的內容已經遷移到lob
segment里
seg/obj: 0x51a682? csc: 0xb89.32fd27ef? itc: 1
flg: E? typ: 5 - LOCAL LOBS
fsl: 0? fnx: 0xffffffff ver: 0x01
Itl
Xid????????????????? Uba???????? Flag
Lck??????? Scn/Fsc
0x01
0x0406.01d.0019e032? 0x00000000.0000.00? -B--
0? fsc 0x0000.00000000
========
bdba
[0x4b80b56f]
kdlich
[11085d24c 56]
flg0? 0x20 [ver=0 typ=data lock=n]
flg1? 0x00
scn?? 0x0b89.32fd27ef
lid?? 00000001000428c9b59c
rid?? 0x00000000.0000
kdlidh
[11085d264 24]
flg2? 0x00 [ver=0 lid=short-rowid
hash=n cmap=n pfill=n]
flg3? 0x00
pskip 0
sskip 0
hash
0000000000000000000000000000000000000000
hwm?? 3970
spr?? 0
data? [11085d280 52 8060]
00 44 00 44 00 44 00 44 00 44 00 44 00 44
00 44 00 44 00 44 00 44 00 44 00 44
00 44 00 44 00 44 00 44 00 44 00 44 00 44
00 44 00 44 00 44 00 44 00 44 00 44
。。。省略
00 44 00 44 00 44 00 44 00 44 00 44 00 44
00 44 00 44 00 4f 00 47 00 56 00 41
結論2:securefiles LOBs inline存儲的字節數為<=3969
bytes,>3969 bytes時會整體遷移到lobsegment里,注意對于CLOB而言這個字節數僅僅表示存儲上所占用的空間,具體對應多少個字符,還要根據數據庫所采用的字符集來確定,對于ZHS16GBK等變長字符集的數據庫來說,1個字符在存儲上要占用兩個字節,所以字符數<=1984時為inline存儲,>1984時為out-of-line存儲,對于其它定長字符集的數據庫則不存在這個轉換關系,對于BLOB類型的字段由于存儲的都是二進制數據所以也無需進行換算
?如何通過chunk
address找到SecureFiles LOBs
SecureFiles LOBs是以chunk為單位存儲的,要找到chunk必須先有chunk
address,chunk address的存放有直接和間接兩種方式,直接方式是指chunk address就保存在表里,通過這個chunk address能直接找到包含數據的chunk,但當一個SecureFiles LOBs較大且占用的空間連續性不是很好的時候就會以間接方式存放,間接方式是指表里的block指向包含chunk address列表的另外一個block,由這另外一個block去指向包含數據的chunk。看下面的圖就很清楚了。
//
4、直接方式尋址//
--創建測試表,disable storage in row
create
tablespace lobtest_out datafile '/oradata06/lobtest_out.dbf' size 128m extent
management local uniform size 128k segment space management auto;
drop table
tout_lob;
create table
tout_lob (id number,outcol2 clob) lob(outcol2) store as securefile lob_out
(tablespace lobtest_out disable storage in row retention);
--為了能占用較多的chunk,采用loadclobfromfile將文本文件內容導入的方式,impmd.log.load.lob文件大小為1571061字節,使用存儲過程進行導入
declare
v_bfile
bfile:=bfilename('HISDMP','impmd.log.load.lob');
v_clob clob;
ncycle
integer:=1;
i integer:=1;
v_dest_offset
integer:=1;
v_src_offset
integer:=1;
v_lang_context
integer:=0;
v_warning
integer;
begin
dbms_lob.fileopen(v_bfile);
v_clob:=empty_clob();
dbms_lob.createtemporary(v_clob,FALSE,dbms_lob.session);
dbms_lob.loadclobfromfile(v_clob,v_bfile,amount=>dbms_lob.lobmaxsize,dest_offset=>v_dest_offset,src_offset=>v_src_offset,bfile_csid=>0,lang_context=>v_lang_context,warning=>v_warning);
while ( i <=
ncycle)
loop
insert into
tout_lob values(i,v_clob);
i:=i+1;
end loop;
dbms_lob.fileclose(v_bfile);
end;
/
--導入完成后對表進行dump
select * from
v$diag_info where name='Default Trace File';
--shzw1_ora_53936650.trc
alter system
flush buffer_cache;
select dbms_rowid.rowid_to_absolute_fno(rowid,'SCOTT','TOUT_LOB')
absfno,dbms_rowid.rowid_relative_fno(rowid)
rfno,dbms_rowid.rowid_block_number(rowid) blkno,count(1) from tout_lob group by
dbms_rowid.rowid_to_absolute_fno(rowid,'SCOTT','TOUT_LOB'),dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid);
ABSFNO
RFNO????? BLKNO?? COUNT(1)
----------
---------- ---------- ----------
115??????? 115
441493????????? 1
select
id,dbms_lob.getlength(outcol2) from tout_lob;
ID
DBMS_LOB.GETLENGTH(OUTCOL2)
----------
---------------------------
1???????????????????? 1571061
alter system
dump datafile 115 block 441493;
--shzw1_ora_53936650.trc
dump內容,其中03 80 02 a1記錄的是包含chunk地址列表的dba地址
tab 0, row 0,
@0x1f6d
tl: 43 fb:
--H-FL-- lb: 0x1? cc: 2
col? 0: [ 2]
c1 02
col? 1: [36]
00 54 00 01 02 0c 80 80 00 02 00 00 00 01 00
04 28 c9 b8 ed 00 10 40 90 00
0a 42 00 2f f1 ea 01 03
80 02 a1
LOB
Locator:
Length:
84(36)
Version:??????? 1
Byte Length:??? 2
LobID: 00.00.00.01.00.04.28.c9.b8.ed
Flags[ 0x02 0x0c 0x80 0x80 ]:
Type: CLOB
Storage: SecureFile
Characterset Format: IMPLICIT
Partitioned Table: No
Options: VaringWidthReadWrite
SecureFile Header:
Length:
16
Old Flag: 0x40 [ SecureFile ]
Flag 0:
0x90 [ INODE Valid ]
Layers:
Lengths Array: INODE:10
INODE:
42 00 2f f1 ea 01 03 80 02 a1
--將03 80 02 a1轉換為rdba地址
select
dbms_utility.data_block_address_File(to_number(replace('03 80 02 a1',' '),'xxxxxxxx'))
rfno ,dbms_utility.data_block_address_block(to_number(replace('03 80 02 a1','
'),'xxxxxxxx')) blkno from dual;
RFNO
BLKNO
----------
----------
14??????? 673
select
name,file#,rfile# from v$datafile where rfile#=14;
NAME???? ?????????????????????????????????????FILE#???? RFILE#
----------------------------------------
---------- ----------
/oradata02/undo/undo202.dbf????????????????????? 14???????? 14
/oradata06/lobtest_out.dbf???????????????????? 1037???????? 14
---證明1037/673位于lob segment
select
segment_name,segment_type,tablespace_name,extent_id,file_id,relative_fno,block_id,blocks,bytes
from dba_extents where segment_name='LOB_OUT' and block_id<=673 and
block_id+blocks>=673;
SEGMENT_NAME???????? SEGMENT_TY TABLESPACE_NAME ?EXTENT_ID
FILE_ID RELATIVE_FNO
BLOCK_ID???? BLOCKS????? BYTES
--------------------
---------- --------------- ---------- ---------- ------------ ----------
---------- ----------
LOB_OUT????????????? LOBSEGMENT LOBTEST_OUT???????????? 33?????? 1037
14??????? 672???????? 16
131072
--對1037/673進行dump的結果顯示一共使用了390個block,從dba:0x038000a7開始
select * from
v$diag_info where name='Default Trace File';
--shzw1_ora_8652048.trc
alter system
dump datafile 1037 block 673; --shzw1_ora_8652048.trc
--shzw1_ora_8652048.trc
dump內容節選
Itl
Xid????????????????? Uba???????? Flag
Lck??????? Scn/Fsc
0x01?? 0x0405.018.00165fd5? 0x0105a321.128c.10? ----
1? fsc 0x0000.00000000
========
bdba??? [0x038002a1]
kdlich? [11085d24c 56]
flg0
0x18 [ver=0 typ=lhb lock=y]
flg1
0x00
scn
0x0b89.32fddadb
lid
00000001000428c9b8ed
rid
0x00000000.0000
kdlihh? [11085d264 24]
flg2
0x00 [ver=0 lid=short-rowid hash=n it=n bt=n xfm=n ovr=n aux=n]
flg3
0x80 [vll=y]
flg4
0x00
flg5
0x00
hash
0000000000000000000000000000000000000000
llen
0.3142122
ver
0.1
#ext
40
asiz
40
hwm
40
ovr
0x00000000.0
dba0
0x00000000
dba1
0x00000000
dba2
0x00000000
dba3
0x00000000
auxp
0x00000000
ldba
0x03800251
nblk? 390
[0] 0x00 0x00 9 0x038000a7
[1] 0x00 0x00 3 0x038000bd
[2] 0x00 0x00 2 0x038000a5
[3] 0x00 0x00 15 0x038000c1
[4] 0x00 0x00 9 0x038000d7
[5] 0x00 0x00 6 0x038000d1
[6] 0x00 0x00 9 0x038000e7
[7] 0x00 0x00 6 0x038000e1
[8] 0x00 0x00 9 0x038000f7
[9] 0x00 0x00 6 0x038000f1
[10] 0x00 0x00 15 0x03800101
[11] 0x00 0x00 9 0x03800117
[12] 0x00 0x00 6 0x03800111
[13] 0x00 0x00 9 0x03800127
[14] 0x00 0x00 6 0x03800121
[15] 0x00 0x00 9 0x03800137
[16]
0x00 0x00 6 0x03800131
[17] 0x00 0x00 9 0x03800147
[18] 0x00 0x00 6 0x03800141
[19] 0x00 0x00 15 0x03800151
[20] 0x00 0x00 15 0x03800161
[21] 0x00 0x00 9 0x03800177
[22] 0x00 0x00 6 0x03800171
[23] 0x00 0x00 9 0x03800187
[24] 0x00 0x00 6 0x03800181
[25] 0x00 0x00 15 0x03800191
[26] 0x00 0x00 9 0x038001a7
[27] 0x00 0x00 6 0x038001a1
[28] 0x00 0x00 15 0x038001b1
[29] 0x00 0x00 9 0x038001c7
[30] 0x00 0x00 6 0x038001c1
[31] 0x00 0x00 15 0x038001d1
[32] 0x00 0x00 15 0x038001e1
[33] 0x00 0x00 15 0x03800201
[34] 0x00 0x00 15 0x038001f1
[35] 0x00 0x00 15 0x03800211
[36] 0x00 0x00 15 0x03800221
[37] 0x00 0x00 15 0x03800231
[38] 0x00 0x00 15 0x03800241
[39] 0x00 0x00 1 0x03800251
--dump一下0x038000a7的內容,包含的就是impmd.log.load.lob文件開頭的4030個字符
alter system
dump datafile 1037 block 167; --shzw1_ora_54067770.trc
--dump內容節選:
hwm
8060
spr?? 0
data
[11085d280 52 8060]
00 0a 00 43 00
6f 00 6e 00 6e 00 65 00 63 00 74 00 65 00 64 00 20 00 74 00 6f
00 3a 00 20 00
4f 00 72 00 61 00 63 00 6c 00 65 00 20 00 44 00 61 00 74 00 61
//
5、滿足什么條件會采用間接方式尋址//
--新建測試表,準備好大小為82957
bytes的文本文件impmd.log.load.12c1
drop table
tout_lob;
create table
tout_lob (id number,outcol2 clob) lob(outcol2) store as securefile lob_out
(tablespace lobtest_out disable storage in row retention);
--執行存儲過程用impmd.log.load.12c1文件填充lob字段
declare
v_bfile
bfile:=bfilename('HISDMP','impmd.log.load.12c1');
v_clob clob;
ncycle
integer:=1;
i integer:=1;
v_dest_offset
integer:=1;
v_src_offset
integer:=1;
v_lang_context
integer:=0;
v_warning
integer;
begin
dbms_lob.fileopen(v_bfile);
v_clob:=empty_clob();
dbms_lob.createtemporary(v_clob,FALSE,dbms_lob.session);
dbms_lob.loadclobfromfile(v_clob,v_bfile,amount=>dbms_lob.lobmaxsize,dest_offset=>v_dest_offset,src_offset=>v_src_offset,bfile_csid=>0,lang_context=>v_lang_context,warning=>v_warning);
while ( i <=
ncycle)
loop
insert into
tout_lob values(i,v_clob);
i:=i+1;
end loop;
dbms_lob.fileclose(v_bfile);
end;
/
--填充后dump
tout_lob表內容
select * from
v$diag_info where name='Default Trace File';
--shzw1_ora_11994174.trc
alter system
flush buffer_cache;
select
dbms_rowid.rowid_to_absolute_fno(rowid,'SCOTT','TOUT_LOB') absfno,dbms_rowid.rowid_relative_fno(rowid)
rfno,dbms_rowid.rowid_block_number(rowid) blkno,count(1) from tout_lob group by
dbms_rowid.rowid_to_absolute_fno(rowid,'SCOTT','TOUT_LOB'),dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid);
ABSFNO
RFNO????? BLKNO?? COUNT(1)
----------
---------- ---------- ----------
441??????? 441
17562????????? 1
alter system
dump datafile 441 block 17562;
--shzw1_ora_11994174.trc內容,一共使用了21個blocks,分別是0x038000a9起始的7個blocks、0x038000bd起始的3個blocks、0x038000a5起始的4個blocks、0x038000ca起始的6個block、0x038000c1起始的1個block,采用的是直接尋址的方式
tab 0, row 0,
@0x1f52
tl: 70 fb:
--H-FL-- lb: 0x1? cc: 2
col? 0: [ 2]
c1 02
col? 1: [63]
00 54 00 01 02 0c 80 80 00 02 00 00 00 01 00
04 28 ca e5 51 00 2b 40 90 00
25 22 00 02 88 1a 01 04 01 03 80 00 a9 07 01 03 80 00 bd 03 01 03 80 00 a5
04 01 03 80 00 ca 06 01 03 80 00 c1 01
--為了模擬出間接尋址的情況,使用如下過程往lob字段里每次增加4000字符,每個字符占用兩個字節,所以每追加一次最多只會占用一個block,便于我們觀察效果
select
id,dbms_lob.getlength(outcol2) from tout_lob;
ID DBMS_LOB.GETLENGTH(OUTCOL2)
----------
---------------------------
182957
--下面的過程執行6次,每次執行后都dump一下觀察是否轉為了間接尋址
declare
v_buffer
varchar2(6000):=lpad('AB',4000,'AB');
v_clob clob;
i integer:=0;
ncycle
integer:=1;
begin
select outcol2
into v_clob from tout_lob for update;
while (i <
ncycle) loop
dbms_lob.writeappend(v_clob,4000,v_buffer);
i:=i+1;
end loop;
commit;
end;
/
--最新一次執行后的dump結果結果如下,blocks數量已經增加到了27個,表里存放的
chunk address數量已經達到了6個,使用的還是直接尋址:
select * from
v$diag_info where name='Default Trace File'; --shzw1_ora_11994176.trc
alter system
dump datafile 441 block 17562;
tab 0, row 0,
@0x1f06
tl: 76 fb:
--H-FL-- lb: 0x1? cc: 2
col? 0: [ 2]
c1 02
col? 1: [69]
00 54 00 01 02 0c 80 80 00 02 00 00 00 01 00
04 28 ca e5 51 00 31 40 90 00
2b 22 00 03 43 9a 07 05 01 03 80 00 a9 07 01 03 80 00 bd0301 03 80 00 a5
04 01 03 80 00 ca 06 01 03 80 00 c1 02 01 03 80 00 c4 05
**最后再執行一次存儲過程后,dump
441/17562的結果里終于呈現出間接尋址的跡象了
--再執行一次過程
declare
v_buffer
varchar2(6000):=lpad('AB',4000,'AB');
v_clob clob;
i integer:=0;
ncycle
integer:=1;
begin
select outcol2
into v_clob from tout_lob for update;
while (i <
ncycle) loop
dbms_lob.writeappend(v_clob,4000,v_buffer);
i:=i+1;
end loop;
commit;
end;
/
--block
441/17562 dump結果,明顯可以看出地址變短了,原來存放的6個chunk address變成了0x038000e1這一個地址
tab 0, row 0,
@0x1edb
tl: 43 fb:
--H-FL-- lb: 0x2? cc: 2
col? 0: [ 2]
c1 02
col? 1: [36]
00 54 00 01 02 0c 80 80 00 02 00 00 00 01 00
04 28 ca e5 51 00 10 40 90 00
0a 42 00 03 df da 08 03
80 00 e1
LOB
Locator:
Length:
84(36)
Version:??????? 1
Byte Length:??? 2
LobID: 00.00.00.01.00.04.28.ca.e5.51
Flags[ 0x02 0x0c 0x80 0x80 ]:
Type: CLOB
Storage: SecureFile
Characterset Format: IMPLICIT
Partitioned Table: No
Options: VaringWidthReadWrite
SecureFile Header:
Length:
16
Old Flag: 0x40 [ SecureFile ]
Flag 0:
0x90 [ INODE Valid ]
Layers:
Lengths Array: INODE:10
INODE:
42 00 03 df da 08 03 80 00 e1
-- dba:0x038000e1的dump結果顯示該塊中包含了前面直接尋址時的chunk地址,
Object id on Block? Y
seg/obj: 0x51a69a? csc: 0xb89.32fe6c01? itc: 1
flg: E? typ: 5 - LOCAL LOBS
fsl: 0
fnx: 0xffffffff ver: 0x01
Itl
Xid????????????????? Uba???????? Flag
Lck??????? Scn/Fsc
0x01?? 0x03fb.012.001414b3? 0x030693ac.fae5.07? ----
1? fsc 0x0000.00000000
========
bdba??? [0x038000e1]
kdlich? [11085d24c 56]
flg0
0x18 [ver=0 typ=lhb lock=y]
flg1
0x00
scn
0x0b89.32fe6c01
lid
00000001000428cae551
rid
0x00000000.0000
kdlihh? [11085d264 24]
flg2
0x00 [ver=0 lid=short-rowid hash=n it=n bt=n xfm=n ovr=n aux=n]
flg3
0x80 [vll=y]
flg4
0x00
flg5
0x00
hash
0000000000000000000000000000000000000000
llen
0.253914
ver
0.8
#ext? 9
asiz? 9
hwm?? 9
ovr
0x00000000.0
dba0
0x00000000
dba1
0x00000000
dba2
0x00000000
dba3
0x00000000
auxp
0x00000000
ldba
0x038000c9
nblk
32
[0] 0x00 0x00 7 0x038000a9
[1] 0x00 0x00 3 0x038000bd
[2] 0x00 0x00 4 0x038000a5
[3] 0x00 0x00 6 0x038000ca
[4] 0x00 0x00 2 0x038000c1
[5] 0x00 0x00 5 0x038000c4
[6] 0x00 0x00 1 0x038000c3
[7] 0x00 0x00 3 0x038000dd
[8] 0x00 0x00 1 0x038000c9
但dba:0x038000e1這個起到地址索引效果的塊位于lob segment里,并不在lob index里,oracle何時會使用lob index有待進一步考證
結論3:當表里直接尋址的chunk Address地址數大于6個時,會轉換為間接尋址的模式
?SecureFiles LOBs的read consistency特性
當lob字段和表中其它非lob字段分開存放的時候,lob字段的讀一致性實現依賴于lob
segment本身,所有的修改前鏡像會保存在lob segment里,和undo表空間沒有任何關系,這樣設計也是為了避免lob字段過大時產生的before-image過大從而影響undo表空間中的其它非lob對象的讀一致性。Lob segment自己的讀一致性管理機制由LOB_retention_clause所定義的值決定,這個值在建表的時候指定,之后也可以使用alter table進行修改,retention有以下四種取值,
MAX:在lob segment達到最大值之后,才開始覆蓋before-image所占用的block,前提是在建lob
segment時必須指定maxsize;
MIN:數據庫工作在閃回模式下,限定特定的lob
segment能夠閃回到多久時間以前的狀態
AUTO:before-image的保留時間參照數據庫的undo_retention參數
NONE:不保存before-image,用于不需要讀一致性的環境
深入lob讀一致性之前,我們先要了解一下一個lob segment中的數據塊有哪些類型,MOS 1453350.1中提供了check_space_securefile存儲過程,里面封裝的是DBMS_SPACE.SPACE_USAGE,用來檢測lob segment中數據塊的使用情況。以owner和lobsegment名作為輸入,例如:exec check_space_securefile('SCOTT','TLOB_AUTO');輸出為:
Segment Blocks/Bytes?? = 64 / 524288
Unused
Blocks/Bytes??? = 34 / 278528
Used
Blocks/Bytes????? = 8 / 65536
Expired
Blocks/Bytes?? = 13 / 106496
Unexpired
Blocks/Bytes = 9 / 73728
==========================================================================
NON Data
Blocks/Bytes? = 56 / 458752
它將數據塊分為unused、used、expired、unexpired四種,
Unused block:這個命名有點誤導,指的是起到存放metadata的block,諸如:Segment Header、CFS Hash Bucket、Committed Free Space、Uncommit Free Space、Persistent Undo,這些類型的的block都是lob segment所特有的,既然能有自己的讀一致性機制這些控制信息必不可少。所以Unused block并非指空閑的數據塊。
Used block:已經包含有用戶數據的block
Expired block:空閑的數據庫塊(包括從未被使用的和曾經被使用過但按照現有的retention策略可以被覆蓋使用的)
Unexpired blocks:存放修改前鏡像,為滿足讀一致性需要暫時保留不能被覆蓋的數據塊
以上輸出中的NON Data Blocks是將segment_blocks-used_blocks而得到
我們分別體驗一下MAX、AUTO、NONE三個參數的作用
//
6、retention MAX ?//
###先測試一下在沒有達到maxsize的情況下,修改前鏡像會一直保留著
--建表,指定lob segment最大為1024k
drop table tlob44;
create table tlob44 (id number,t44col2 clob) lob(t44col2)
store as securefile tlob_44 (tablespace ts128k3 storage(maxsize
1024K) disable storage in row retention max);
--插入若干條記錄
declare
v_str varchar2(6000);
i number;
j number;
begin
i:=101;
j:=1;
while ( i < 123 ) loop
v_str:=lpad(chr(i),6000,chr(i));
insert into tlob44 values(j,v_str);
i:=i+1;
j:=j+1;
end loop;
commit;
end;
/
--觀察lob segment的空間使用情況
exec check_space_securefile('SCOTT','TLOB_44');
Segment Blocks/Bytes?? = 96 / 786432
Unused Blocks/Bytes??? = 36 / 294912
Used Blocks/Bytes????? = 45 / 368640
Expired Blocks/Bytes?? = 15 / 122880
Unexpired Blocks/Bytes = 0 / 0
===========================================================================
NON Data Blocks/Bytes
= 51 / 417792
--刪除其中id<10的9條記錄,有18個block變成了unexpired
delete tlob44 where id<10;
commit;
exec
check_space_securefile('SCOTT','TLOB_44');
Segment Blocks/Bytes?? = 96 / 786432
Unused Blocks/Bytes??? = 36 / 294912
Used Blocks/Bytes????? = 27 / 221184
Expired Blocks/Bytes?? = 15 / 122880
Unexpired Blocks/Bytes = 18 / 147456
===========================================================================
NON Data Blocks/Bytes
= 69 / 565248
--再插入9條記錄,由于沒有達到maxsize所以unexpired blocks不會被重用
declare
v_str varchar2(6000);
i number;
j number;
begin
i:=98;
j:=1;
while ( i < 107 ) loop
v_str:=lpad(chr(i),6000,chr(i));
insert into tlob44 values(j,v_str);
i:=i+1;
j:=j+1;
end loop;
commit;
end;
/
---unexpired blocks依然保持18個,新進來的數據使用新分配的空間
exec
check_space_securefile('SCOTT','TLOB_44');
Segment Blocks/Bytes?? = 112 / 917504
Unused Blocks/Bytes??? = 37 / 303104
Used Blocks/Bytes????? = 45 / 368640
Expired Blocks/Bytes?? = 12 / 98304
Unexpired Blocks/Bytes = 18 / 147456
===========================================================================
NON Data Blocks/Bytes? = 67 / 548864
###接著測試在達到maxsize的情況下,如果有新進數據會覆蓋修改前鏡像
drop table tlob33;
create table tlob33 (id number,t33col2 clob) lob(t33col2)
store as securefile tlob_33 (tablespace ts128k3 storage(maxsize
1024K) disable storage in row retention max);
--插入44行記錄,都是大小寫英文字母,每行6000個字符,占據2個blocks
declare
v_str
varchar2(6000);
j
number:=1;
i number;
begin
i:=101;
while ( i
< 123 ) loop
v_str:=lpad(chr(i),6000,chr(i));
insert
into tlob33 values(j,v_str);
i:=i+1;
j:=j+1;
end loop;
i:=65;
while ( i
< 87 ) loop
v_str:=lpad(chr(i),6000,chr(i));
insert
into tlob33 values(j,v_str);
i:=i+1;
j:=j+1;
end loop;
commit;
end;
/
--統計space usage,還剩1個空閑的block
exec
check_space_securefile('SCOTT','TLOB_33');
Segment
Blocks/Bytes?? = 128 / 1048576
Unused
Blocks/Bytes??? = 38 / 311296
Used
Blocks/Bytes????? = 89 / 729088
Expired Blocks/Bytes
= 1/ 8192
Unexpired Blocks/Bytes = 0 / 0
===========================================================================
NON Data Blocks/Bytes
= 39 / 319488
--只能再插入一個block,插入第二個block時就報錯了,因為已經達到1024k上限了,說明maxsize設置生效
declare
v_str45
varchar2(3000):=lpad('S',3000,'S');
begin
insert into
tlob33 values(45,v_str45);
commit;
end;
/
PL/SQL procedure
successfully completed.
declare
v_str46
varchar2(3000):=lpad('T',3000,'T');
begin
insert into
tlob33 values(46,v_str46);
commit;
end;
/
ERROR at line 1:
ORA-60010:
adding (144) blocks to LOB segment SCOTT.TLOB_33 with MAXSIZE (128)
ORA-06512: at
line 4
--expired block=0說明沒有空閑空間來容納新的記錄
SQL> exec
check_space_securefile('SCOTT','TLOB_33');
Segment
Blocks/Bytes?? = 128 / 1048576
Unused
Blocks/Bytes??? = 38 / 311296
Used
Blocks/Bytes????? = 90 / 737280
Expired
Blocks/Bytes?? = 0 / 0
Unexpired
Blocks/Bytes = 0 / 0
===========================================================================
NON Data Blocks/Bytes
= 38 / 311296
select
bytes,segment_name from dba_segments where segment_name='TLOB_33';
BYTES
SEGMENT_NAME
----------
--------------------
1048576 TLOB_33
-- session 2:為測試一致性讀另開的
set transaction read only;
--刪除剛剛插入的id=45的記錄,釋放出一個block
delete tlob33 where id=45;
commit;
--刪除的記錄放在Unexpired Blocks里
exec
check_space_securefile('SCOTT','TLOB_33');
Segment
Blocks/Bytes?? = 128 / 1048576
Unused
Blocks/Bytes??? = 38 / 311296
Used
Blocks/Bytes????? = 89 / 729088
Expired
Blocks/Bytes?? = 0 / 0
Unexpired Blocks/Bytes = 1 / 8192
--session 2能一致性讀到id=45的記錄,
select
id,dbms_lob.getlength(t33col2) from tlob33 where id=45;
ID DBMS_LOB.GETLENGTH(T33COL2)
----------
---------------------------
45??????????????????????? 3000
--往tlob33表中再次插入一條記錄,unexpired的block會被重用
declare
v_str47 varchar2(3000):=lpad('U',3000,'U');
begin
insert into
tlob33 values(47,v_str47);
commit;
end;
/
--unexpired blocks變為0,Used Blocks增加1
exec check_space_securefile('SCOTT','TLOB_33');
Segment Blocks/Bytes?? = 128 /
1048576
Unused Blocks/Bytes??? = 38 /
311296
Used Blocks/Bytes????? = 90 /
737280
Expired Blocks/Bytes?? = 0 / 0
Unexpired Blocks/Bytes = 0 / 0
===========================================================================
NON Data Blocks/Bytes? = 38 /
311296
--session 2,再次查詢tlob33收到ORA-1555錯誤:
select * from tlob33 where id=45;
ERROR:
ORA-01555: snapshot too old: rollback segment number? with name "" too small
ORA-22924: snapshot too old
select count(*) from tlob33 where id<45;
COUNT(*)
----------
44
--繼續刪除id<3的記錄
delete tlob33 where id<3;
2 rows deleted.
commit;
--空出來4個blocks
exec check_space_securefile('SCOTT','TLOB_33');
Segment Blocks/Bytes?? = 128 /
1048576
Unused Blocks/Bytes??? = 38 /
311296
Used Blocks/Bytes????? = 86 /
704512
Expired Blocks/Bytes?? = 0 / 0
Unexpired Blocks/Bytes = 4 / 32768
===========================================================================
NON Data Blocks/Bytes? = 42 /
344064
--session 2中能一致性讀到這兩條記錄
select count(*) from tlob33 where id<3;
COUNT(*)
----------
2
--往tlob33表中再次插入一條記錄,4個expired blocks里有兩個會被重用,根據先進先出的原則被重用的應該是先插入的id=1的記錄所在的block被覆蓋
declare
v_str48 varchar2(6000):=lpad('V',6000,'V');
begin
insert into tlob33 values(48,v_str48);
commit;
end;
/
--session 2,測試結果驗證了上面的結論:id=1的記錄不能讀取,id=2的記錄能讀到
SQL>select count(*) from tlob33 where id<3;
ERROR:
ORA-01555: snapshot too old: rollback segment number? with name "" too small
ORA-22924: snapshot too old
SQL> select * from tlob33 where id=1;
ERROR:
ORA-01555: snapshot too old: rollback segment number? with name "" too small
ORA-22924: snapshot too old
SQL> select * from tlob33 where id=2;
ID
----------
T33COL2
--------------------------------------------------------------------------------
2
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
結論4:retention=max時當lob segment大小增加到maxsize值時,會挪用unexpired
blocks作為可用空間存放新入數據,挪用unexpired blocks時遵循先進先出的原則,即優先覆蓋較早生成的block。在未觸及maxsize的情況下修改前鏡像會一直保留著。
//
7、retention AUTO ?//
--設置undo_retention=90,retention auto時會參照undo_retention設置時間保留修改前鏡像
alter system set undo_retention=90;
SQL> show parameter
undo_retention
NAME???????????????????????????????? TYPE??????? VALUE
------------------------------------
----------- ------------------------------
undo_retention?????????????????????? integer???? 90
--創建測試表
create table tlobauto (id
number,autocol2 clob) lob(autocol2) store as securefile tlob_auto
(tablespace ts128k1 storage(maxsize
1024K) disable storage in row retention auto);
--檢查空間使用情況
Set serveroutput on
exec
check_space_securefile('SCOTT','TLOB_AUTO');
Segment Blocks/Bytes?? = 16 / 131072
Unused Blocks/Bytes??? = 11 / 90112
Used Blocks/Bytes????? = 0 / 0
Expired Blocks/Bytes?? = 5 / 40960
Unexpired Blocks/Bytes = 0 / 0
===========================================================================
NON Data Blocks/Bytes? = 16 / 131072
--填充16行,每行50個字符
declare
v_str_length integer:=50;?? --指定隨機字串的單位長度
v_nrows integer:=16;????????????? --指定插入行數
v_conn_num integer:=1;?????? --指定隨機字串的單元數
i integer:=0;
j integer:=0;
v_str varchar2(32767);
begin
while (i < v_nrows) loop
v_str:=dbms_random.string('U',v_str_length);
while (j < v_conn_num-1) loop
v_str:=v_str||dbms_random.string('U',v_str_length);
j:=j+1;
end loop;
dbms_output.put_line(length(v_str));
insert into tlobauto
values(i,v_str);
commit;
i:=i+1;
j:=0;
end loop;
commit;
end;
/
--17個used block,13個expired
blocks
SQL> exec
check_space_securefile('SCOTT','TLOB_AUTO');
Segment Blocks/Bytes?? = 64 / 524288
Unused Blocks/Bytes??? = 34 / 278528
Used Blocks/Bytes????? = 17 / 139264
Expired Blocks/Bytes?? = 13 / 106496
Unexpired Blocks/Bytes = 0 / 0
===========================================================================
NON Data Blocks/Bytes? = 47 / 385024
--刪除id<5的5行記錄
Delete tlobauto where id<5;
Commit;
--5個unexpired blocks,存放了被刪除的5行記錄
SQL> exec
check_space_securefile('SCOTT','TLOB_AUTO');
Segment Blocks/Bytes?? = 64 / 524288
Unused Blocks/Bytes??? = 34 / 278528
Used Blocks/Bytes????? = 12 / 98304
Expired Blocks/Bytes?? = 13 / 106496
Unexpired Blocks/Bytes = 5 / 40960
===========================================================================
NON Data Blocks/Bytes? = 52 / 425984
--等待90秒后再檢驗,unexpired
blocks為0,5個blocks都加到了expired blocks上面
SQL> exec
check_space_securefile('SCOTT','TLOB_AUTO');
Segment Blocks/Bytes?? = 64 / 524288
Unused Blocks/Bytes??? = 34 / 278528
Used Blocks/Bytes????? = 12 / 98304
Expired Blocks/Bytes?? = 18 / 147456
Unexpired Blocks/Bytes = 0 / 0
===========================================================================
NON Data Blocks/Bytes? = 52 / 425984
--上面的結果看似驗證了retention
auto時before-image在lob segment里的保留時間跟隨undo_retention的設置,但這也不是絕對的,比如下面的場景
--重新建立表tlobauto
Drop table tlobauto;
create table tlobauto (id
number,autocol2 clob) lob(autocol2) store as securefile tlob_auto
(tablespace ts128k1 storage(maxsize
1024K) disable storage in row retention auto);
--初始空間使用情況
exec
check_space_securefile('SCOTT','TLOB_AUTO');
Segment Blocks/Bytes?? = 16 / 131072
Unused Blocks/Bytes??? = 11 / 90112
Used Blocks/Bytes????? = 0 / 0
Expired Blocks/Bytes?? = 5 / 40960
Unexpired Blocks/Bytes = 0 / 0
===========================================================================
NON Data Blocks/Bytes? = 16 / 131072
--修改undo_retention為7200
Alter system set undo_retention=7200
scope=memory;
SQL> show parameter
undo_retention
NAME???????????????????????????????? TYPE??????? VALUE
------------------------------------
----------- ------------------------------
undo_retention??????????????????? ???integer
7200
--第一輪填充tlobauto表,16行,每行50個字符
set serveroutput on
declare
v_str_length integer:=50;?? --指定隨機字串的單位長度
v_nrows integer:=16;????????????? --指定插入行數
v_conn_num integer:=1;?????? --指定隨機字串的單元數
i integer:=0;
j integer:=0;
v_str varchar2(32767);
begin
while (i < v_nrows) loop
v_str:=dbms_random.string('U',v_str_length);
while (j < v_conn_num-1) loop
v_str:=v_str||dbms_random.string('U',v_str_length);
j:=j+1;
end loop;
dbms_output.put_line(length(v_str));
insert into tlobauto values(i,v_str);
commit;
i:=i+1;
j:=0;
end loop;
commit;
end;
/
--check space usage
exec
check_space_securefile('SCOTT','TLOB_AUTO');
Segment Blocks/Bytes?? = 64 / 524288
Unused Blocks/Bytes??? = 34 / 278528
Used Blocks/Bytes????? = 17 / 139264
Expired Blocks/Bytes?? = 13 / 106496
Unexpired Blocks/Bytes = 0 / 0
===========================================================================
NON Data Blocks/Bytes? = 47 / 385024
--刪除9行記錄
delete tlobauto where id>6 and
id<16;
commit;
--check space usage
exec check_space_securefile('SCOTT','TLOB_AUTO');
Segment Blocks/Bytes?? = 64 / 524288
Unused Blocks/Bytes??? = 34 / 278528
Used Blocks/Bytes????? = 8 / 65536
Expired Blocks/Bytes?? = 13 / 106496
Unexpired Blocks/Bytes = 9 / 73728
===========================================================================
NON Data Blocks/Bytes? = 56 / 458752
--第二輪繼續填充tlobauto表,插入11行,每行50個字符
set serveroutput on
declare
v_str_length integer:=50;?? --指定隨機字串的單位長度
v_nrows integer:=18;????????????? --指定插入行數
v_conn_num integer:=1;?????? --指定隨機字串的單元數
i integer:=7;
j integer:=0;
v_str varchar2(32767);
begin
while (i < v_nrows) loop
v_str:=dbms_random.string('U',v_str_length);
while (j < v_conn_num-1) loop
v_str:=v_str||dbms_random.string('U',v_str_length);
j:=j+1;
end loop;
dbms_output.put_line(length(v_str));
insert into tlobauto
values(i,v_str);
commit;
i:=i+1;
j:=0;
end loop;
commit;
end;
/
--check space usage發現used blocks從8增加到了19,expired
blocks從13下減到了11
exec check_space_securefile('SCOTT','TLOB_AUTO');
Segment Blocks/Bytes?? = 64 / 524288
Unused Blocks/Bytes??? = 34 / 278528
Used
Blocks/Bytes????? = 19/ 155648
Expired
Blocks/Bytes?? = 2/ 16384
Unexpired Blocks/Bytes = 9 / 73728
===========================================================================
NON Data Blocks/Bytes? = 45 / 368640
--第三輪又插入三行記錄,每行50個字符
set serveroutput on
declare
v_str_length integer:=50;?? --指定隨機字串的單位長度
v_nrows integer:=21;????????????? --指定插入行數
v_conn_num integer:=1;?????? --指定隨機字串的單元數
i integer:=18;
j integer:=0;
v_str varchar2(32767);
begin
while (i < v_nrows) loop
v_str:=dbms_random.string('U',v_str_length);
while (j < v_conn_num-1) loop
v_str:=v_str||dbms_random.string('U',v_str_length);
j:=j+1;
end loop;
dbms_output.put_line(length(v_str));
insert into tlobauto
values(i,v_str);
commit;
i:=i+1;
j:=0;
end loop;
commit;
end;
/
--最終檢查空間使用情況發現雖然沒有達到undo_retention所指定的7200秒,lob segment也沒有達到其maxsize所定義的上限值1024k,但還是挪用了1個unexpired
blocks,unexpired blocks從9減少到了8,可見在retention auto的情況下并不是完全聽從undo_retention的指揮,為何會出現這種情況還下不了結論,但至少證明了retention auto并不像文檔中說的那么簡單,還是有自己的一套算法
exec
check_space_securefile('SCOTT','TLOB_AUTO');
Segment Blocks/Bytes?? = 80 / 655360
Unused Blocks/Bytes??? = 35 / 286720
Used Blocks/Bytes????? = 22 / 180224
Expired Blocks/Bytes?? = 15 / 122880
Unexpired
Blocks/Bytes = 8/ 65536
===========================================================================
NON Data Blocks/Bytes? = 58 / 475136
結論5:retention auto的情況下before-image的保留時間不完全遵循與undo_retention參數的設定值,可能會引起ORA-01555錯誤
//
8、retention none ?//
--retention none比較好理解就是永遠不保存修改前的鏡像,這種情況下無法實現read consistency
create table tlobnone (id number,nonecol2 clob) lob(nonecol2)
store as securefile tlob_none (tablespace ts128k1 storage(maxsize 1024K)
disable storage in row retention none);
insert into tlobnone values(1,'A');
commit;
--插入一行后空間使用情況
exec check_space_securefile('SCOTT','TLOB_NONE');
Segment Blocks/Bytes?? = 48 / 393216
Unused Blocks/Bytes??? = 33 / 270336
Used Blocks/Bytes????? = 2 / 16384
Expired Blocks/Bytes?? = 13 / 106496
Unexpired Blocks/Bytes = 0 / 0
===========================================================================
NON Data Blocks/Bytes? = 46 / 376832
--session 2:測試一致性讀所開的session
Set transaction read only
Select * from tlobnone where id=1;
ID
----------
NONECOL2
--------------------------------------------------------------------------------
1
A
--刪除這唯一的一行
Delete tlobnone where id=1;
Commit;
--unexpired blocks為0,被刪除的數據對應的block直接歸到了空閑的block里
SQL> exec check_space_securefile('SCOTT','TLOB_NONE');
Segment Blocks/Bytes?? = 48 / 393216
Unused Blocks/Bytes??? = 33 / 270336
Used Blocks/Bytes????? = 1 / 8192
Expired Blocks/Bytes?? = 14 / 114688
Unexpired Blocks/Bytes = 0 / 0
===========================================================================
NON Data Blocks/Bytes? = 47 / 385024
--session 2:還是能讀到修改前的鏡像,因為被刪除的行所對應的block只是被標記為了空閑塊的屬性,block里的內容尚未被清理或者覆蓋,所以session 2能夠實現read consistency
Set transaction read only
Select * from tlobnone where id=1;
ID
----------
NONECOL2
--------------------------------------------------------------------------------
1
A
--執行下列語句15次,用盡14個expired
blocks
insert into tlobnone values(1,'B');
--再回到session 2執行時報了ORA-01555,因為數據塊被真正覆蓋了
select * from tlobnone;
*
ERROR:
ORA-01555: snapshot too old: rollback
segment number? with name ""
too small
ORA-22924: snapshot too old
結論6:retention none的情況下無法實現read
consistency,隨時會導致ORA-01555
總結
以上是生活随笔為你收集整理的oracle t44,SecureFiles LOBs基础知识之存储篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何注册、发布 CSDN博客
- 下一篇: 地理探测器原理介绍