《Oracle唯一索引和NULL空值之间的关系》文章写到了dump索引数据块,当我们需要看一些数据表、索引在块上的存储形式时,dump数据块是一种很直接的操作。
1. 创建测试表,含1000条数据,创建唯一索引,
SQL> create table tt1 as select owner, object_id, object_name from dba_objects where rownum < 1001;
Table created.
SQL> create unique index idx_tt1_01 on tt1(owner, object_id, object_name);
Index created.
SQL> select count(*) from tt1;
COUNT(*)
----------
1000
可知他现在有5个叶子节点块,索引层高为2,
SQL> select table_name,index_name,blevel,leaf_blocks,num_rows,last_analyzed,degree,status from user_indexes where table_name='TT1';
TABLE_NAME INDEX_NAME BLEVEL LEAF_BLOCKS NUM_ROWS LAST_ANALYZED DEGREE STATUS
------------------------- ------------------------- ---------- ----------- ---------- ------------------ ---------------------------------------- --------
TT1 IDX_TT1_01 1 5 1000 26-APR-22 1 VALID
2. 查询索引的object_id,
SQL> select object_id from user_objects where object_name='IDX_TT1_01';
OBJECT_ID
----------
73011
3. 查询索引的treedump,此处的参数,就是索引的object_id,
SQL> alter session set events 'immediate trace name treedump level 73011';
Session altered.
4. 到Oracle的trace路径下,可以直接cd,或者通过oradebug setmypid -> oradebug tracefile_name得到trace文件名,文件片段如下,可以看到有5个leaf,这和上面查询到的索引包含5个节点叶子块是一致的,
----- begin tree dump
*** 2022-04-25T19:08:55.943552+08:00 (BISALPDB1(3))
branch: 0x3002bbb 50342843 (0: nrow: 5, level: 1)
leaf: 0x3002bbc 50342844 (-1: row:270.270 avs:837)
leaf: 0x3002bbd 50342845 (0: row:225.225 avs:816)
leaf: 0x3002bbe 50342846 (1: row:224.224 avs:840)
leaf: 0x3002bbf 50342847 (2: row:234.234 avs:826)
leaf: 0x3002bc0 50342850 (3: row:50.50 avs:6158)
----- end tree dump
5. 我们可以dump其中一个索引叶子节点的数据块,例如第五个leaf,根据值50342850,通过dbms_utility包的data_block_address_file和data_block_address_block就可以得到这个索引叶子节点对应的数据文件号、数据块号,此处是12号文件的第11200个数据块,
SQL> select dbms_utility.data_block_address_file(50342850) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(50342850)
----------------------------------------------
12
SQL> select dbms_utility.data_block_address_block(50342850) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(50342850)
-----------------------------------------------
11200
6. dump索引数据块,
SQL> alter system dump datafile 12 block 11200;
System altered.
从trace路径,就可以找到dump出来的文件,从以下位置开始,就是索引数据块的信息了,可以看到typ:2-INDEX表示他是个索引的数据块,
Block header dump: 0x03003b70
Object id on Block? Y
seg/obj: 0x11d33 csc: 0x0000000000f8b3d6 itc: 2 flg: E typ: 2 - INDEX
brn: 1 bdba: 0x3003b68 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x02 0x0008.00d.000007f7 0x024000f6.02bf.17 --U- 1 fsc 0x0000.00f8b3f1
Leaf block dump
===============
header address 1981317220=0x76188064
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 3
kdxcosdc 0
kdxconro 127
kdxcofbo 290=0x122
kdxcofeo 3435=0xd6b
kdxcoavs 3145
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 50346863=0x3003b6f
kdxledsz 6
kdxlebksz 8032
row#0[7990] flag: -------, lock: 0, len=42, data:(6): 03 00 2b d6 00 60
col 0; len 3; (3): 53 59 53
col 1; len 3; (3): c2 09 4d
col 2; len 25; (25):
53 59 53 5f 4c 4f 42 30 30 30 30 30 30 30 38 37 35 43 30 30 30 34 38 24 24
row#1[7949] flag: -------, lock: 0, len=41, data:(6): 03 00 2b d6 00 5f
col 0; len 3; (3): 53 59 53
col 1; len 3; (3): c2 09 4e
col 2; len 24; (24):
53 59 53 5f 49 4c 30 30 30 30 30 30 30 38 37 35 43 30 30 30 34 38 24 24
row#2[7907] flag: -------, lock: 0, len=42, data:(6): 03 00 2b d6 00 5e
col 0; len 3; (3): 53 59 53
col 1; len 3; (3): c2 09 4f
col 2; len 25; (25):
53 59 53 5f 4c 4f 42 30 30 30 30 30 30 30 38 37 35 43 30 30 30 34 39 24 24
...
row#125[3449] flag: -------, lock: 0, len=32, data:(6): 03 00 2b d6 00 da
col 0; len 3; (3): 53 59 53
col 1; len 3; (3): c2 0b 02
col 2; len 15; (15): 41 50 50 4c 59 24 5f 43 44 52 5f 49 4e 46 4f
row#126[3435] flag: -------, lock: 2, len=14, data:(6): 03 00 2b d7 00 00
col 0; len 1; (1): 61
col 1; len 2; (2): c1 02
col 2; NULL
----- end of leaf block Logical dump -----
----- end of leaf block dump -----
以第126行为例,
第1行的data(6)是rowid,
第2-4行是第1-3列索引值,
row#125[3449] flag: -------, lock: 0, len=32, data:(6): 03 00 2b d6 00 da
col 0; len 3; (3): 53 59 53
col 1; len 3; (3): c2 0b 02
col 2; len 15; (15): 41 50 50 4c 59 24 5f 43 44 52 5f 49 4e 46 4f
1. rowid
"data:(6): 03 00 2b d6 00 da"是16进制的rowid,先转成二进制,
00000011 00000000 00101011 11010110 00000000 11011010
按照rowid组成规则,
(1) 对应文件编号,表明该行所在文件的编号,表空间的每一个文件标号都是唯一的,文件编号所占用的位置是10位,如下所示,
00000011 00,10进制的文件号:12
(2) 块编号,表明改行所在文件的块的位置块编号需要22位,如下所示,
000000 00101011 11010110,10进制的块号:11222
(3) 行编号,表明该行在行目录中的具体位置行编号需要16位,如下所示,
00000000 11011010,10进制的行号:218
即第126行的数据是在第10个数据文件中第11222个数据块的第218行。
2. 索引实际值
col 0; len 3; (3): 53 59 53
col 1; len 3; (3): c2 0b 02
col 2; len 15; (15): 41 50 50 4c 59 24 5f 43 44 52 5f 49 4e 46 4f
dump文件中的索引值,可以转成实际的索引列的值,此时可以借助travel大神的函数f_get_from_dump,原始下载地址,应该被墙了,常规操作不让访问了,
http://www.traveldba.com/wp-content/uploads/scripts/f_get_from_dump.sql
除了翻墙,可以从我的github下载,
https://github.com/bisal-liu/oracle/blob/master/f_get_from_dump
他支持数值类型、字符串类型、ROWID类型等的转换,以第126行为例,
(1) 第一列是VARCHAR2类型的,
col 0; len 3; (3): 53 59 53
调用函数f_get_from_dump,得到值SYS,
SQL> select f_get_from_dump('53,59,53','VARCHAR2') from dual;
F_GET_FROM_DUMP('53,59,53','VARCHAR2')
--------------------------------------------------------------------------------
SYS
(2) 第二列是NUMBER类型的,
col 1; len 3; (3): c2 0b 02
调用函数f_get_from_dump,得到值1001,,
SQL> select f_get_from_dump('c2,0b,02','NUMBER') from dual;
F_GET_FROM_DUMP('C2,0B,02','NUMBER')
--------------------------------------------------------------------------------
1001
(3) 第三列是VARCHAR2类型的,
col 2; len 15; (15): 41 50 50 4c 59 24 5f 43 44 52 5f 49 4e 46 4f
调用函数f_get_from_dump,得到值APPLY$_CDR_INFO,
SQL> select f_get_from_dump('41,50,50,4c,59,24,5f,43,44,52,5f,49,4e,46,4f','VARCHAR2') from dual;
F_GET_FROM_DUMP('41,50,50,4C,59,24,5F,43,44,52,5F,49,4E,46,4F','VARCHAR2')
--------------------------------------------------------------------------------
APPLY$_CDR_INFO
从这些数据块的文件中,我们就可以了解表的数据、索引的数据是怎么物理存储的,对于理解一些原理层的知识,非常有用,当然要做到融会贯通,从现象到本质,还得加以时日的训练,还在学习中。