ITPub博客

首页 > 数据库 > Oracle > [20141008]索引字符串的长度问题.txt

[20141008]索引字符串的长度问题.txt

原创 Oracle 作者:lfree 时间:2014-10-08 11:43:38 0 删除 编辑

[20141008]索引字符串的长度问题.txt

--oracle 的B tree 索引,一般保存方式是长度+键值+...+长度+rowid键值,如果索引唯一,rowid在前面(没有长度指示器),这样可以节省1个字节.
--但是如果索引的字符串长度长度比如超过255个字符,这样索引的长度部分如何保存呢?

--曾经写过一篇"varchar2(4000)如何保存",链接如下:

http://blog.itpub.net/267265/viewspace-747304/

        如果一行能被存储于一个数据块(data block)中,那么其行头(row header)所需容量将不少于 3 字节(byte)。在行头信息之后依次
储存的是各列的列长(column length)及列值(column value)。列长存储于列值之前,如列值不超过250 字节,那么 Oracle使用1字节存
储其列长;如列值超过 250 字节,则使用 3 字节存储其列长。列数据(column data)所需的存储空间取决于此列的数据类型(datatype)。
如果某列的数据类型为变长(variable length)的,那么存储此列值所需的空间可能会随着数据更新而增长或缩小。

总结:
1.如果列值长度小于等于250字节,Oracle使用1字节存储其列长.内容为字段的长度.
2.如果列值长度超过250字节,则使用3字节存储其列长。前面1个字节使用0xfe(表示超过250),后面2个字节表示列值长度.


--所以,我开始想这个应该跟字符串在数据块的保存方式一致,实际情况呢?做一个测试看看:

1.建立测试环境:

SCOTT@test> @ver
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

create table t ( a varchar2(200),b varchar2(3000));
insert into t values (lpad('1',127,'1'),lpad('a',127,'a'));
insert into t values (lpad('2',127,'2'),lpad('b',128,'b'));
insert into t values (lpad('3',127,'3'),lpad('c',1000,'c'));
commit;
create index i_t_a_b on t(a,b);
exec dbms_stats.gather_table_stats(ownname=>user, tabname=>'t', estimate_percent=>null, method_opt=>'FOR ALL COLUMNS SIZE 1');

2.转储索引信息:
SCOTT@test> select segment_name,header_file,header_block from dba_segments where owner=user and segment_name='I_T_A_B';
SEGMENT_NAME         HEADER_FILE HEADER_BLOCK
-------------------- ----------- ------------
I_T_A_B                        4          562

--4*127+128+1000=1636,可以估计应该都保存在索引的root节点.为EADER_BLOCK+1.

alter system checkpoint ;
alter system dump datafile 4 block 563 ;

Leaf block dump
===============
header address 182927614564=0x2a9755e264
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 3
kdxcosdc 0
kdxconro 3
kdxcofbo 42=0x2a
kdxcofeo 6361=0x18d9
kdxcoavs 6319
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8032
row#0[7767] flag: ------, lock: 0, len=265
col 0; len 127; (127):
31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
...
31 31
col 1; len 127; (127):
61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
...
61 61
col 2; len 6; (6):  01 00 02 13 00 00
row#1[7500] flag: ------, lock: 0, len=267
col 0; len 127; (127):
32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
...
32 32
col 1; len 128; (128):
62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62
...
62 62 62
col 2; len 6; (6):  01 00 02 13 00 01
row#2[6361] flag: ------, lock: 0, len=1139
col 0; len 127; (127):
33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
...
33 33
col 1; len 1000; (1000):
63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
....
63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
col 2; len 6; (6):  01 00 02 13 00 02
----- end of leaf block dump -----
End dump data blocks tsn: 4 file#: 4 minblk 563 maxblk 563


--可以发现第一条索引条目前面的flag,lock占用2个字节 1+127+1+127+1+6=263, 2+263=265,正好符合.
--但是第二条索引条目,仅仅b字段增加1个字节,应该总厂266才对,而实际上是267.why?

SCOTT@test> @10to16 1000
10 to 16 HEX   REVERSE16
-------------- ------------------
00000000003e8 0xe8030000

