Oracle中的段(r10笔记第81天)

时间:2022-05-05
本文章向大家介绍Oracle中的段(r10笔记第81天),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

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  

所以对于段来说,里面的信息对应的都是逻辑的概念,地址是逻辑地址,是相对的,而非绝对的。切莫认为一个区可以同时属于多个段。