Oracle的体系结构中,关于存储结构大家应该都很熟悉了。
估计下面这张图大家都看得熟悉的不能再熟悉了。
简单来说,里面的一个重要概念就是段,如果是开发同学,可能每次听到这里都会有些模糊,好像懂,好像不懂。n年前给开发同学内部培训的时候,有开发同事就百思不得其解,我当时按照书上的原话也没有收服它们,同样的话出自DBA的口中似乎多了一些信服的味道。多年之后,还有同学问起我这个问题的时候,让我想起了曾经苦苦学习Oracle的那些时光,我简单说说我的理解。
首先老三篇的内容我们换一种玩法,如果是一个11gR2的默认数据库环境,下面的这个语句会创建几个段。
create table test_seg (id number primary key,name varchar2(30),memo clob);
这个问题问住了不少的人,而似乎我也听到了不少意料之中的答案,如果认为是1个的,就是理解创建了一个表,那就是一个段了,这个段类型是TABLE,如果认为是2个的,那就是看到了里面有一个主键,那就是对应一个索引段,如果认为是3个的,那就是看到了LOB字段,不过还差一步,LOB其实是会创建两个段,一个是LOB段,一个是LOB索引段,结构化的数据是Oracle的强项,而如果加入半结构化的数据处理,就得额外花些功夫。而如果我们在11gR2的环境中,我们耳濡目染的而一个特性,延迟段创建,存在一个数据库参数segment_deferred_creation默认为true,即开启了延迟段创建,所以上面的语句不会创建任何的段。
SQL> select segment_name,segment_type from user_segments;
no rows selected
而我们插入一部分的数据,我们就插入近50万数据吧。insert into test_seg select level,'obj'||level,'' from dual connect by level<500000;
这样一来,问题就回归了,是4个段,其中SYS_IL开头的段是索引段,剩下SYS_开头的两个是LOB相关的段。
select segment_name,segment_type from user_segments;
SEGMENT_NAME SEGMENT_TYPE
------------------------------ ------------------------------------
TEST_SEG TABLE
SYS_IL0000089494C00003$$ LOBINDEX
SYS_C0011978 INDEX
SYS_LOB0000089494C00003$$ LOBSEGMENT
怎么去理解这个段呢。
我们还是求助于user_segments来看看。
SQL> select segment_name,extents,blocks from user_segments
SEGMENT_NAME EXTENTS BLOCKS
------------------------------ ---------- ----------
TEST_SEG 26 1408
SYS_IL0000089494C00003$$ 1 8
SYS_C0011978 23 1024
SYS_LOB0000089494C00003$$ 1 8
可以看到段TEST_SEG存在26个区块,含有1408个数据块。这些都是概览的信息。
如果想得到更细致一些的信息呢,那就是user_extents了。我们简化一下,只是查看segment_type为TABLE的区块信息。
SQL> select segment_name,segment_type,extent_id,bytes,blocks from user_extents
where segment_name='TEST_SEG' order by segment_name,extent_id ;
TEST_SEG TABLE 0 65536 8
TEST_SEG TABLE 1 65536 8
TEST_SEG TABLE 2 65536 8
TEST_SEG TABLE 3 65536 8
TEST_SEG TABLE 4 65536 8
T
这里我们可以看到段级别对应的区是0-25,和user_segments中看到的26个区数据是一致的。
而这些区块的ID其实就是一个逻辑的编号,是基于段级别的划分。
同样上面的语句,我们简单改一下,查看segment_type为INDEX的区块信息。如下:
SEGMENT_NAME SEGMENT_TYPE EXTENT_ID BYTES BLOCKS
-------------------- -------------------- ---------- ---------- ----------
SYS_C0011978 INDEX 0 65536 8
SYS_C0011978 INDEX 1 65536 8
SYS_C0011978 INDEX 2 65536 8
SYS_C0011978 INDEX 3 65536 8
SYS_C0011978 INDEX 4 65536 8
SYS_C0011978 INDEX 5 65536 8
SYS_C0011978 INDEX 6 65536 8
SYS_C0011978 INDEX 7 65536 8
可以很明显看到区块的ID也是从0开始,比如0-7
这里就需要明白,这里的区块0是相对于这个segment_type为INDEX的segment为SYS_C0011978而言的。所以这里的值是一个相对的值,而不是绝对的。
而绝对的位置怎么查找呢,这得依赖于物理结构,能够定位的一个利器就是ROWID,我们取出1行数据来看看。
SQL> select dbms_rowid.ROWID_RELATIVE_FNO(rowid) as file#,dbms_rowid.ROWID_BLOCK_NUMBER(rowid) as block#,dbms_rowid.ROWID_ROW_NUMBER(rowid) as row#,a.* from test.test_seg a where rownum<2;
FILE# BLOCK# ROW# ID NAME MEMO
---------- ---------- ---------- ---------- -------------------- ----------
4 134803 0 468 obj468
可以看到是4号数据文件,对应的数据块是134803
要知道一个数据块里存放的数据基本上不是1行数据,而是相关的多行数据。
我们就可以做一个dump来看看。
alter system dump datafile 4 block 134803; 然后查看 select *from v$diag_info; 来得到trace文件的路径。
可以通过trace文件看到下面的内容:
tab 0, row 0, @0x1f8a
tl: 14 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 3] c2 05 45
col 1: [ 6] 6f 62 6a 34 36 38
tab 0, row 1, @0x1f7c
tl: 14 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 3] c2 05 46
col 1: [ 6] 6f 62 6a 34 36 39
目前存在3个字段,但是因为当前的LOB字段为空,就直接延迟创建了,只显示出来了两个字段。
而继续做一个update操作,提交事务。
SQL> update test_seg set memo='aaa' where rownum=1;
commit;
可以通过trace看到里面的内容已经大大不同,一个最大的变化就是LOB段的延迟创建情况,这个时候修改LOB段数据,所以就会重新初始化。
tab 0, row 0, @0x690
tl: 57 fb: --H-FL-- lb: 0x2 cc: 3
col 0: [ 3] c2 05 45
col 1: [ 6] 6f 62 6a 34 36 38
col 2: [42]
00 54 00 01 02 0c 80 00 00 02 00 00 00 01 00 00 00 12 ef 45 00 16 09 00 00
00 00 00 00 06 00 00 00 00 00 01 00 61 00 61 00 61
LOB
Locator:
Length: 84(42)
Version: 1
Byte Length: 2
LobID: 00.00.00.01.00.00.00.12.ef.45
Flags[ 0x02 0x0c 0x80 0x00 ]:
Type: CLOB
Storage: BasicFile
Enable Storage in Row
Characterset Format: IMPLICIT
Partitioned Table: No
Options: VaringWidthReadWrite
Inode:
Size: 22
Flag: 0x09 [ Valid DataInRow ]
Future: 0x00 (should be '0x00')
Blocks: 0
Bytes: 6
Version: 00000.0000000001
Inline data[6]
Dump of memory from 0x00007FBE38B88127 to 0x00007FBE38B8812D
7FBE38B88120 00010000 00610061 02002C61 [....a.a.a,..]
tab 0, row 1, @0x1f7c
tl: 14 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 3] c2 05 46
col 1: [ 6] 6f 62 6a 34 36 39
所以对于段来说,里面的信息对应的都是逻辑的概念,地址是逻辑地址,是相对的,而非绝对的。切莫认为一个区可以同时属于多个段。