--从转储内容看:
Dump of memory from 0x0000002A9755E200 to 0x0000002A97560200
2A9755E200 0000A206 01000233 B7C4186D 04010002  [....3...m.......]
2A9755E210 0000C7B6 00046F02 0004735E B7C4186B  [.....o..^s..k...]
2A9755E220 00000002 00320002 01000230 00000000  [......2.0.......]
2A9755E230 00000000 00000000 00000000 00000000  [................]
2A9755E240 00000000 0000FFFF 00000000 00000000  [................]
2A9755E250 00000000 00028000 B7C4186B 00000000  [........k.......]
2A9755E260 00000000 03800000 00000000 002A0003  [..............*.]
2A9755E270 18AF18D9 00000000 00000000 00000000  [................]
2A9755E280 00000000 00001F60 1D4C1E57 AAAA18D9  [....`...W.L.....]
2A9755E290 AAAAAAAA AAAAAAAA AAAAAAAA AAAAAAAA  [................]
        Repeat 118 times
2A9755EA00 AAAAAAAA AAAAAAAA 00000000 00000000  [................]
2A9755EA10 00000000 00000000 00000000 00000000  [................]
        Repeat 273 times
2A9755FB30 00000000 00000000 00000000 7F000000  [................]
2A9755FB40 33333333 33333333 33333333 33333333  [3333333333333333]
        Repeat 6 times
2A9755FBB0 33333333 33333333 33333333 83333333  [333333333333333.]
2A9755FBC0 636363E8 63636363 63636363 63636363  [.ccccccccccccccc]  <==83e8 表示字段b的长度1000,而1000的16进制表示是0x03e8.
2A9755FBD0 63636363 63636363 63636363 63636363  [cccccccccccccccc]
        Repeat 60 times
2A9755FFA0 63636363 63636363 00010663 02001302  [ccccccccc.......]
2A9755FFB0 327F0000 32323232 32323232 32323232  [...2222222222222]
2A9755FFC0 32323232 32323232 32323232 32323232  [2222222222222222]
        Repeat 6 times
2A97560030 80803232 62626262 62626262 62626262  [22..bbbbbbbbbbbb]  <=== 8080 表示字段b的长度128,可以猜测另外80表示长度大于等于128.
2A97560040 62626262 62626262 62626262 62626262  [bbbbbbbbbbbbbbbb]
        Repeat 6 times
2A975600B0 62626262 02000106 00010013 31317F00  [bbbb..........11]
2A975600C0 31313131 31313131 31313131 31313131  [1111111111111111]
        Repeat 6 times
2A97560130 31313131 31313131 31313131 61617F31  [1111111111111.aa]  <=== 7F  表示字段b的长度127
2A97560140 61616161 61616161 61616161 61616161  [aaaaaaaaaaaaaaaa]
        Repeat 6 times
2A975601B0 61616161 61616161 61616161 00010661  [aaaaaaaaaaaaa...]
2A975601C0 00001302 00000000 00000000 00000000  [................]
2A975601D0 00000000 00000000 00000000 00000000  [................]
        Repeat 1 times
2A975601F0 00000000 00000000 00000000 186D0601  [..............m.]

对比转储,可以猜测与总结如下:

1.当字符串长度小于等于127时,使用1个字节表示长度.
2.当字符串大于等于128时,使用2个字节来保存长度,内容为字符串长度+0x8000.
3.真搞不懂为什么与数据块的保存方式不同,oracle要创造2种不同的方式保存字符串.


--补充通过bbed观察看看.
BBED> set dba 4,563
        DBA             0x01000233 (16777779 4,563)

BBED> p kd_off
sb2 kd_off[0]                               @132      8032
sb2 kd_off[1]                               @134      0
sb2 kd_off[2]                               @136      7767

--kd_off[1] 指向 0,难道中间靠前面的信息来推断吗? 这个东西不是很理解,先放一下.

BBED> p *kd_off[2]
rowdata[1410]
-------------
ub1 rowdata[1410]                           @7867     0x00

BBED> x /rccx
rowdata[1410]                               @7867
-------------
flag@7867:     0x00 (NONE)
lock@7868:     0x00
data key:
col  0[127] @7870: 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
col  1[127] @7998: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
col    2[6] @8126:  0x01  0x00  0x02  0x13  0x00  0x00

BBED> dump /v offset 7997 count 32
File: /u01/app/oracle11g/oradata/test/users01.dbf (4)
Block: 563                                                         Offsets: 7997 to 8028                                                      Dba:0x01000233
---------------------------------------------------------------------------------------------------------------------------------------------------------------
7f616161 61616161 61616161 61616161 61616161 61616161 61616161 61616161                                     l .aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

<48 bytes per line>

-- 0x7f=127.

BBED> set offset 7600
        OFFSET          7600

BBED> x /rccx
rowdata[1143]                               @7600
-------------
flag@7600:     0x00 (NONE)
lock@7601:     0x00
data key:
col  0[127] @7603: 2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
col  1[128] @7732: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
col    2[6] @7861:  0x01  0x00  0x02  0x13  0x00  0x01

BBED> dump /v offset 7730 count 32
File: /u01/app/oracle11g/oradata/test/users01.dbf (4)
Block: 563                                                         Offsets: 7730 to 7761                                                      Dba:0x01000233
---------------------------------------------------------------------------------------------------------------------------------------------------------------
80806262 62626262 62626262 62626262 62626262 62626262 62626262 62626262                                     l ..bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

<48 bytes per line>

--8080表示长度128.

BBED> set offset 6461
        OFFSET          6461

BBED> x /rccx
rowdata[4]                                  @6461
----------
flag@6461:     0x00 (NONE)
lock@6462:     0x00
data key:
col  0[127] @6464: 3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
col 1[1000] @6593: ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
...
cccc
col    2[6] @7594:  0x01  0x00  0x02  0x13  0x00  0x02


BBED> dump /v offset 6591 count 32
File: /u01/app/oracle11g/oradata/test/users01.dbf (4)
Block: 563                                                         Offsets: 6591 to 6622                                                      Dba:0x01000233
---------------------------------------------------------------------------------------------------------------------------------------------------------------
83e86363 63636363 63636363 63636363 63636363 63636363 63636363 63636363                                     l ..cccccccccccccccccccccccccccccc

<48 bytes per line>

--83e8 表示长度1000.

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/267265/viewspace-1291526/,如需转载,请注明出处,否则将追究法律责任。

全部评论
熟悉oracle相关技术,擅长sql优化,rman备份与恢复,熟悉linux shell编程。

注册时间:2008-01-03

  • 博文量
    2698
  • 访问量
    6480271