在oracle数据库中rowid是唯一的吗,或许很多人第一反应都是这么认为。这种说法其实并不准确,应该说在oracle 8以前,在整个数据库层面rowid是唯一的,而从oracle 8开始,在数据库层面rowid并不是全局唯一的,针对表空间来说,存储在其中的数据的rowid则是相对唯一的。
我们知道在数据库中存放的每一条记录都有一个ROWID ,代表着这条记录在数据库存放的物理地址。
从oracle 8开始ROWID的格式发生了变化,大小也从8字节增加到了10个字节。当你对某个表进行重组或者导出导入,则ROWID会变化。比如对于一个分区表来说,如果通过update语句将数据行从一个分区迁移到另一个分区,则其rowid会发生变化。
Oracle 7格式
在Oracle 7中ROWID由8个字节组成:
- 字节1到4 (bits 1 to 32): 数据块编号 (0-4294967295)
- 字节5到6 (bits 33 to 48): 数据块里的行编号 (0-65535)
- 字节7到8 (bits 49 to 64): 数据文件编号(0-65535)
每个字节由2个16进制的字符表示 (0-9A-F),同时每个部分由圆点隔开: BBBBBBBB.RRRR.FFFF
这个时候数据库里文件数最大为1022,当时使用已经足够,而随着业务的不断发展,所需要的文件数也越来越大,1022也明显不够用。到oracle 8时,想增加数据文件数量的同时又不想修改已有数据的rowid,因为这会牵涉到在升级过程中需要修改所有数据块的巨大工作量,所以对rowid的组成部分进行了变更。
Oracle 8格式
在Oracle 8中ROWID由10个字节组成:
- 字节1到4 (bits 1 to 32): data object id (0-4294967295)
- 字节5和半个字节6 (bits 33 to 44):表空间里的文件编号(0-4095)
- 半个字节6和字节7和8 (bits 45 to 64):文件里的块编号(0-1048575)
- 字节9和字节10 (bits 65 to 80):数据块里的行编号(0-65535)
每个字段均以基数64显示 (A-Za-z0-9+/): OOOOOOFFFBBBBBBRRR,对于大文件表空间来说,文件和块的字段组合一起来给出大文件中块的编号。
Relative file number
从oracle 8起,为了不影响已有rowid,做了很多工作:
- 引入了‘relative file number’,文件标识部分对于数据库并不是唯一的,只对表空间表示唯一。这表示每个表空间会有1022个数据文件,而不是整个数据库。
- 对于数据库中的前1022个文件来说,relative_fno的值与file_id相同,所以这样oracle 7中的数据文件是可以兼容的:之前file_id的数值现在就变成了relative_fno。
- 当数据库的数据文件超过1022时,file_id可以继续增加(现在它不储存在rowid里了),但是relative_fno则重置为1。
这样产生的结果就是rowid再也不是唯一的了,当我们不得不通过rowid去查询数据行时,我们必须得知道它处在哪个表空间下面,因为rowid的组成部分与表空间相关联。
在很早之前的版本中,因为每个表只会处于一个表空间下面。例如当表处在USERS表空间下,当通过唯一索引去查询行的rowid,那我们会知道rowid是处于USERS表空间,因为跟表一样。
然而当出现了分区表,这种情况就不再使用,每个分区都可以处在不同的表空间下面。这就是为什么要引入扩展rowid的原因,例如分区表的全局索引,比原来的rowid更大,除了给出relative file number的信息外还指出了表空间信息。它被称为扩展rowid,与原先的rowid区分开,后者仅适用于我们知道表空间的情况。
Data object id
在旧版本的oracle中只有一个object_id,因为逻辑表(table)和物理表(segment)之间是一对一的关系,这种情况在分区表出来以后发生了改变,因为一个逻辑表会有多个物理表(partition)组成。
每一个分区都是一个逻辑表,都有一个唯一的object_id,但是现在引入了一个新的data_object_id用来区分物理对象,在表的创建过程中,data_object_id是等于object_id的,然而这个值会发生变化。
当truncate表时,只需要重置表的高水位线,而不用去处理所有的数据块。然后你插入新的数据会覆盖原来的位置。但是为了避免其他正在操作的用户混淆原数据块和新的,truncate操作会改变data_object_id用来表示这是一个新的段,这个段仍与原来的逻辑表相关联。
通过分区技术可以进行交换分区:逻辑object_id发生了改变,但是物理data_object_id未发生变化,因为数据存放的仍然是同一个段内。
通常在逻辑层面上我们用到的都是object_id,比如锁表和锁分区,称作OBJECT_ID, OBJ# 或者 OBJN。而在处理物理段时,都会用到data_object_id,比如坏块、cache中的块时,会看到DATA_OBJECT_ID, DATAOBJ# 或 OBJD等。
ROWID不唯一
根据上面的介绍知道对于ROWID来说表空间是可以独立于数据库的,只需要在表空间中唯一即可。这也在通过TTS导入表空间的时候,就不需要对大量涉及到的相关索引、链表等的ROWID进行重新关联、修改对应关系等,对于导入的数据也只需要让它保持原样存放即可,这样会很大程度上提高TTS的导入效率。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 
 | SQL> create tablespace ts1 datafile size 20m;
 Tablespace created.
 
 SQL> create table t1 tablespace ts1 as select '1' id from dual;
 
 Table created.
 
 SQL> select * from t1;
 
 ID
 ---
 1
 
 SQL> select rowid from t1;
 
 ROWID
 ------------------
 AAAk0XACOAAAACDAAA
 
 SQL> alter tablespace ts1 read only;
 
 Tablespace altered.
 
 [oracle@testyum datafile]$ cp o1_mf_ts1_hrz542bl_.dbf ts2.dbf
 
 # 导出表空间
 [oracle@testyum u01]$ expdp system/oracle@pdb12c dumpfile=ts1.dmp directory=expdp_dir transport_tablespaces=ts1
 
 Connected to: Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
 Starting "SYSTEM"."SYS_EXPORT_TRANSPORTABLE_01":  system/********@pdb12c dumpfile=ts1.dmp directory=expdp_dir transport_tablespaces=ts1
 Processing object type TRANSPORTABLE_EXPORT/TABLE_STATISTICS
 Processing object type TRANSPORTABLE_EXPORT/STATISTICS/MARKER
 Processing object type TRANSPORTABLE_EXPORT/PLUGTS_BLK
 Processing object type TRANSPORTABLE_EXPORT/POST_INSTANCE/PLUGTS_BLK
 Processing object type TRANSPORTABLE_EXPORT/TABLE
 Processing object type TRANSPORTABLE_EXPORT/POST_TABLE_ACTION
 Master table "SYSTEM"."SYS_EXPORT_TRANSPORTABLE_01" successfully loaded/unloaded
 ******************************************************************************
 Dump file set for SYSTEM.SYS_EXPORT_TRANSPORTABLE_01 is:
 /u01/app/oracle/ts1.dmp
 ******************************************************************************
 Datafiles required for transportable tablespace TS1:
 /u01/app/oracle/oradata/ORA12C/86DF3648F8F79A5AE0536892A8C00070/datafile/o1_mf_ts1_hrz542bl_.dbf
 
 # 重新导入表空间
 SQL> alter tablespace ts1 rename to ts2;
 
 Tablespace altered.
 
 SQL> grant dba to test identified by test;
 
 Grant succeeded.
 
 [oracle@testyum u01]$ impdp system/oracle@pdb12c dumpfile=ts1.dmp directory=expdp_dir transport_datafiles=/u01/app/oracle/oradata/ORA12C/86DF3648F8F79A5AE0536892A8C00070/datafile/ts2.dbf remap_schema=xb:test
 
 Connected to: Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
 Master table "SYSTEM"."SYS_IMPORT_TRANSPORTABLE_01" successfully loaded/unloaded
 Starting "SYSTEM"."SYS_IMPORT_TRANSPORTABLE_01":  system/********@pdb12c dumpfile=ts1.dmp directory=expdp_dir transport_datafiles=/u01/app/oracle/oradata/ORA12C/86DF3648F8F79A5AE0536892A8C00070/datafile/ts2.dbf remap_schema=xb:test
 Processing object type TRANSPORTABLE_EXPORT/PLUGTS_BLK
 Processing object type TRANSPORTABLE_EXPORT/TABLE
 Processing object type TRANSPORTABLE_EXPORT/TABLE_STATISTICS
 Processing object type TRANSPORTABLE_EXPORT/STATISTICS/MARKER
 Processing object type TRANSPORTABLE_EXPORT/POST_TABLE_ACTION
 Processing object type TRANSPORTABLE_EXPORT/POST_INSTANCE/PLUGTS_BLK
 
 # rowid是一模一样
 SQL> select rowid from xb.t1;
 
 ROWID
 ------------------
 AAAk0XACOAAAACDAAA
 
 SQL> select rowid from test.t1;
 
 ROWID
 ------------------
 AAAk0XACOAAAACDAAA
 
 | 
通过上面这个测试可以验证rowid确实可以重复,也间接可以说明在这样的数据库环境中多个segment可以含有相同的data_object_id
| 12
 3
 4
 5
 6
 7
 8
 
 | select owner,object_id,data_object_idfrom dba_objects
 where object_name='T1';
 
 OWNER                           OBJECT_ID DATA_OBJECT_ID
 ------------------------------ ---------- --------------
 XB                                 150807         150807
 TEST                               150858         150807
 
 | 
因为新的rowid包含Relative file number,所以也可以得到他们的Relative file number是相同的。
| 12
 3
 4
 5
 6
 7
 
 | select tablespace_name, file_id, relative_fno from dba_data_fileswhere tablespace_name in('TS1','TS2');
 
 TABLESPACE_NAME         FILE_ID RELATIVE_FNO
 -------------------- ---------- ------------
 TS2                         142          142
 TS1                         143          142
 
 | 
而当对表进行ALTER TABLE MOVE操作时,为move的表创建了一个新段,这个新段的data_object_id同样会不一样,从而ROWID也会不一样。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | SQL> alter tablespace ts2 read write;
 Tablespace altered.
 
 SQL> select rowid from t1;
 
 ROWID
 ------------------
 AAAk0XACOAAAACDAAA
 
 SQL> alter table t1 move;
 
 Table altered.
 
 SQL> select rowid from t1;
 
 ROWID
 ------------------
 AAAk1NACOAAAACLAAA		---不一致
 
 select owner,object_id,data_object_id
 from dba_objects
 where object_name='T1';
 
 OWNER                           OBJECT_ID DATA_OBJECT_ID
 ------------------------------ ---------- --------------
 XB                                 150807         150861	-- data_object_id发生改变
 TEST                               150858         150807
 
 |