[20141008]索引字串的長度問題.txt

lfree發表於2014-10-08

[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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章