这是一张非常基本同时也是非常重要的结构图。物理结构很简单,就是操作系统物理块组成数据文件,对于操作系统来说,数据文件跟其他非数据库文件没有区别。重点是逻辑结构。
一、表空间
从9i开始,oracle支持不同块大小的表空间,也就是说同一个数据库中,不同表空间的数据块大小可以不一样。假设默认8k的话,还可以创建比如4k、16k、32k、64k等大小的表空间。不过如果有不同块大小的表空间,请记得为它们设置不同的db_nk_cache_size参数,以为这些表空间的数据访问分配内存。
从10g开始,oracle支持重命名表空间,语法是alter tablespace
rename to
;这个操作在传输表空间时非常有用,当源和目标的表空间名称不一致时,通过此命令可以方便地更名,这是一大进步。
二、段(segment)
参考上图,一个段可以跨越多个数据文件,但只能在一个表空间中。一个段由多个区(extent)组成。每一个段都有一个单独的data_object_id对应。这里需要额外说明一下data_object_id和object_id的区别。简单的说,object_id对应的是对象,而data_object_id对应的是段。比如一个procedure有object_id,但没有data_object_id,因为它不是段。因为每一个段都唯一对应一个表空间,而每一个段都拥有自己独一无二的data_object_id,所以通过data_object_id,就能唯一定位这个段所在的表空间,这就是rowid的原理。
这里做一个实验,来说明data_object_id和object_id的区别以及一些操作对data_object_id的影响。首先创建一张分区表
SQL>create table part_test(id number,create_time
date)
partition
by range (id)
(partition
part1 values less than (100),
partition part2 values less than (200),
partition
part3 values less than (maxvalue));
看一下没有数据时的情况
SQL>select
object_name,subobject_name,object_type,object_id,data_object_id
from user_objects where object_name = 'PART_TEST';
OBJECT_NAME SUBOBJECT_NAME OBJECT_TYPE OBJECT_ID DATA_OBJECT_ID
-------------------- -------------------- -------------------
---------- --------------
PART_TEST TABLE 73205
PART_TEST PART1 TABLE
PARTITION 73206 73206
PART_TEST PART2 TABLE
PARTITION 73207 73207
PART_TEST PART3 TABLE
PARTITION 73208 73208
可以看出,初始没有数据的时候,data_object_id和object_id一样。考虑到data_object_id是记录了数据存放的位置,我们看一下先插入数据,再将表truncate之后的情况。
SQL> insert into part_test values(1,sysdate);
1 row created.
SQL> insert into part_test
values(101,sysdate+1);
1 row created.
SQL> commit;
Commit complete.
SQL> truncate table part_test;
Table truncated.
SQL> select
object_name,subobject_name,object_type,object_id,data_object_id
from user_objects where object_name = 'PART_TEST';
OBJECT_NAME SUBOBJECT_NAME OBJECT_TYPE OBJECT_ID DATA_OBJECT_ID
-------------------- -------------------- -------------------
---------- --------------
PART_TEST PART3 TABLE
PARTITION 73208 73208
PART_TEST PART2 TABLE
PARTITION 73207 73211
PART_TEST PART1 TABLE
PARTITION 73206 73210
PART_TEST TABLE 73205
可以看出,有数据的段,对应的data_object_id改变了,这是因为truncate之后,oracle重新分配了这些段存储数据的空间,因此改变了data_object_id。而没有数据的段,data_object_id没有改变,因为oracle并没有对这些段重新分配数据存储空间。
三、区(extent)
一个区由连续的块组成。这里要强调的就是连续。因此,一个区是不能跨数据文件的(参见上图跟数据文件相连的那条线)
四、块(block)
这是oracle存取数据最小的单位,在建立数据库的时候指定,一旦指定则无法修改。(但在9i之后可以创建不同块大小的表空间,一定程度上解决了这个问题。)
一个块除了了块头、表目录和行目录之外的空间,都可以用来存放数据。按照默认8k大小的块来说,这部分可以真正存放数据的空间是8096字节。然后因为各种其他开销,每行数据的最小长度大约是11个字节,也就是说,每个块最大存放的行数理论值是8096/11=736行。我们可以做个试验来验证一下。
SQL> create table block_test(a varchar2(1)) pctfree
0 tablespace datatb;
Table created.
SQL> select object_id from user_objects where
object_name = 'BLOCK_TEST';
OBJECT_ID
----------
73213
SQL> select spare1 from sys.tab$ where
OBJ#=73213;
SPARE1
----------
736
可以看到,当我们创建了一个pctfree=0(表示不预留空间,块中所有可以存放数据的空间都用来存放数据)的表时,最大存放行数理论值为736,跟我们的计算一致。那么实际可以存放多少呢?我们继续做下去。
SQL> begin
2 for i in 1 .. 10000
loop
3 insert into block_test
values('1');
4 end loop
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
使用一个循环,向表中插入10000条数据,每条数据只有一个字节。
SQL> select file#,block#,count(*)
2 from
(select dbms_rowid.rowid_relative_fno(rowid) file#,
3 dbms_rowid.rowid_block_number(rowid) block#
4 from block_test)
5 group by
file#,block#;
然后我们通过上面的语句来看这些数据的分布
FILE# BLOCK# COUNT(*)
---------- ---------- ----------
4 209 733
4 212 733
4 203 733
4 214 733
4 206 733
4 215 733
4 205 733
4 208 733
4 210 733
4