原文链接:https://www.modb.pro/db/22554?cyn

摘要:描述Oracle数据库的物理ROWID类型的存储结构

Oracle的ROWID用来唯一标识表中的一条记录,是这条数据在数据库中存放的物理地址。
Oracle的ROWID分为两种:物理ROWID和逻辑ROWID。索引组织表使用逻辑ROWID,其他类型的表使用物理ROWID。其中物理ROWID在Oracle的8版本中进行了扩展,Oracle7及以下版本使用约束ROWID,Oracle8及以上版本使用扩展ROWID。本文描述物理扩展ROWID,由于约束ROWID仅仅是为了兼容早期版本,因此不做讨论。

SQL>createtabletest_rowid(idnumber,row_idrowid);表已创建。SQL>insertintotest_rowidvalues(1,null);已创建1行。SQL>updatetest_rowidsetrow_id=rowidwhereid=1;已更新1行。SQL>commit;提交完成。SQL>selectrowid,row_idfromtest_rowid;ROWIDROW_ID------------------------------------AAABnRAAGAAAACWAAAAAABnRAAGAAAACWAAA

Oracle的物理扩展ROWID有18位,每位采用64位编码,分别用AZ、az、0~9、+、/共64个字符表示。A表示0,B表示1,……Z表示25,a表示26,……z表示51,0表示52,……,9表示61,+表示62,/表示63。
ROWID具体划分可以分为4部分。
1.OOOOOO:前6位表示DATA OBJECT NUMBER,将起转化位数字后匹配DBA_OBJECTS中的DATA_OBJECT_ID,可以确定表信息。
如上面例子中的DATA OBJECT NUMBER是AAABnR,转化位数字是1×64×64 +39×64 + 17。

SQL>selectowner,object_namefromdba_objects2wheredata_object_id=1*64*64+39*64+17;OWNEROBJECT_NAME-----------------------------------------------------------YANGTKTEST_ROWID

2.FFF:第7到9位表示相对表空间的数据文件号。
上面的例子中是AAG,表示数据文件6。

SQL>selectfile_name,tablespace_namefromdba_data_fileswhererelative_fno=6;FILE_NAMETABLESPACE_NAME------------------------------------------------------------E:\ORACLE\ORADATA\TEST\YANGTK01.DBFYANGTK

3.BBBBBB:第10到15位表示这条记录在数据文件中的第几个BLOCK中。
上面的例子是AAAACW,转化位数字是2×64+22,表示这条记录在数据文件中的第150个BLOCK。
4.RRR:最后3位表示这条记录是BLOCK中的第几条记录。
上面的例子是AAA,表示第0条记录(总是从0开始计数)。

SQL>altersystemdumpdatafile6block150;系统已更改。SQL>selectrow_id,dump(row_id,16)dump_rowidfromtest_rowid;ROW_IDDUMP_ROWID-------------------------------------------------------------------AAABnRAAGAAAACWAAATyp=69Len=10:0,0,19,d1,1,80,0,96,0,0

找到对应的dump文件,可以发现类型的信息

***2004-12-2117:58:26.000***SESSIONID:(13.91)2004-12-2117:58:26.000Startdumpdatablockstsn:6file#:6minblk150maxblk150buffertsn:6rdba:0x01800096(6/150)scn:0x0000.2e389c16seq:0x01flg:0x06tail:0x9c160601frmt:0x02chkval:0xc97dtype:0x06=transdataBlockheaderdump:0x01800096ObjectidonBlock?Yseg/obj:0x19d1csc:0x00.2e389c0fitc:2flg:Otyp:1-DATAfsl:0fnx:0x0ver:0x01ItlXidUbaFlagLckScn/Fsc0x010x0003.009.000000570x0080004b.0042.56--U-1fsc0x0000.2e389c160x020x0000.000.000000000x00000000.0000.00----0fsc0x0000.00000000data_block_dump,dataheaderat0x651105c===============tsiz:0x3fa0hsiz:0x14pbl:0x0651105cbdba:0x0180009676543210flag=--------ntab=1nrow=1frre=-1fsbo=0x14fseo=0x3f89avsp=0x3f7btosp=0x3f7b0xe:pti[0]nrow=1offs=00x12:pri[0]offs=0x3f89block_row_dump:tab0,row0,@0x3f89tl:17fb:--H-FL--lb:0x1cc:2col0:[2]c102col1:[10]000019d1018000960000end_of_block_dumpEnddumpdatablockstsn:6file#:6minblk150maxblk150

