在上一节提到,对于大文件表空间其相对文件号(RELATIVE_FNO)为1024:
SQL> select file_name,file_id,relative_fno from dba_data_files 2 where relative_fno=1024; FILE_NAME FILE_ID RELATIVE_FNO -------------------------------------------------- ---------- ------------ +DATADG/smsboss/datafile/bossmgr.271.577809233 5 1024
那么文件号和相对文件号有什么区别呢?又为什么会有这两个东西呢?
Oracle的ROWID及数据库限制
众所周知,数据存放在数据文件中,因为其存储,数据的一系列属性就随之确定,这些属性包括记录存储所在的数据文件(file#)、所属的数据库对象(obj#)、所在的数据块号(block_no#)以及在表中的行号等,将这些属性合并起来就构成了Oracle的ROWID。
Oracle的ROWID又可以分为两种:物理ROWID和逻辑ROWID。索引组织表使用逻辑ROWID,其他类型的表使用物理ROWID。这里主要介绍物理ROWID。
ROWID在表中并不存在,但是查询表时可以使用这一伪列:
SQL> select rowid,t.* from scott.dept t; ROWID DEPTNO DNAME LOC ------------------ ---------- -------------- ------------- AAACXuAABAAAGDyAAA 10 ACCOUNTING BEIJING AAACXuAABAAAGDyAAB 20 RESEARCH BEIJING AAACXuAABAAAGDyAAC 30 SALES BEIJING AAACXuAABAAAGDyAAD 40 OPERATIONS BEIJING
因为ROWID可以唯一地标识一条记录,所以索引(Index)中存储了ROWID值,通过索引访问记录实际上也就是通过从索引中获得ROWID,再根据ROWID到数据表中定位记录的过程。
前面提到,MOVE表之后索引需要重建就是因为存储位置发生了改变,索引中的ROWID无法定位到新的数据存储,所以需要重建。而Shrink过程因为使用的是DML操作,所以索引可以同时被维护,完成操作之后不再需要重建索引。
Oracle的ROWID在不同的版本中一直在不断变化:
· 在Oracle 6中,ROWID中仅用6 bit代表文件号,所以数据库最多只能有26=64个数据文件(去掉全0和全1,实际上最多只能代表62个文件);
· 到Oracle 8,ROWID组成扩展为FFFF.BBBBBBBB.RRRR,占用6个字节(其中10 bit file# + 22bit block# + 16bit row#);
· 在Oracle 8i中,Oracle更是在ROWID中引入了dataobj#,现在的ROWID格式变更为OOOOOO.FFF.BBBBBB.RRR,占用10个字节。新的ROWID采用Base64编码,一共有18位,代表80位二进制数,其中O是对象号,F是文件号,B是块号,R是行号,这80位的方式为32bit obj# +10bit rfile#+22bit block# +16bit row#。
ROWID的64个编码及代表的数字顺序如表5-3所示。
表5-3 ROWID的64个编码及其代表的顺序
通过上文可以知道,在Oracle 8i之前,ROWID中存放的是文件号,为了扩展数据库的存储能力,从Oracle 8i开始ROWID的组成中增加了对象号,而file#变成了rfile#。文件号到相对文件号的改变,使得数据库的数据文件数限制从全库不能超过1023个变成了每个表空间不能超过1023个数据文件。这就极大地扩展了Oracle数据库的存储能力。
了解了ROWID的构成之后,就可以很清楚地理解Oracle数据库的一些内部限制,例如:
· 每表空间最大文件数量=210=1024,去掉全0和全1,通常为1022个;
· 每数据文件最大数据块数量=222=4M Block,通常每个文件最大Block数量为4M个Oracle块;
· 每个Block中的行数=216=65536,通常每个Block最多只能容纳65536条记录。
BigFile表空间的ROWID
上一节介绍的是Oracle 10g之前的ROWID格式,Oracle 8i以来的ROWID格式为:
OOOOOO.FFF.BBBBBB.RRR
Oracle 10g引入了BigFile表空间,大文件表空间可以容纳更多的数据块,这是因为ROWID算法发生了改变,由于大文件表空间只能包含一个文件,所以ROWID中不再需要rfile#,大文件表空间的ROWID格式现在为:
OOOOOO.LLLLLLLLL.RRR
其中L代表新的Block号,现在有32位用于代表Block号,所以对于大文件表空间,现在最大能够容纳4G个Block。如果block_size为32kB,大文件表空间最大容量可以达到4G×32kB =128TB。
使用dbms_rowid包获得ROWID的详细信息
ROWID中包含了记录的详细信息,通过Oracle提供的系统包dbms_rowid可以获得这些信息,我们可以通过一个自定义函数来简化该系统包的使用:
create or replace function get_rowid (l_rowid in varchar2) return varchar2 is ls_my_rowid varchar2(200); rowid_type number; object_number number; relative_fno number; block_number number; row_number number; begin dbms_rowid.rowid_info(l_rowid,rowid_type,object_number,relative_fno, block_number, row_number); ls_my_rowid := 'Object# is :'||to_char(object_number)||chr(10)|| 'Relative_fno is :'||to_char(relative_fno)||chr(10)|| 'Block number is :'||to_char(block_number)||chr(10)|| 'Row number is :'||to_char(row_number); return ls_my_rowid ; end; /
来看一下其用法:
SQL> select rowid,a.* from dept a; ROWID DEPTNO DNAME LOC ------------------ ---------- -------------- ------------- AAABiPAABAAAFRSAAA 10 ACCOUNTING NEW YORK AAABiPAABAAAFRSAAB 20 RESEARCH DALLAS AAABiPAABAAAFRSAAC 30 SALES CHICAGO AAABiPAABAAAFRSAAD 40 OPERATIONS BOSTON 通过get_rowid函数可以转换出ROWID中的信息: SQL> select get_rowid('AAABiPAABAAAFRSAAA') row_id from dual; ROW_ID ------------------------------------------------------------ Object# is :6287 Relative_fno is :1 Block number is :21586 Row number is :0
在Oracle 10g中,针对大文件表空间,DBMS_ROWID包中增加TS_TYPE_IN参数用于区分SMALLFILE或BIGFILE表空间。