1. SecureFile LOB的特点
最近张乐亦(Kamus)在gtalk上问了俺一个问题,为啥SecureFile类型的LOB要比以前的LOB性能要好很多,是不是存储结构上发生了什么变化。这两天抽空看了些资料,现在整理出来算是回答他的问题了。
关于
11g以前的LOB类型的实现方式可以参考前一段写的一篇博文《CLOB的物理存储结构及语言编码详解》。从11g开始,
Oracle提供了一种新的LOB存储方式叫SecureFile,以前旧有的LOB存储方式就叫BasicFile了。Oracle宣称,“SecureFile不仅是新一代 LOB,它们还为 LOB 带来了更多的价值,尤其是以前只能在文件系统领域中获得的特性。SecureFile可以进行加密以确保安全性,可以进行重复消除和压缩以提高存储效率,可以进行缓存(或不进行缓存)以加快访问(或节省缓冲池空间),可以按多个级别记录以减少崩溃后的平均恢复时间。引入 SecureFile 后,您可以在数据库中存储更多的非结构化文档,而不会导致过多的开销,也不会失去 OS 文件系统提供的任何重要功能。”
简单来说就三条:
一、提供了压缩、重复消除、加密等新功能
二、比以前的LOB的性能提高很多
三、易用性(无需设置CHUNK、PCTVERSION、FREELISTS、FREELIST GROUPS、FREEPOOLS参数)
注意:压缩需要Oracle Advanced Compression Option,加密需要Oracle Advanced Security Option,这两个option都是单独购买的,没有包括在Enterprise Edition里面。
2. 使用SecureFile
想要使用SecureFile LOB很简单,只需指定STORE AS SECUREFILE子句就行了(测试环境为11gR2):
CREATE TABLE tst.t11 (id number,c1 CLOB) LOB (c1) STORE AS SECUREFILE;
Securefile列标明了是否为SecureFile类型的LOB:
SELECT table_name,segment_name,index_name,securefile FROM dba_lobs WHERE table_name='T11';
TABLE_NAME SEGMENT_NAME INDEX_NAME SECUREFIL
---------- ------------------------------ ------------------------------ ---------
T11 SYS_LOB0000069030C00001$$ SYS_IL0000069030C00001$$ YES
使用Securefile LOB的表也是自动生成LOB segment和LOB index的。但是此时LOB index只有在使用重复消除功能时才会使用,在其他情况下均不会使用。要注意,Securefile LOB只能在ASSM的表空间(自动管理的表空间)里创建,不过既然从9i起ASSM表空间就是默认设置了,一般这里不会有多大问题。还要多说一句,只是要求SecureLOB所在的LOB列数据需要存放在ASSM表空间中,而包含LOB列的那个表,你还是可以放在手动管理的表空间中。
想使用SecureFile LOB,对数据库的参数DB_SECUREFILE设置也有一定的要求:
PERMITTED:数据库的默认参数。指定SecureFile时创建SecureFile类型的LOB;未指定时,或显式指定BasicFile时,创建BasicFile类型的LOB。
FORCE:无论是否指定SecureFile,强制创建SecureFile类型的LOB。在手动管理的表空间上创建LOB时,无论STORAGE子句是否指定SecureFile,均报ORA-43853错。
ALWAYS:无论是否指定SecureFile,强制创建SecureFile类型的LOB。在手动管理的表空间上创建LOB时,若 STORAGE子句未显式指定LOB类型,创建为BasicFile类型的LOB;若STORAGE子句显式指定SecureFile类型,则也报 ORA-43853错。
NEVER:无论是否指定SecureFile,强制创建BasicFile类型的LOB。指定SecureFile类型特有的功能如压缩,加密,重复消除时,报ORA-43854错。
IGNORE:无论是否指定SecureFile,强制创建BasicFile类型的LOB。忽略SecureFile类型特有的功能,创建BasicFile类型的LOB。
3. BasicFile和SecureFile的架构比较
3.1. 可变Chunk
首先介绍一下SecureFile中的可变Chunk。大家都知道在BasicFile的LOB中,Chunk的大小是一定的,最小跟DB Block的大小一样,最大为32KB,这存在一些问题。比如chunk比LOB的数据小很多的情况下,访问LOB就会产生很多IO,而chunk比 LOB的数据大很多的情况下,又会产生对存储空间的浪费。而在SecureFile中,chunk的size是可变的,由Oracle自动动态分配,最小跟DB Block的大小一样,最大为64MB。这样在存储较小的LOB时,使用比较小的chunk;在存储比较大的LOB时,会使用比较大的chunk。注意不是说一个LOB就放在一个chunk里,而是
oracle根据LOB data的数据大小会自动决定chunk数和chunk的size,具体可以看下面“SecureFile的物理存储结构”一节的实验结果。
3.2. LOB index
在LOB数据的存储方式上,两种LOB也有很大的区别。关于BasicFile的存储方式,在《CLOB的物理存储结构及语言编码详解》一文中有详细的介绍,大概就是表中的LOB字段只存储LOB locator,指向LOB index,LOB index再指向LOB segment里实际的LOB数据。不难看出,这里增加了一个LOB index的结构,那么不可避免的,LOB index就有可能产生竞争,成为瓶颈。在SecureFile中,LOB index只有在使用重复消除功能时才会使用(关于这个结论的验证方法,在CLOB那篇文章中有记载,这里不再赘述了)。简而言之,SecureFile中只要不使用重复消除功能就没LOB index什么事,自然性能就上去了。
BasicFile Col1 LOB col —-> LOB index —-> LOB data
SecureFile Col1 LOB col —————-> LOB data
3.3. 空闲空间搜索
在BasicFile里,关于有空间的使用情况的信息是保存在LOB index和LOB segment里的。在INSERT或UPDATE操作LOB segment时,以下面的顺序来搜索空闲空间:
1. 在LOG segment的管理区搜索空闲空间,如果没有,转下一步
2. 访问LOB index,把可以释放的空间(如已经commit的transaction使用的UNDO)释放掉,并更新索引entry。如果不存在这种可以释放的空间,转下一步
3. 将HWM升高,扩大LOB segment,使用新分配的空间
由此可见,BasicFile的LOB在搜索空闲空间时,可能会去扫描LOB index。因此LOB index的竞争,或者在LOB数据很多的情况下,搜索LOB index的空闲空间这个操作本身都会造成时间上的花费。
对于空闲空间的管理,SecureFile将其放入了shared pool,这比BasicFile空闲空篇博文《CLOB的物理存储结构及语言编码详解》。从11g开始,Oracle提供了一种新的LOB存储方式叫SecureFile,以前旧有的LOB存储方式就叫BasicFile了。Oracle宣称,“SecureFile不仅是新一代 LOB,它们还为 LOB 带来了更多的价值,尤其是以前只能在文件系统领域中获得的特性。SecureFile可以进行加密以确保安全性,可以进行重复消除和压缩以提高存储效率,可以进行缓存(或不进行缓存)以加快访问(或节省缓冲池空间),可以按多个级别记录以减少崩溃后的平均恢复时间。引入 SecureFile 后,您可以在数据库中存储更多的非结构化文档,而不会导致过多的开销,也不会失去 OS 文件系统提供的任何重要功能。”
简单来说就三条:
一、提供了压缩、重复消除、加密等新功能
二、比以前的LOB的性能提高很多
三、易用性(无需设置CHUNK、PCTVERSION、FREELISTS、FREELIST GROUPS、FREEPOOLS参数)
注意:压缩需要Oracle Advanced Compression Option,加密需要Oracle Advanced Security Option,这两个option都是单独购买的,没有包括在Enterprise Edition里面。
2. 使用SecureFile
想要使用SecureFile LOB很简单,只需指定STORE AS SECUREFILE子句就行了(测试环境为11gR2):
CREATE TABLE tst.t11 (id number,c1 CLOB) LOB (c1) STORE AS SECUREFILE;
Securefile列标明了是否为SecureFile类型的LOB:
SELECT table_name,segment_name,index_name,securefile FROM dba_lobs WHERE table_name='T11';
TABLE_NAME SEGMENT_NAME INDEX_NAME SECUREFIL
---------- ------------------------------ ------------------------------ ---------
T11 SYS_LOB0000069030C00001$$ SYS_IL0000069030C00001$$ YES
使用Securefile LOB的表也是自动生成LOB segment和LOB index的。但是此时LOB index只有在使用重复消除功能时才会使用,在其他情况下均不会使用。要注意,Securefile LOB只能在ASSM的表空间(自动管理的表空间)里创建,不过既然从9i起ASSM表空间就是默认设置了,一般这里不会有多大问题。还要多说一句,只是要求SecureLOB所在的LOB列数据需要存放在ASSM表空间中,而包含LOB列的那个表,你还是可以放在手动管理的表空间中。
想使用SecureFile LOB,对数据库的参数DB_SECUREFILE设置也有一定的要求:
PERMITTED:数据库的默认参数。指定SecureFile时创建SecureFile类型的LOB;未指定时,或显式指定BasicFile时,创建BasicFile类型的LOB。
FORCE:无论是否指定SecureFile,强制创建SecureFile类型的LOB。在手动管理的表空间上创建LOB时,无论STORAGE子句是否指定SecureFile,均报ORA-43853错。
ALWAYS:无论是否指定SecureFile,强制创建SecureFile类型的LOB。在手动管理的表空间上创建LOB时,若 STORAGE子句未显式指定LOB类型,创建为BasicFile类型的LOB;若STORAGE子句显式指定SecureFile类型,则也报 ORA-43853错。
NEVER:无论是否指定SecureFile,强制创建BasicFile类型的LOB。指定SecureFile类型特有的功能如压缩,加密,重复消除时,报ORA-43854错。
IGNORE:无论是否指定SecureFile,强制创建BasicFile类型的LOB。忽略SecureFile类型特有的功能,创建BasicFile类型的LOB。
3. BasicFile和SecureFile的架构比较
3.1. 可变Chunk
首先介绍一下SecureFile中的可变Chunk。大家都知道在BasicFile的LOB中,Chunk的大小是一定的,最小跟DB Block的大小一样,最大为32KB,这存在一些问题。比如chunk比LOB的数据小很多的情况下,访问LOB就会产生很多IO,而chunk比 LOB的数据大很多的情况下,又会产生对存储空间的浪费。而在SecureFile中,chunk的size是可变的,由Oracle自动动态分配,最小跟DB Block的大小一样,最大为64MB。这样在存储较小的LOB时,使用比较小的chunk;在存储比较大的LOB时,会使用比较大的chunk。注意不是说一个LOB就放在一个chunk里,而是oracle根据LOB data的数据大小会自动决定chunk数和chunk的size,具体可以看下面“SecureFile的物理存储结构”一节的实验结果。
3.2. LOB index
在LOB数据的存储方式上,两种LOB也有很大的区别。关于BasicFile的存储方式,在《CLOB的物理存储结构及语言编码详解》一文中有详细的介绍,大概就是表中的LOB字段只存储LOB locator,指向LOB index,LOB index再指向LOB segment里实际的LOB数据。不难看出,这里增加了一个LOB index的结构,那么不可避免的,LOB index就有可能产生竞争,成为瓶颈。在SecureFile中,LOB index只有在使用重复消除功能时才会使用(关于这个结论的验证方法,在CLOB那篇文章中有记载,这里不再赘述了)。简而言之,SecureFile中只要不使用重复消除功能就没LOB index什么事,自然性能就上去了。
BasicFile Col1 LOB col —-> LOB index —-> LOB data
SecureFile Col1 LOB col —————-> LOB data
3.3. 空闲空间搜索
在BasicFile里,关于有空间的使用情况的信息是保存在LOB index和LOB segment里的。在INSERT或UPDATE操作LOB segment时,以下面的顺序来搜索空闲空间:
1. 在LOG segment的管理区搜索空闲空间,如果没有,转下一步
2. 访问LOB index,把可以释放的空间(如已经commit的transaction使用的UNDO)释放掉,并更新索引entry。如果不存在这种可以释放的空间,转下一步
3. 将HWM升高,扩大LOB segment,使用新分配的空间
由此可见,BasicFile的LOB在搜索空闲空间时,可能会去扫描LOB index。因此LOB index的竞争,或者在LOB数据很多的情况下,搜索LOB index的空闲空间这个操作本身都会造成时间上的花费。
对于空闲空间的管理,SecureFile将其放入了shared pool,这比BasicFile空闲空间管理的效率有了质的提高。 Shared Pool里的这个内存结构叫In-memory dispenser,它把空闲空间的信息cache在内存里,因此速度要比访问LOB index快了N个数量级。In-memory dispenser负责接受前台进程对于LOB空间的请求,并进行chunk的分配。
在In-memory dispenser中管理的空闲空间不是全部,而只是一部分而已,它的信息由后台进程SMCO/Wnnn来定期的更新。SMCO/Wnnn监视 SecureFile LOB segment的使用情况,根据需要保证空闲空间的供应。注意SMCO/Wnnn也负责普通的ASSM表空间的空间动态分配。
1. SMCO进程(Space Management Coordinator)。负责前瞻式(Proactive)的空间分配,它动态产生slave进程Wnnn来完成实际的工作。
2. Wnnn(SMCO Worker)每10分钟扫描一遍LOB segment的状态,根据需要把空chunk移动到In-memory dispenser中。如果这样空chunk还是不够,则扩大LOB segment。
此时在INSERT或UPDATE操作LOB segment时,以下面的顺序来搜索空闲空间:
1. 前台进程向In-memory dispenser发出需要chunk的请求
2. In-memory dispenser里的chunk信息里如果空chunk数量不足,或者空chunk的size不够时,在LOG segment的管理区搜索空闲空间,将空chunk的信息cache在In-memory dispenser里。如果搜索不到空闲空间,转下一步
3. 将HWM升高,扩大LOB segment,使用新分配的空间
3.4. STORAGE参数
跟BasicFile一样,SecureFile同样也有enable storage in row和disable storage in row的区别,在SecureFile的LOB里默认设置同样也是enable storage in row。LOB控制结构size加上LOB数据size一共未满4000字节时,enable storage in row的情况下就存储在源表的LOB列内,超出时就存放在LOB segment里;而disable storage in row的情况下则无论是否超过4000字节,LOB数据均存放在LOB segment里。
《CLOB的物理存储结构及语言编码详解》一文中提到过,enable storage in row的情况下源表的LOB列最多能存放3964字节;而在DB11gR1的SecureFile LOB中,变成了3740字节;DB11gR2时又变成了3952字节。均为引用的数据,具体区别尚未弄清。个人认为Size的变化都是因为LOB的控制信息发生了细微的变化。注意这里的size都是在未使用重复消除、加密、压缩选项的情况下得出的。
4. SecureFile的物理存储结构
下面就是本文的重头戏了,SecureFile的LOB到底在物理上是怎么存储的。在Table的Segment里的LOB列中,存放着两个 layer:Locator Layer(20字节)和Inode Layer。Locator的内容和BasicFile里是一样的,也包括了控制信息和10字节的LOB ID。而Inode Layer包含了RCI Header和Inode部分,其中Inode部分包含了指向LOB segment的指针,或者在In-line存储的情况下包含实际的LOB 数据。RCI(LOB Row-Column Intersection)即表Segment里存储的LOB列的所有数据,RCI Header里存储的是SecureFile的LOB控制信息。具体的定义如下:
2字节的Inode Layer整个的size,即表Segment里LOB列中,Locator Layer之外的size。如果是in-line存储的话也包括LOB数据的size在内。
1字节的flag,具体含义如下
0×01 此LOB是有效LOB
0×02 此inode在Index中
0×04此inode是in-line的
0×08 in-line的数据是实际的LOB数据
0×10 此inode是临时lob的控制结构
0×40此LOB是SecureFile的LOB
比如我们dump出block这里是0×48,就说明这个LOB是SecureFile的LOB,而且是in-line存储的。
1字节的SecureFile的LOB的选项flag:0×1是启用重复消除,0×2是启用压缩,0×4是启用加密。下面举例说明:
上文里创建过T11表,T11表没指定storage in row选项,因此就是默认的enable storage in row。给T11表插入两条测试数据,一条是in-line方式存储的,一条是out-of-line方式存储的:
SELECT id,dbms_lob.getlength(c1) FROM tst.t11;
ID DBMS_LOB.GETLENGTH(C1)
---------- ----------------------
1 56
2 25583
ALTER databas FLUSH buffer_cache;
SELECT dbms_rowid.rowid_to_absolute_fno(rowid,'TST','T11') fno,
dbms_rowid.rowid_block_number(rowid) bno,id FROM tst.t11;
FNO BNO ID
---------- ---------- ----------
5 132 1
5 132 2
ALTER system dump datafile 5 block 132;
SELECT value FROM v$diag_info WHERE name='Default Trace File';
VALUE
--------------------------------------------------------------------------------
/u01/app/oracle/diag/rdbms/dbeuc/dbeuc/trace/dbeuc_ora_25611.trc
先看第一条数据:
tab 0, row 0, @0x1f03
tl: 149 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [142]
00 54 00 01 02 0c 80 80 00 02 00 00 00 01 00 00 00 61 3c c7 00 7a 48 90 00
<- - - - - - A - - - - - - -> <- - - - - - B - - - - - - -> <- - C - -> <- 74 00 00 70 01 30 d3 30 b8 30 cd 30 b9 90 4b 55 b6 30 4c … … … - - - D - - -> < - - - - - - - - - - E - - - - - - - - - … … …
LOB
Locator:
Length: 84(142)
Version: 1
Byte Length: 2
LobID: 00.00.00.01.00.00.00.61.3c.c7
Flags[ 0x02 0x0c 0x80 0x80 ]:
Type: CLOB
Storage: SecureFile
Characterset Format: IMPLICIT
Partitioned Table: No
Options: VaringWidthReadWrite
SecureFile Header:
Length: 122
Old Flag: 0x48 [ DataInRow SecureFile ]
Flag 0: 0x90 [ INODE Valid ]
Layers:
Lengths Array: INODE:116
INODE:
00 00 70 01 30 d3 30 b8 30 cd 30 b9 90 4b 55 b6 30 4c 30 88
… … …
上面dump出的十六进制信息含义如下:
A:00 54 00 01 02 0c 80 80 00 02 Lob Locator Header
我认为这里的定义应该跟BasicFile没有发生变化:2字节的LOB locator长度 (除这两个长度字节外)+ 2字节的LOB locator structure版本 + 4字节的FLAG + 2字节的字符集里字符的长度
B:00 00 00 01 00 00 00 61 3c c7 LOB ID
C:00 7a 48 90 RCI Header
0x007a(=122字节):这个filed后的Inode的size(即D部分 + E部分的size)
0×48:这是in-line的SECUREFILE LOB
0×90:未启用重复消除,压缩,加密
D:00 74 00 00 70 01 Inode管理信息
0×0074(=116字节):这个field后面(即后面的00 00 70 01四个字节 + E部分)Inode数据的size。116减4字节为112字节,这跟上面得到的LOB的length为56是能匹配上的。
0×0000:in-line存储LOB data。第二位的0表示后面LOB Data的size是用1字节表示
0×70(=112字节):LOB Data的size
0×01:LOB Data的version
E:这里开始是真正的LOB Data
第二条:
tab 0, row 1, @0x1893
tl: 51 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [44]
00 54 00 01 02 0c 80 80 00 02 00 00 00 01 00 00 00 61 3d 2e 00 18 40 90 00
<- - - - - - A - - - - - - -> <- - - - - - B - - - - - - -> <- - C - -> <- 12 21 00 c7 de 0a 01 01 01 40 33 a2 01 01 01 40 33 14 06 - - - - D - - - - -> <- - - - - - - - E - - - - - - - ->
LOB
Locator:
Length: 84(44)
Version: 1
Byte Length: 2
LobID: 00.00.00.01.00.00.00.61.3d.2e
Flags[ 0x02 0x0c 0x80 0x80 ]:
Type: CLOB
Storage: SecureFile
Characterset Format: IMPLICIT
Partitioned Table: No
Options: VaringWidthReadWrite
SecureFile Header:
Length: 24
Old Flag: 0x40 [ SecureFile ]
Flag 0: 0x90 [ INODE Valid ]
Layers:
Lengths Array: INODE:18
INODE:
21 00 c7 de 0a 01 01 01 40 33 a2 01 01 01 40 33 14 06
上面dump出的十六进制信息含义如下:
A:Lob Locator Header
B:LOB ID
C:00 18 40 90 RCI Header
0×0018(=24字节):这个filed后的Inode的size(即D部分 + E部分的size)
0×40:这是out-line的SECUREFILE LOB
0×90:未启用重复消除,压缩,加密
D:00 12 21 00 c7 de 0a 01 Inode管理信息
0×0012(=18字节):这个field后面(即后面的六个字节 + E部分)Inode数据的size。
0×2100:第一位的2表示后面的E部分是chunk的RDBA + size,第二位的1表示后面LOB Data的size是用2字节表示
0xc7de(=51166字节):LOB Data的size
0x0a: LOB Data的version
0×01:代表后面有2个chunk,如果是2就是3个chunk,以此类推。
E:01 01 40 33 a2 01 01 01 40 33 14 06
01 01 40 33 a2 01:RDBA以0x014033a2开头的1个block
01 01 40 33 14 06:RDBA以0×01403314开头的6个block
我们看一下第一个chunk:
SELECT DBMS_UTILITY.data_block_address_block(TO_NUMBER('14033a2','xxxxxxxx'))
bno FROM dual;
BNO
----------
13218
得到:
bdba [0x014033a2]
kdlich [0x2b4878d45a4c 56]
flg0 0x28 [ver=0 typ=data lock=y]
flg1 0x00
scn 0x0000.0045ff19
lid 00000001000000613d2e - ->这是LOB ID
rid 0x00000000.0000
kdlidh [0x2b4878d45a64 24]
flg2 0x00 [ver=0 lid=short-rowid hash=n cmap=n pfill=n]
flg3 0x00
pskip 0
sskip 0
hash 0000000000000000000000000000000000000000
hwm 8060 - ->这个block里存放了8060字节的数据
spr 0
data [0x2b4878d45a80 52 8060]
30 d3 30 b8 30 cd 30 b9 90 4b 55 b6 30 4c 30 88 30 8a 89 07 96 d1 30 55 30 92
我们再看一下最后一个block:
SELECT DBMS_UTILITY.data_block_address_block(TO_NUMBER('1403314','xxxxxxxx'))
bno FROM dual;
BNO
----------
13076
现在需要dump从13076开始的第6个即13081号block,得到:
… … …
hwm 2806
… … …
一共8060×6+2806=51166字节的LOB data,这与前面的结果是能相互印证的。
下面我们再来看disable storage in row的情况下SecureFile是如何存储的。
在这种情况下指向LOB data的指针可能有两种存储方式:
第一种:LOB data的size不算很大的情况下,在Table Segment里的LOB列中以chunk的初始RDBA + size的方式存储,一个chunk信息接着一个chunk信息;
第二种:LOB data的size很大的情况下,在Table Segment里的LOB列中存储LHB(Lob Header Block)的信息,在LHB中存放所有chunk及size的列表。
第一种跟上面的第二条数据存储方式差不多,就不再介绍了,下面我们看第二种情况:
CREATE TABLE tst.t12 (id number,c2 CLOB) LOB (c2) STORE AS SECUREFILE(disable storage IN row);
插入一条很大的LOB数据:
SELECT id,dbms_lob.getlength(c2) FROM tst.t12;
ID DBMS_LOB.GETLENGTH(C2)
---------- ----------------------
1 49498672
ALTER databas FLUSH buffer_cache;
SELECT dbms_rowid.rowid_to_absolute_fno(rowid,'TST','T12') fno,
dbms_rowid.rowid_block_number(rowid) bno,id FROM tst.t12;
FNO BNO ID
---------- ---------- ----------
5 173 1
ALTER system dump datafile 5 block 173;
现在来看一下Table Segment里存放LOB列的地方是什么信息:
tab 0, row 0, @0x1f1b
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 02 00 00 00 01 00 00 00 61 3e 58 00 11 40 90 00
< - - - - - - A - - - - - - > <- - - - - - B - - - - - - -> <- - C - -> <- 0b 43 00 05 e6 94 60 41 01 40 3b 81 - - - - D - - - - - - > <- - E - ->
LOB
Locator:
Length: 84(37)
Version: 1
Byte Length: 2
LobID: 00.00.00.01.00.00.00.61.3e.58
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:
43 00 05 e6 94 60 41 01 40 3b 81
上面dump出的十六进制信息含义如下:
A:Lob Locator Header。
B:LOB ID
C:00 11 40 90 RCI Header
0×0018(=17字节):这个filed后的Inode的size(即D部分 + E部分的size)
0×40:这是out-line的SECUREFILE LOB
0×90:未启用重复消除,压缩,加密
D:00 0b 43 00 05 e6 94 60 41 Inode管理信息
0x000b(=11字节):这个field后面(即后面的四个字节 + E部分)Inode数据的size。
0×4300:第一位的4表示后面的E部分是LHB的RDBA,第二位的3表示LOB data的size是4字节。
0x05e69460(=98997344字节):LOB Data的size
0×41: LOB Data的version
E:01 40 3b 81 这是LHB的RDBA
我们把LHB给dump出来看看:
SELECT DBMS_UTILITY.data_block_address_block(TO_NUMBER(LTRIM('01403b81'),'xxxxxxxx'))
BNO FROM dual;
BNO
----------
15233
ALTER system dump datafile 5 block 15233;
可以看到下面有chunk列表,以 block数 + RDBA的形式存储:
bdba [0x01403b81]
kdlich [0x2b3f1e77844c 56]
flg0 0x18 [ver=0 typ=lhb lock=y]
flg1 0x00
scn 0x0000.00462283
lid 00000001000000613e58
rid 0x00000000.0000
kdlihh [0x2b3f1e778464 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.98997344
ver 0.65
#ext 100
asiz 100
hwm 100
ovr 0x00000000.0
dba0 0x00000000
dba1 0x00000000
dba2 0x00000000
dba3 0x00000000
auxp 0x00000000
ldba 0x01406ab5
nblk 12283 - ->这个LOB共占用了多少个block
[0] 0x00 0x00 73 0x014039b7 - ->从RDBA 0x014039b7 开始的73个block
[1] 0x00 0x00 7 0x014000c9 - ->从RDBA 0x014000c9开始的7个block
[2] 0x00 0x00 5 0x014000bb
… … … …
[97] 0x00 0x00 795 0x01406665
[98] 0x00 0x00 224 0x01406585
[99] 0x00 0x00 85 0x01406a61
接下来对于LOB data所在的block的dump这里就不做了,方法跟上述的类似。
5. 两种LOB性能测试比较
上面说了这么多新的SecureFile的LOB怎么怎么好,怎么怎么牛,大家一定有疑问了,是不是我在这忽悠大家呢。下面就给大家看些干货,真实的测试数据。
首先介绍下LOB参数:
LOGGING:在CREATE/UPDATE/INSERT LOB数据时会写入REDO LOG文件。但NOLOGGING会Internally转换成FILESYSTEM_LIKE_LOGGING,而FILESYSTEM_LIKE_LOGGING会确保数据库CRASH完整恢复
NOLOGGING:在CREATE/UPDATE/INSERT LOB数据时不写入REDO LOG文件。
FILESYSTEM_LIKE_LOGGING:数据库只记录LOB的METADATA到REDO LOG
NOCACHE:LOB数据不CACHE在SGA
CACHE:LOB数据CACHE在SGA
测试环境:
虚拟机OEL5.5 64
bit + DB11.2.0.1,测试数据是一个110MB的文本文档lob.txt:
[oracle@cdcjp11vm1 ~]$ du -m lob.txt
110 lob.txt
[oracle@cdcjp11vm1 ~]$ cat lob.txt|wc -l
1916928
[oracle@cdcjp11vm1 ~]$ head –n1 lob.txt
ビジネス運営がより複雑さを増すなかで、ITに対する変化の要求は高まりを見せ、関連するリスクの軽減もあわせて求めら
创建了一个存储过程insert_clob(代码在附录中),作用是插入若干条CLOB数据即lob.txt的内容,本次测试每次是插20条数据,共2.2GB,记录所花的时间。
存储方式 DML类型 SecureFile MB/s BasicFile MB/s 性能比%
CACHE + LOGGING INSERT 54.152 s 40.626 243.726 s 9.027 450.05%
CACHE + NOLOGGING INSERT 59.398 s 37.038 NOT support - -
NOCACHE + LOGGING INSERT 43.799 s 50.229 289.213 s 7.607 660.23%
NOCACHE + NOLOGGING INSERT 48.512 s 45.349 293.454 s 7.497 604.90%
结论已经很明显,新SecureFile格式的LOB性能相比较以前BasicFile有了巨大的提升,而且在最典型LOB的选项组合NOCACHE + LOGGING的情况下,性能提升的比例最大。
两种LOB的性能数据也可以参考这篇博文:
http://blog.sina.com.cn/s/blog_6058d7c10100nx26.html他的测试结果如下,有的测试结果跟我的结果相比有一定的出入,可能是环境的问题,也可能是数据的问题(我是110MB的文本文件,他是5MB的文本文件),也可能是程序的问题(他用的
java,我用的是PL/SQL)。另外他这篇文章里有关于SELECT(即READ)的性能数据,在NOCACHE + LOGGING的情况下,性能提升约三倍。
存储方式 DML类型 SecureFile MB/s BasicFile MB/s 性能比%
CACHE + LOGGING INSERT 9.17 8.64 106.13%
CACHE + LOGGING SELECT 39.52 4.42 894.12%
CACHE + NOLOGGING INSERT 31.56 - -
CACHE + NOLOGGING SELECT 35.31 - -
NOCACHE + LOGGING INSERT 36.63 2.32 1578.88%
NOCACHE + LOGGING SELECT 50.28 16.28 308.85%
NOCACHE + NOLOGGING INSERT 9.38 2.51 373.71%
NOCACHE + NOLOGGING SELECT 5.54 11.36 48.76%
1. 总结及附录
做个总结吧,我认为SecureFile的LOB之所以比BasicFile的LOB性能有提升,就是因为可变chunk、LOB index不再使用、空闲空间搜索放到了shared pool里这三大原因共同决定的,尤其是后两者,比起以前的BasicFile LOB,架构设计上有了飞跃。我们也能看出虽然Oracle数据库的发展不像以前那么革命性了,但是在很多方面,新版本的Oracle数据库还是取得了巨大的进步。
附录:
测试表(只写了一种,其他的选项组合类似):
create table tst.LOBTAB(ARTICLE_ID NUMBER PRIMARY KEY,ARTICLE_NAME VARCHAR2(50),
ARTICLE_DATA CLOB) tablespace data lob (ARTICLE_DATA)
store as SECUREFILE (tablespace DATA cache) LOGGING;
插入CLOB数据的存储过程insert_clob:
create or replace procedure tst.insert_clob (fromid in number,endid in number)
AS
i NUMBER;
V_LOB CLOB;
V_FILE BFILE := BFILENAME('HOME_DIR', 'lob.txt');
V_SOURCE NUMBER := 1;
V_DEST NUMBER := 1;
V_LANG NUMBER := 0;
V_WARN NUMBER;
BEGIN
for i in fromid..endid loop
V_SOURCE := 1;
V_DEST := 1;
INSERT INTO tst.LOBTAB VALUES (i, 'ABC'||to_char(i), 'TEST');
UPDATE tst.LOBTAB SET ARTICLE_DATA = EMPTY_CLOB where ARTICLE_ID=i RETURN ARTICLE_DATA INTO V_LOB;
DBMS_LOB.FILEOPEN(V_FILE);
DBMS_LOB.OPEN(V_LOB, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADCLOBFROMFILE(
V_LOB,
V_FILE,
DBMS_LOB.GETLENGTH(V_FILE),
V_DEST,
V_SOURCE,
0,
V_LANG,
V_WARN);
DBMS_LOB.CLOSE(V_LOB);
DBMS_LOB.FILECLOSEALL;
COMMIT;
end loop;
END;
/
计算CLOB的INSERT操作的时间差是使用以下的PL/SQL:
declare
a VARCHAR2(50);
b VARCHAR2(50);
begin
select to_char(systimestamp,'HH24:MI:SS.FF3') into a from dual;
TST.insert_clob(1,20);
select to_char(systimestamp,'HH24:MI:SS.FF3') into b from dual;
dbms_output.put_line(a);
dbms_output.put_line(b);
end;
/