设为首页 收藏本站
查看: 1342|回复: 0

[经验分享] 详解ORACLE ROWID之来龙去脉

[复制链接]

尚未签到

发表于 2016-8-6 10:33:27 | 显示全部楼层 |阅读模式
  rowid 从字面解释为行标识。在oracle中,通过rowid定位记录是最快和最有效的方式。那么rowid在oracle中是怎样定位记录的哪?并且它为什么是最有效的方式?带着这些问题,让我们一步一步揭开rowid的神秘面纱。
  首先,我们看一下怎样获取表中记录的rowid:通过rowid伪列
  

SQL> select rowid,id from ppp;
ROWID   ID
------------------ ----------
AAAS5VAAEAAAAemAAC    3
AAAS5VAAEAAAAemAAD    9
AAAS5VAAEAAAAemAAE    7

  

  我们知道,oracle在逻辑上有多个表空间构成,每个表空间又包含多个数据文件,数据文件对应操作系统上的文件(asm情况类似,不做考虑),而数据文件又包含若干数据块,我们的表记录就是存储在数据块中。因此,如果我们知道某条记录所存储的数据文件、数据块和在块中的偏移量,就可以非常快速的读取记录并展现给用户。rowid恰恰可以帮助我们实现这一点,因为在rowid字符串中包含了数据文件、数据块和记录在块中地址的信息。
  在8i之前,oracle采用受限的rowid(Restricted rowid),受限rowid是针对整个数据库范围的rowid,由三部分构成,即数据文件编号,块编号和记录在块内的偏移量。受限rowid占用6个字节的存储空间,其中数据文件编号占用10bit,数据块编号占用22bit,偏移量即记录在数据块中的行号占用16bit。从这里我们也可看出在8i之前的数据库中:
  每个数据库最多包含1022个文件(2个文件预留)
  每个文件最多可以有4m个数据块
  每个块最多可以存储64k条记录。
  受限的rowid在底层存储使用二进制格式,展现时采用varchar2和16进制混合的形式:BBBBBBBB.RRRR.FFFF (block#.row#.file#),如:
  

SQL> select dbms_rowid.rowid_to_restricted('AAAS5VAAEAAAAemAAE',0) from dual;
DBMS_ROWID.ROWID_T
------------------
000007A6.0004.0004


到目前为止,情况都是非常明朗的,但是随着oracle的发展,我们需要突破某些限制,例如单个数据库最多1022个数据文件的限制,同时我们还要保持数据库的向后兼容性以及为数据库的某些特性如表空间迁移做出充分的考虑。在这种情况下,仅仅只是通过扩展受限rowid的存储长度是不够的,例如我们将数据文件编号占用的存储空间从10bit扩增到20bit,虽然可以在单个数据库中存储更多的数据文件,但是却增加了向后兼容的难度(因为物理存储格式发生了变化),而且在使用表空间迁移时,如果从旧版本迁移到8i之后的版本,我们需要扫描整个迁移的表空间来修改其中存储的rowid信息,这显然与表空间迁移的初衷(通过拷贝文件和导入元数据信息来导入表空间)是相违背的。  
  为了达到以上种种目的,oracle引入了相对文件号的概念,这种方法的主要思想是改变之前rowid中数据文件编号是参考整个数据库范围i的事实,将其参考的范围改为表空间,即文件编号为4的文件不再是数据库中编号为4的数据文件,而是某个表空间中编号为4的数据文件。这样我们便可以在不改变物理存储格式的情况下(仅仅是我们在解析rowid内容时的处理逻辑发生了变化,如将前10bit解析为表空间相对文件号rfn,而不是文件号file_id,然后通过数据字典视图将),进行数据库的扩容等等。
  

SQL> select file_id,relative_fno from dba_data_files;
FILE_ID RELATIVE_FNO
---------- ------------
4      4
3      3
2      2
1      1
5      5
6      6
7      6
8      8
9      9

  
  从这里我们看到file_id和relative_fno是一一相等的,其实不然,在数据文件数量没有超过1022个时,oracle数据库尽量保持file_id和relative_fno的相同,在超过1022个数据文件后,oracle就会保证在整个数据库内file_id是唯一的,在单个表空间中relative_fno是唯一的。
  那么这时就会存在一个问题,不同表空间中的具有相同相对文件号数据文件oracle是怎样区分开来的那?为了解决这个问题,oracle在原有6byte rowid的基础上又添加了DATA_OBJECT_ID的信息,构成扩展rowid,即扩展rowid由四部分构成:data_object_id,rfn,block#,row#。通过data_object_id 和数据字典视图的结合,oracle可以非常快速的将rfn转换为file_id,从而也就可以准确的进行行定位。
  

  我们以11g中索引的存储格式为例,总结如下:
  

普通表:
--普通索引:6字节
row#0[8020] flag: ------, lock: 0, len=12
col 0; len 2; (2):  c1 02
col 1; len 6; (6):  01 00 05 e3 00 00
row#1[8008] flag: ------, lock: 0, len=12
col 0; len 2; (2):  c1 03
col 1; len 6; (6):  01 00 05 e3 00 01
row#2[7996] flag: ------, lock: 0, len=12
col 0; len 2; (2):  c1 04
col 1; len 6; (6):  01 00 05 e3 00 02
--全局分区:
row#0[8020] flag: ------, lock: 0, len=12
col 0; len 2; (2):  c1 02
col 1; len 6; (6):  01 00 05 e3 00 00
row#1[8008] flag: ------, lock: 0, len=12
col 0; len 2; (2):  c1 03
col 1; len 6; (6):  01 00 05 e3 00 01
row#2[7996] flag: ------, lock: 0, len=12
col 0; len 2; (2):  c1 04
col 1; len 6; (6):  01 00 05 e3 00 02
----- end of leaf block dump -----
分区表:
--全局分区:分区  10字节
col 0; len 2; (2):  c1 02
col 1; len 10; (10):  00 01 2e 55 01 00 07 a6 00 00
row#1[7984] flag: ------, lock: 0, len=16
col 0; len 2; (2):  c1 04
col 1; len 10; (10):  00 01 2e 55 01 00 07 a6 00 02
row#2[7968] flag: ------, lock: 0, len=16
col 0; len 2; (2):  c1 08
col 1; len 10; (10):  00 01 2e 55 01 00 07 a6 00 04
row#3[7952] flag: ------, lock: 0, len=16
col 0; len 2; (2):  c1 0a
col 1; len 10; (10):  00 01 2e 55 01 00 07 a6 00 03
---本地索引:6字节
row#0[8020] flag: ------, lock: 0, len=12
col 0; len 2; (2):  c1 04
col 1; len 6; (6):  01 00 07 a6 00 02
row#1[8008] flag: ------, lock: 0, len=12
col 0; len 2; (2):  c1 08
col 1; len 6; (6):  01 00 07 a6 00 04
row#2[7996] flag: ------, lock: 0, len=12
col 0; len 2; (2):  c1 0a
col 1; len 6; (6):  01 00 07 a6 00 03
----- end of leaf block dump -----


多数索引存储的是6byte的rowid信息(解析为rfn),通过索引定位记录时,oracle将6 byte rowid和对应表的DATA_OBJECT_ID构成扩展rowid,然后通过扩展rowid定位记录。在分区表的全局索引中,直接存储扩展rowid,是因为分区表的各个分区可能存储在不同的表空间中,而各个分区的DATA_OBJECT_ID是不同的,因此rowid中直接存储各个分区段的扩展rowid。另一方面,分区表是在8i后引入的,不存在向后兼容的要求。  
  从上面的讨论可知,如果在数据库中使用了tts,那么对于某一rowid在数据库范围内可能不是唯一的,但是在表级别肯定是唯一的,如下所示:
  

SQL> create tablespace ts5 datafile '/home/easy/oracle/oradata/easy/ts501.dbf' size 10m;
表空间已创建。
SQL> create user zy identified by zy default tablespace ts5;
用户已创建。
SQL> grant connect,resource to zy;
授权成功。
SQL> conn zy/zy
已连接。
SQL> create table test1(name varchar2(10));
表已创建。
SQL> insert into test1 values('thgdk');
已创建 1 行。
SQL> insert into test1 values('zhuuuyy');
已创建 1 行。
SQL> commit;
提交完成。
SQL> conn / as sysdba
已连接。
SQL> alter tablespace ts5 read only;
表空间已更改。
SQL> ho cp /home/easy/oracle/oradata/easy/ts501.dbf  /home/easy/oracle/oradata/easy/ts601.dbf
SQL> host expdp system/oracle dumpfile=ts5.dmp directory=tsdir transport_tablespaces=ts5
Export: Release 11.2.0.3.0 - Production on 星期六 11月 16 10:49:03 2013
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.
连接到: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
启动 "SYSTEM"."SYS_EXPORT_TRANSPORTABLE_01":  system/******** dumpfile=ts5.dmp directory=tsdir transport_tablespaces=ts5
处理对象类型 TRANSPORTABLE_EXPORT/PLUGTS_BLK
处理对象类型 TRANSPORTABLE_EXPORT/TABLE
处理对象类型 TRANSPORTABLE_EXPORT/POST_INSTANCE/PLUGTS_BLK
已成功加载/卸载了主表 "SYSTEM"."SYS_EXPORT_TRANSPORTABLE_01"
******************************************************************************
SYSTEM.SYS_EXPORT_TRANSPORTABLE_01 的转储文件集为:
/home/easy/oracle/oradata/easy/ts5.dmp
******************************************************************************
可传输表空间 TS5 所需的数据文件:
/home/easy/oracle/oradata/easy/ts501.dbf
作业 "SYSTEM"."SYS_EXPORT_TRANSPORTABLE_01" 已于 10:50:15 成功完成

SQL> alter tablespace ts5 rename to old_ts5;
表空间已更改。
SQL> create user zy_new identified by zy;
用户已创建。
SQL> grant connect,resource to zy_new;
授权成功。
SQL> host impdp system/oracle dumpfile=ts5.dmp directory=tsdir transport_datafiles=/home/easy/oracle/oradata/easy/ts601.dbf remap_schema=zy:zy_new
Import: Release 11.2.0.3.0 - Production on 星期六 11月 16 11:14:28 2013
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.
连接到: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
已成功加载/卸载了主表 "SYSTEM"."SYS_IMPORT_TRANSPORTABLE_01"
启动 "SYSTEM"."SYS_IMPORT_TRANSPORTABLE_01":  system/******** dumpfile=ts5.dmp directory=tsdir transport_datafiles=/home/easy/oracle/oradata/easy/ts601.dbf remap_schema=zy:zy_new
处理对象类型 TRANSPORTABLE_EXPORT/PLUGTS_BLK
处理对象类型 TRANSPORTABLE_EXPORT/TABLE
处理对象类型 TRANSPORTABLE_EXPORT/POST_INSTANCE/PLUGTS_BLK
作业 "SYSTEM"."SYS_IMPORT_TRANSPORTABLE_01" 已于 11:14:30 成功完成
SQL> conn zy/zy
已连接。
SQL> select rowid,name from test1;
ROWID   NAME
------------------ ----------
AAAS5uAAKAAAACEAAA thgdk
AAAS5uAAKAAAACEAAB zhuuuyy
SQL> conn zy_new/zy
已连接。
SQL> select rowid,name from test1;
ROWID   NAME
------------------ ----------
AAAS5uAAKAAAACEAAA thgdk
AAAS5uAAKAAAACEAAB zhuuuyy
SQL> conn / as sysdba
已连接。
SQL> select object_id,data_object_id from dba_objects where object_name='TEST1';
OBJECT_ID DATA_OBJECT_ID
---------- --------------
77422    77422
77467    77422
SQL> ALTER TABLESPACE OLD_TS5 READ WRITE;
表空间已更改。


  
  到现在为止,相信大家对受限rowid和扩展rowid已经非常了解了。由于平时工作中大家与扩展rowid接触的机会比较多,下面我们来总结一下扩展rowid的相关知识。
  扩展rowid占用10byte的存储空间,32bite表示DATA_OBJECT_ID,10BIT相对文件编号,22bit数据块和16bit行号。当我们通过查询语句获取rowid时,展现给我们的是18个字符构成的字符串,其中:
  数据对象编号    文件编号    块编号      行编号

OOOOOO       FFF        BBBBBB     RRR

这些字符串是10byte信息的64位编码表示

A-Z <==> 0 - 25 (26)

a-z <==> 26 - 51 (26)

0-9 <==> 52 - 61 (10)

+/ <==> 62 - 63 (2)


  依据数学知识,我们可以将rowid转换为DATA_OBJECT_ID,RFN,BLOCK#,ROW#。那么有没有更方便的方法?oracle为我们提供了dbms_rowid包,下面看具体用法:
  

SQL> select dbms_rowid.rowid_object(rowid) object_id,
2  dbms_rowid.rowid_relative_fno(rowid) file_id,
3  dbms_rowid.rowid_block_number(rowid) block_id,
4  dbms_rowid.rowid_row_number(rowid) rnum from tab2;
OBJECT_ID    FILE_IDBLOCK_ID       RNUM
---------- ---------- ---------- ----------
77168    1   91673  0
77168    1   91673  1

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-253616-1-1.html 上篇帖子: (转)Oracle完全检查点和增量检查点详解 下篇帖子: ORACLE内部函数大全以及与SQLSERVER的区别
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表