ROWID唯一性

在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,做了很多工作:

  1. 引入了‘relative file number’,文件标识部分对于数据库并不是唯一的,只对表空间表示唯一。这表示每个表空间会有1022个数据文件,而不是整个数据库。
  2. 对于数据库中的前1022个文件来说,relative_fno的值与file_id相同,所以这样oracle 7中的数据文件是可以兼容的:之前file_id的数值现在就变成了relative_fno。
  3. 当数据库的数据文件超过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的导入效率。

1
2
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

1
2
3
4
5
6
7
8
select owner,object_id,data_object_id
from 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是相同的。

1
2
3
4
5
6
7
select tablespace_name, file_id, relative_fno from dba_data_files
where 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也会不一样。

1
2
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
作者

xbdba

发布于

2020-10-21

更新于

2022-10-31

许可协议

评论