Oracle内核技术揭密_吕海波 学习笔记
oracle 10g中初始会为新建的表分配至少一个区,11.2.0.3版本之后不为新建表分配区,只有插入一条数据后才分配空间。
如下代码中,没有任何权限的ma账号,可以建表并指定表空间为tp,就说明此时是没有为表分配空间的,因为ma账号没有使用tp表空间的权限。当为ma账号授权并插入一条数据后,dba_extents中有数据了,说明这时是分配了空间的。
SQL> create user ma identified by ma;
User created.
SQL> create table ma.t60(id number(5),name varchar2(10)) tablespace tp;
Table created.
SQL> select extent_id,file_id,block_id,blocks from dba_extents where segment_name='T60' order by extent_id;
no rows selected
SQL> insert into ma.t60 values(1,'a1');
insert into ma.t60 values(1,'a1')
*
ERROR at line 1:
ORA-01950: no privileges on tablespace 'TP'
SQL> grant unlimited tablespace to ma;
Grant succeeded.
SQL> insert into ma.t60 values(1,'a1');
1 row created.
SQL> select extent_id,file_id,block_id,blocks from dba_extents where segment_name='T60' order by extent_id;
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------- ---------- ---------- ----------
0 4 327616 8
区是空间分配的基本单位,它分为统一区大小表空间和系统管理区大小表空间。
统一区大小表空间和区的使用规则:
创建一个数据文件大小为32M,区大小为1M的表空间,在此表空间中新建一张表,并插入一笔资料。可以看到这一笔资料放在7号数据文件的第128号到255号数据块中,共分配了128个数据块,每个块大小是8KB,128个块大小刚好是1MB,这是一个区的大小。
SQL> CREATE TABLESPACE TBS DATAFILE '/data1/oradata/ora1/tbs01.dbf' SIZE 32M uniform size 1M;
Tablespace created.
SQL> create table ma.t61(id number(5),name varchar2(10)) tablespace tbs;
Table created.
SQL> insert into ma.t61 values(1,'a1');
1 row created.
SQL> select extent_id,file_id,block_id,blocks from dba_extents where segment_name='T61' order by extent_id;
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------- ---------- ---------- ----------
0 7 128 128
从上面代码可以看到第一个区是从128号块开始分配的,原因在于从11gR2开始,0-127号块被oracle留作内内部使用了,用于标记区的分配情况。0-1号块是文件头,2-127号块是位图块。其中第一个位图块,也就是2号块是位图段头,从3号块开如才是真正的位图数据。3-127号块共125个块,每个块8KB,共1000K字节,每字节有8位,共8000K字节,也就是可以记录8000K个区的分配情况。
在分配这些块时,oracle内部有一个标记位,0-2号块被使用了,标记位就先标记3号位,如果3-4号位又被占用了,就标记5号位;如果3号位现在被释放了,标记位会再标记3号位,当有请求分配数据块时,标记位就从当前位置向后查找并分配。
当开启了闪回drop时,如果drop了表,区是不会被释放的,标记位也不会下降,因为此时的drop只是改名,并没有真正删表。
系统管理区大小:
如下代码中,创建一个1G的表空间TBS2,在它上面建一张表t62,插入一笔数据后可以看到第一个区的大小为8个块,也就是64K。用insert into ... select语句往表中多次插入数据后,16号区的大小发生了变化,变为128个块,也就是1M的大小。如果数据继续增大,区大小将变为8M,后面还会增大。
SQL> CREATE TABLESPACE TBS2 DATAFILE '/data1/oradata/ora1/tbs201.dbf' SIZE 1G;
SQL> create table ma.t62(id number(5),name varchar2(10)) tablespace tbs2;
SQL> insert into ma.t62 values(1,'aa1');
SQL> commit;
SQL> select extent_id,file_id,block_id,blocks from dba_extents where segment_name='T62' order by extent_id;
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------- ---------- ---------- ----------
0 8 128 8
SQL> insert into ma.t62 select * from ma.t62;
65536 rows created.
SQL> commit;
SQL> select extent_id,file_id,block_id,blocks from dba_extents where segment_name='T62' order by extent_id;
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------- ---------- ---------- ----------
0 8 128 8
1 8 136 8
2 8 144 8
3 8 152 8
4 8 160 8
5 8 168 8
6 8 176 8
7 8 184 8
8 8 192 8
9 8 200 8
10 8 208 8
11 8 216 8
12 8 224 8
13 8 232 8
14 8 240 8
15 8 248 8
16 8 256 128
从上面的例子可知,表中只要有一笔数据进来,就会为这笔数据分配一个区的大小,因此小区比大区的空间利用率更高;但是当全表扫描时,大区可以减少磁头在区间的定位,可以提高性能。当然,对于随机访问,性能上就没有那么大的区别。
在使用统一区大小时,当段的大小超过64M,可以将区大小设为8M;很多系统一次I/O的最大读写数据量为1M,在一个8M的区里还是需要8次I/O,这时超过1M的区并不能减少I/O次数。当一次需要读取2M数据时,这2M数据又同时在一个8M的区里,这时虽然需要2次I/O,但是这是连续I/O;如果区大小只有1M,那么存这2M数据的区可能不连续,虽然也是2次I/O,但却可能是随机I/O,连续I/O性能要高于随机I/O性能。因此在使用统一区大小时,可以使用稍一点的区大小。
系统管理的区,在做位图标记时,以64K为准,也就是每一个位标记8个数据块的使用情况,如要分配1M的空间,需要修改16个标记位。
在统一区大小表空间中,由于每个区大小一样,被删掉的区可以重复使用,也就不存在碎片问题。但在系统管理区大小的表空间中,有很多64K的区,互相不连续,当分配1M这样的更大的区时,那些不连续的64K区无法被重用,这样就会存在碎片。