从图5-2中可以看出,Oracle通过表空间为数据库提供使用空间,那么Oracle必然提供一种技术用于空间管理。
Oracle主要的空间管理方式有两种,一种是Oracle 8i以前的字典管理表空间(DMT)技术,一种就是Oracle 8i以后的本地管理表空间(LMT)技术。由于区间(Extent)是Oracle创建对象时的最小分配单元,所以表空间的管理实际上就是针对于区间的管理。
字典管理表空间
在Oracle 8i以前(不包括8i),只存在一种表空间的管理模式,这就是字典管理表空间(Dictionary Managed Tablespace,简称DMT)。之前创建表空间的主要语法为:
CREATE TABLESPACE tablespace_name DATAFILE 'datafile_path_name' [SIZE INTETER [K|M] |[DEFAULT STORAGE]|[PERMANENT|TEMPORARY]]
这里关键字DEFAULT STORAGE指明了该表空间的默认存储格式,包含了INITIAL、NEXT、PCTINCREASE等相关参数的设置。如果在表空间中创建对象(如表、索引等)时不指定存储参数的话,Oracle将采用表空间的存储参数作为对象的默认参数;PERMANENT|TEMPORARY指明了该表空间的类型是永久的还是临时的。
当表空间被创建之后,可以通过查询dba_tablespaces视图来确定表空间的类型:
SQL> select tablespace_name, extent_management,allocation_type 2 from dba_tablespaces; TABLESPACE_NAME EXTENT_MAN ALLOCATIO ------------------------------ ---------- --------- SYSTEM DICTIONARY USER RBS DICTIONARY USER TEMP DICTIONARY USER USERS LOCAL UNIFORM 4 rows selected.
EXTENT_MANAGEMENT显示为DICTIONARY的就是字典管理表空间。所谓字典管理表空间是指,当创建或删除对象时,Oracle的空间分配或回收是通过数据库中的数据字典表来记录和管理的,用于管理的两个数据字典表分别是:UET$(used extents-已使用的空间)和FET$(free extents-空闲空间)。
可以来简单看一下这两个字典表的结构:
SQL> desc uet$ 名称 空? 类型 ----------------------------------------- -------- ---------------------------- SEGFILE# NOT NULL NUMBER SEGBLOCK# NOT NULL NUMBER EXT# NOT NULL NUMBER TS# NOT NULL NUMBER FILE# NOT NULL NUMBER BLOCK# NOT NULL NUMBER LENGTH NOT NULL NUMBER SQL> desc fet$ 名称 空? 类型 ----------------------------------------- -------- ---------------------------- TS# NOT NULL NUMBER FILE# NOT NULL NUMBER BLOCK# NOT NULL NUMBER LENGTH NOT NULL NUMBER
可以清晰地看到,这两个字典表基本上是通过文件号(file#)、数据块号(block#)等信息来管理空间的分配和回收的。
字典管理表空间的工作方式是——当建立一个新的段或者段在表空间中请求新的空间时,Oracle通过执行一系列的SQL语句来完成这个工作,这些工作包括从FET$中找到可用的自由空间,移动或增加相应的行到UET$中,并在FET$中删除相应的记录;当删除一个段的时候,Oracle则移动UET$中相应的行到FET$。
这个过程的发生是连续的、串行的,在繁忙的数据库中,这类操作极可能导致竞争和等待,产生数据字典的争用;另一方面,当数据字典的表的信息被修改时,系统同样要记录undo和redo信息,频繁的修改又不可避免地对整个数据库的性能产生影响。
在繁忙的数据库中,字典管理的表空间可能会成为灾难,下面展示一段Statspack报告,在这个采样周期为1小时的报告中:
DB Name DB Id Instance Inst Num Release OPS Host ------------ ----------- ------------ -------- ----------- --- ------------ TEAM 2757346 ss7 1 8.1.7.4.0 NO db-server Snap Id Snap Time Sessions ------- ------------------ -------- Begin Snap: 2716 28-10月-03 08:15:03 20 End Snap: 2718 28-10月-03 09:15:31 20 Elapsed: 60.47 (mins)
注意到在根据Buffer Gets排序的Top SQL中,有大量的SQL是在进行字典表的空间维护操作,这已经成为了这个数据库的性能瓶颈,下面报告中的SQL语句很好地展示了字典管理表空间的空间维护方式:
Buffer Gets Executions Gets per Exec % Total Hash Value --------------- ------------ -------------- ------- ------------ 4,800,073 10,268 467.5 51.0 2913840444 select length from fet$ where file#=:1 and block#=:2 and ts#=:3 803,187 10,223 78.6 8.5 528349613 delete from uet$ where ts#=:1 and segfile#=:2 and segblock#=:3 a nd ext#=:4 454,444 10,300 44.1 4.8 1839874543 select file#,block#,length from uet$ where ts#=:1 and segfile#=: 2 and segblock#=:3 and ext#=:4 23,110 10,230 2.3 0.2 3230982141 insert into fet$ (file#,block#,ts#,length) values (:1,:2,:3,:4) 21,201 347 61.1 0.2 1705880752 select file# from file$ where ts#=:1 9,505 12 792.1 0.1 1714733582 select f.file#, f.block#, f.ts#, f.length from fet$ f, ts$ t whe re t.ts#=f.ts# and t.dflextpct!=0 and t.bitmapped=0 6,426 235 27.3 0.1 1877781575 delete from fet$ where file#=:1 and block#=:2 and ts#=:3 1,267 37 34.2 0.0 904892542 select file#,block#,length from fet$ where length>=:1 and ts#=:2 and file#=:3 901 235 3.8 0.0 3687396716 insert into uet$ (segfile#,segblock#,ext#,ts#,file#,block#,lengt h)values (:1, :2, :3, :4, :5, :6, :7)
字典管理表空间面临的另外一个严重问题是:空间碎片。
在字典管理表空间中,我们经常会遇到这样的悖论,一个表空间中的自由空间(可以通过dba_free_space视图来计算)还有几百MB,但是一个请求几百KB空间的表可能都已经无法扩展。这是因为表空间中的自由空间并不连续,每块自由空间都很小,已经不能满足一个表的扩展需要,这在Oracle 8的年代常常成为困扰DBA的难题。
在字典管理表空间中,当要满足一个空间要求时,数据库要寻找表空间中的自由空间来使用,如果自由空间大于请求空间,那么自由空间就将被细分,这样将逐渐形成越来越多的离散的、分隔的、小块的自由空间,即空间碎片。
随着时间推移,应用系统的广泛使用,数据中所产生的碎片也会越来越多,这些碎片将对数据库产生以下几点主要影响:
· 导致系统性能衰减,如上所述,当要满足一个空间扩展要求时,数据库将首先查找当前最大的自由空间,经过分割,自由空间将逐渐变小,要找到一个满足扩展需求的自由空间已经变得越来越难,从而导致空间分配中的速度问 题。
· 大量空间被浪费,尽管有一部分自由空间(如表空间的PCTINCREASE设置为非0值)将会被SMON(系统监控)后台进程周期性地合并,但始终有一部分自由空间无法得以自动合并,从而浪费了大量的宝贵空间,并且非0的PCTINCREASE更容易导致更多的碎片。
· 空间释放成为难题,在DMT管理的表空间中,你也许听说过DROP一个大表或删除一个表空间往往要花费数十小时甚至几天的时间,同时会导致严重的性能问题。这是因为当需要删除一个表或表空间时,Oracle同样要维护其空间分配,这样fet$和uet$表同样要被维护,这样的维护耗时将超乎想象。
这里简单地引用一个维护字典管理表空间的案例,以便给大家一些直观的印象。在以下的案例中,我们需要删除一个字典管理的表空间,在删除表空间时,Oracle需要递归地删除表空间里所有的Extent,包括使用的和未使用的。下面以fet$为例,ts#=3的是目标表空间,在fet$中存在44万左右的Extents:
SQL>select ts#,count(*) from fet$ group by ts#; TS# COUNT(*) ---------- ---------- 0 817 1 12160 3 440932 4 15775 5 5300 6 314997 6 rows selected.
在DROP表空间时,可以反复查询这个视图,根据Extent减少的速度来评估Oracle完成删除所需要的时间。在这个案例中,Oracle每秒仅能清除3个Extent。
那么完成这个操作大约需要40多个小时,可以看到,这个时间消耗是相当惊人的:
SQL> select 440932/3/3600 from dual; 440932/3/3600 ------------- 40.827037
最终Oracle认识到字典表空间已经成为影响数据库性能的严重问题,于是Oracle在Oracle 8i中开始引入了本地管理表空间(Local Management Tablespace)的技术。