| hai's profileOracle & StarcraftBlogLists | Help |
|
January 19 oracle rowid搞oracle的人都很清楚rowid这个东西,rowid就是唯一标志记录物理位置的一个id,在oracle 8版本以前,rowid由file#+block#+row#组成,占用6个bytes的空间,10 bit 的 file# ,22bit 的 block# ,16 bit 的 row#。
从oracle 8开始rowid变成了extend rowid,由data_object_id#+rfile#+block#+row#组成,占用10个bytes的空间, 32bit的 data_object_id#,10 bit 的 rfile#,22bit 的 block#,16 bit 的 row#.由于rowid的组成从file#变成了rfile#,所以数据文件数的限制也从整个库不能超过1023个变成了每个表空间不能超过1023个数据文件。
说了rowid的组成,那么我们再来看看rowid在索引里面占用的字节数又是什么样子的。在oracle 8以前索引中存储的rowid占用字节数也是6bytes,在oracle8之后,虽然oracle使用了extend rowid,但是在普通索引里面依然存储了bytes的rowid,只有在global index中存储的是10bytes的extend rowid,而extend rowid也是global index出现的一个必要条件,下面我们会解释原因。
为什么golbal index需要把data_object_id#也包含在index rowid entry中呢?如果不包含会这么样?首先我们需要知道index的rowid entry的存在是为了能根据它找到表的这条记录存在哪个具体的物理位置,我们需要知道它在哪个数据文件,在哪个block,在那一行,普通的索引oracle根据rfile#,block#,row#就可以知道了,但是partition table可以分布在多个表空间,也就是可以分布在多个数据文件,当我们建立local index时,index rowid entry并不包含data_object_id#,因为oracle可以知道这个index对应的是哪一个table分区,并可以得到table分区的ts#(tablespace号),那么oracle根据ts#和rfile#就可以找到具体的数据文件。但是如果换成是golbal index,如果不包含data_object_id#,那么我们并不能知道这个索引对应着哪个表分区,也自然不能知道它的rfile#和file#的转换关系,所以它将找不到所对应的记录。包含data_object_id#后,oracle可以根据data_object_id#实现rfile#和file#的转换然后找到记录对应的物理位置。需要注意的是要理解以上概念我们还是需要了解file#和rfile#的区别。
关于file#和rfile#的区别可以参考biti_rainy的一篇blog
继续上面的话题,我们猜想oracle实现rfile#和file#的转换是不是由一些递归sql来实现,所以我们做了一个测试来看是否oracle从file$等基表中实现呢?我们做了一个10046 trace,发现并不存在相应的递归sql,那oracle怎么实现呢?可能是直接取一些x$table中的数据了吧,那哪个x$table保留这些信息呢?
x$kccfe!!!
SQL 10G>desc x$kccfe
Name Null? Type ----------------------------------------- -------- ---------------------------- ADDR RAW(4) INDX NUMBER INST_ID NUMBER FENUM NUMBER FECSZ NUMBER FEBSZ NUMBER FESTA NUMBER FECRC_SCN VARCHAR2(16) FECRC_TIM VARCHAR2(20) FECRC_THR NUMBER FECRC_RBA_SEQ NUMBER FECRC_RBA_BNO NUMBER FECRC_RBA_BOF NUMBER FECRC_ETB RAW(132) FECPS VARCHAR2(16) FECPT VARCHAR2(20) FECPC NUMBER FESTS VARCHAR2(16) FESTT VARCHAR2(20) FEBSC VARCHAR2(16) FEFNH NUMBER FEFNT NUMBER FEDUP NUMBER FEURS VARCHAR2(16) FEURT VARCHAR2(20) FEOFS VARCHAR2(16) FEONC_SCN VARCHAR2(16) FEONC_TIM VARCHAR2(20) FEONC_THR NUMBER FEONC_RBA_SEQ NUMBER FEONC_RBA_BNO NUMBER FEONC_RBA_BOF NUMBER FEONC_ETB RAW(132) FEPOR NUMBER FETSN NUMBER FETSI NUMBER FERFN NUMBER FEPFT NUMBER FEDOR NUMBER FEPDI NUMBER FEFDB NUMBER FEPLG_SCN VARCHAR2(16) FEPAX NUMBER FEFLG NUMBER 从这个x$table中oracle可以实现file和rfile的转换。
最后我们来看一个例子
SQL 10G>desc test
Name Null? Type ----------------------------------------- -------- ---------------------------- A NUMBER B VARCHAR2(32) test是一张分区表
SQL 10G>select table_name,partition_name,tablespace_name from dba_tab_partitions where table_name='TEST';
TABLE_NAME PARTITION_NAME TABLESPACE_NAME
------------------------------ ------------------------------ ------------------------------ TEST P1 TESTROWID TEST P2 TESTROWID 这条记录所在的物理位置
SQL 10G>select dbms_rowid.ROWID_OBJECT(rowid) data_object_id#,dbms_rowid.ROWID_RELATIVE_FNO(rowid) rfile#,dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block#,dbms_rowid.ROWID_ROW_NUMBER(rowid) row# from test ;
DATA_OBJECT_ID# RFILE# BLOCK# ROW#
--------------- ---------- ---------- ---------- 63665 28 15 0 我们对它创建全局索引
SQL 10G>create index ind_test on test(a);
Index created.
再创建本地索引
SQL 10G>create index ind_test_local on test(b) local;
Index created. SQL 10G>select dump(rowid,16) rid from test;
RID
---------------------------------------------------------------------------- Typ=69 Len=10: 0,0,f8,b1,7,0,0,f,0,0 去看看全局索引和本地索引中rowid entry的区别
SQL 10G>select header_block,header_file from dba_segments where segment_name='IND_TEST';
HEADER_BLOCK HEADER_FILE
------------ ----------- 1403 4 SQL 10G>select header_block,header_file from dba_segments where segment_name='IND_TEST_LOCAL';
HEADER_BLOCK HEADER_FILE
------------ ----------- 11 33 11 34 SQL 10G>ALTER SYSTEM DUMP DATAFILE 4 BLOCK 1404;
System altered.
SQL 10G>ALTER SYSTEM DUMP DATAFILE 33 BLOCK 12;
System altered.
全局索引ind_test的rowid entry
col 1; len 10; (10): 00 00 f8 b1 07 00 00 0f 00 00 本地索引ind_test_local的rowid entry
col 1; len 6; (6): 07 00 00 0f 00 00
可以看出本地索引存储了6bytes rowid,全局索引存储了10bytes rowid
再来看一下00 00 f8 b1 07 00 00 0f 00 00
转换成bit就是
00000000 00000000 11111000 10110001 00000111 00000000 00000000 00001111 00000000 00000000
32bit的 data_object_id#,10 bit 的 rfile#,22bit 的 block#,16 bit 的 row#.
00000000 00000000 11111000 10110001 data_object_id#
转换成10进制就是
2^15+2^14+2^13+2^12+2^11+2^7+2^5+2^4+2^0=63665
00000111 00 rfile#
2^4+2^3+2^2=28
0000000000000000001111 block#
2^3+2^2+2^1+2^0=15
0000000000000000 rowi# 0
SQL 10G>select dbms_rowid.ROWID_OBJECT(rowid) data_object_id#,dbms_rowid.ROWID_RELATIVE_FNO(rowid) rfile#,dbms_rowid.ROWID_BLOCK_NUMBER(rowid) block#,dbms_rowid.ROWID_ROW_NUMBER(rowid) row# from test ;
DATA_OBJECT_ID# RFILE# BLOCK# ROW#
--------------- ---------- ---------- ---------- 63665 28 15 0 验证通过
再来看一下如果file#超过1023后oracle会怎么处理
SQL 10G>select file#,ts#,rfile# from v$datafile where file#>1023;
FILE# TS# RFILE#
---------- ---------- ---------- 1024 14 1 1025 14 3 1026 14 4 1027 14 5 1028 14 6 1029 14 7 1030 14 8 1031 14 9 1032 14 10 1033 14 11 1034 14 12 FILE# TS# RFILE#
---------- ---------- ---------- 1035 14 13 1036 14 14 1037 15 14 可以看到在一个tablespace里面rfile#从1开始到1023
SQL 10G>select file#,rfile# from v$datafile where ts#=14 order by file#;
FILE# RFILE#
---------- ---------- 2 2 15 15 16 16 17 17 18 18 19 19 20 20 21 21 22 22 23 23 24 24 。。。。。。 FILE# RFILE#
---------- ---------- 1015 1015 1016 1016 1017 1017 1018 1018 1019 1019 1020 1020 1021 1021 1022 1022 1023 1023 1024 1 1025 3 FILE# RFILE# ---------- ---------- 1026 4 1027 5 1028 6 1029 7 1030 8 1031 9 1032 10 1033 11 1034 12 1035 13 1036 14 January 16 what is data_object_id前些天有人问data_object_id究竟是个什么东东,既然有了object_id那还要data_object_id干吗?
其实object_id和data_object_id同样是表示数据库对象的一个唯一标志,但是object_id表示的是逻辑id,data_object_id表示的是物理id。如果一些object没有物理属性的话那它就不存在data_object_id,例如procedure,function,package,data type,db link,mv定义,view定义,临时表,分区表定义等等这些object都是没有对应着某个segment,因此它们的data_object_id都为空。
当表刚创建的时候它的object_id和data_object_id都是相等的,但是如果表经过move或truncate后那么data_object_id将会有变化。
SQL 10G>select object_id,data_object_id from user_objects where object_name='T'; OBJECT_ID DATA_OBJECT_ID
---------- -------------- 63053 63053 刚开始创建表时object_id=data_object_id
SQL 10G>SELECT HEADER_FILE,HEADER_BLOCK,BLOCKS FROM DBA_SEGMENTS WHERE SEGMENT_NAME='T' AND OWNER='TEST';
HEADER_FILE HEADER_BLOCK BLOCKS
----------- ------------ ---------- 4 2019 8 SQL 10G>alter table t move;
Table altered.
SQL 10G>select object_id,data_object_id from user_objects where object_name='T';
OBJECT_ID DATA_OBJECT_ID
---------- -------------- 63053 63463 在move以后可以看到data_object_id发生变化了
SQL 10G>SELECT HEADER_FILE,HEADER_BLOCK,BLOCKS FROM DBA_SEGMENTS WHERE SEGMENT_NAME='T' AND OWNER='TEST';
HEADER_FILE HEADER_BLOCK BLOCKS
----------- ------------ ---------- 4 467 8 SQL 10G>truncate table t;
Table truncated.
SQL 10G>select object_id,data_object_id from user_objects where object_name='T';
OBJECT_ID DATA_OBJECT_ID
---------- -------------- 63053 63464 SQL 10G>SELECT HEADER_FILE,HEADER_BLOCK,BLOCKS FROM DBA_SEGMENTS WHERE SEGMENT_NAME='T' AND OWNER='TEST';
HEADER_FILE HEADER_BLOCK BLOCKS
----------- ------------ ---------- 4 467 8 truncate之后虽然segment的位置没有移动,但是data_object_id还是发生变化了。
SQL 10G>alter table t add(b number);
Table altered.
SQL 10G>select object_id,data_object_id from user_objects where object_name='T';
OBJECT_ID DATA_OBJECT_ID ---------- -------------- 63053 63464 我们对t表加一个字段,发现data_object_id没有发生变化,我们可以认为只有当segment发生变化时data_object_id才会随之变化。我们来看看data_object_id从哪里来的
SQL 10G> select text from dba_views where view_name='DBA_OBJECTS';
select u.name, o.name, o.subname, o.obj#, o.dataobj#,
decode(o.type#, 0, 'NEXT OBJECT', 1, 'INDEX', 2, 'TABLE', 3, 'CLUSTER', 4, 'VIEW', 5, 'SYNONYM', 6, 'SEQUENCE', 7, 'PROCEDURE', 8, 'FUNCTION', 9, 'PACKAGE', 11, 'PACKAGE BODY', 12, 'TRIGGER', 13, 'TYPE', 14, 'TYPE BODY', 19, 'TABLE PARTITION', 20, 'INDEX PARTITION', 21, 'LOB', 22, 'LIBRARY', 23, 'DIRECTORY', 24, 'QUEUE', 28, 'JAVA SOURCE', 29, 'JAVA CLASS', 30, 'JAVA RESOURCE', 32, 'INDEXTYPE', 33, 'OPERATOR', 34, 'TABLE SUBPARTITION', 35, 'INDEX SUBPARTITION', 40, 'LOB PARTITION', 41, 'LOB SUBPARTITION', 42, NVL((SELECT distinct 'REWRITE EQUIVALENCE' FROM sum$ s
WHERE s.obj#=o.obj# and bitand(s.xpflags, 8388608) = 8388608), 'MATERIALIZED VIEW'), 43, 'DIMENSION', 44, 'CONTEXT', 46, 'RULE SET', 47, 'RESOURCE PLAN', 48, 'CONSUMER GROUP', 51, 'SUBSCRIPTION', 52, 'LOCATION', 55, 'XML SCHEMA', 56, 'JAVA DATA', 57, 'SECURITY PROFILE', 59, 'RULE', 60, 'CAPTURE', 61, 'APPLY', 62, 'EVALUATION CONTEXT', 66, 'JOB', 67, 'PROGRAM', 68, 'JOB CLASS', 69, 'WINDOW', 72, 'WINDOW GROUP', 74, 'SCHEDULE', 79, 'CHAIN',
81, 'FILE GROUP', 'UNDEFINED'), o.ctime, o.mtime, to_char(o.stime, 'YYYY-MM-DD:HH24:MI:SS'), decode(o.status, 0, 'N/A', 1, 'VALID', 'INVALID'), decode(bitand(o.flags, 2), 0, 'N', 2, 'Y', 'N'), decode(bitand(o.flags, 4), 0, 'N', 4, 'Y', 'N'), decode(bitand(o.flags, 16), 0, 'N', 16, 'Y', 'N') from sys.obj$ o, sys.user$ u where o.owner# = u.user# and o.linkname is null and (o.type# not in (1 /* INDEX - handled below */, 10 /* NON-EXISTENT */)
or (o.type# = 1 and 1 = (select 1 from sys.ind$ i where i.obj# = o.obj# and i.type# in (1, 2, 3, 4, 6, 7, 9)))) and o.name != '_NEXT_OBJECT' and o.name != '_default_auditing_options_' union all select u.name, l.name, NULL, to_number(null), to_number(null), 'DATABASE LINK', l.ctime, to_date(null), NULL, 'VALID','N','N', 'N' from sys.link$ l, sys.user$ u where l.owner# = u.user#
可以看到data_object_id是从obj$.dataobj#来的,而obj$.dataobj#又对应着seg$.HWMINCR
SQL 10G>select max(HWMINCR) from sys.seg$;
MAX(HWMINCR) ------------ 63464 这里的HWMINCR就是data_object_id的来源,每次seg$里生成新的一条记录都会增加HWMINCR这个值,同时obj$.dataobj#也会跟着变化。
|
|
|