有时需要查看表的DUMP信息,但是很难准确定位表中数据开始于哪个BLOCK,根据ROWID中包含的信息就可以方便的找到起始BLOCK。
下面简单描述一下ROWID类型是如何存储的。

SQL>selectrow_id,dump(row_id,16)dump_rowidfromtest_rowid;ROW_IDDUMP_ROWID-------------------------------------------------------------------AAABnRAAGAAAACWAAATyp=69Len=10:0,0,19,d1,1,80,0,96,0,0

前4位表示ROWID的前6位,也就是DATA_OBJECT_ID信息。数据以数值的格式保存。

SQL>selectto_number('19d1','xxxxxx')fromdual;TO_NUMBER('19D1','XXXXXX')--------------------------6609SQL>select1*64*64+39*64+17fromdual;1*64*64+39*64+17----------------6609

这里存在一个问题,根据ROWID的取值范围,OBJECT_DATA_ID最大的值是64的6次方,而根据DUMP,oracle只用了4位保存,因此取值范围是256的4次方。

SQL>setnumwid12SQL>selectpower(64,6),power(256,4),power(64,6)/power(256,4)fromdual;POWER(64,6)POWER(256,4)POWER(64,6)/POWER(256,4)------------------------------------------------68719476736429496729616

可见,OBJECT_DATA_ID的最大值是4294967296,当超过这个值时会出现重复的情况。(当然,现实中不大可能)。
后面4位比较特殊,是数据文件号和BLOCK数的“和”值构成。
数据文件的数值乘64后保存在5、6位上。

SQL>selectto_number('0180','xxxx')fromdual;TO_NUMBER('0180','XXXX')------------------------384SQL>select6*64fromdual;6*64------------384

同时,6位BLOCK的值,也保存在这4位上,并与数据文件转存结果相加。仍然是以数字格式存放。

SQL>selectto_number('96','xxx')fromdual;TO_NUMBER('96','XXX')---------------------150SQL>select2*64+22fromdual;2*64+22----------150

由于采用两位保存数据文件的值,且最小单位是64,因此,ROWID中可以保存的数据文件数是1024,超过1024会造成ROWID的重复。

SQL>select256*256/64fromdual;256*256/64----------1024

由于BLOCK的值和数据文件共用这4位,因此BLOCK的第3位最大值应小于64,这样才能保证ROWID的不重复。因此BLOCK值的最大值应该是4194304。

SQL>select64*256*256fromdual;64*256*256----------4194304

最后两位保存BLOCK中记录的值。这个值的最大值是65536。

SQL>select256*256fromdual;256*256----------65536

下面看一个例子,Oracle是如何将相对文件号和BLOCK号“共享”第5、6字节的。

SQL>selectblocksfromuser_segmentswheresegment_name='TEST1';BLOCKS----------86016SQL>selectmax(rowid),dump(max(rowid))dump_rowidfromtest1;MAX(ROWID)DUMP_ROWID-------------------------------------------------------------AAABy+AAJAAAU5EAAMTyp=69Len=10:0,0,28,190,2,65,78,68,0,12SQL>selectdbms_rowid.rowid_relative_fno('AAABy+AAJAAAU5EAAM')fno,2dbms_rowid.rowid_block_number('AAABy+AAJAAAU5EAAM')block_numfromdual;FNOBLOCK_NUM--------------------985572SQL>select9*64,2*256+65fromdual;9*642*256+65--------------------576577SQL>select1*256*256+78*256+68fromdual;1*256*256+78*256+68-------------------85572

可以看到,5、6为的值除以64得到的商是相对文件号,余数是BLOCK号的高位,乘以65536后加上低两位才是BLOCK号。

相关阅读:Oracle基本数据类型存储格式浅析——逻辑ROWID https://www.modb.pro/db/22638?cyn