【IT168 服务器学院】数据库空间的有效使用和维护不仅是数据库管理的重要工作,也是大多数开发人员所关心的内容,因为它直接关系到数据库性能的发挥。同时数据碎片是经常令人头疼的问题,碎片不仅造成空间的浪费,更重要的是会直接影响应 用程序的响应速度。
Oracle提供了不少方法用于数据空间的使用、监控和维护,同时也在各版本中陆续对这方面的功能进行了增强,目的在于简化这方面工作的复杂度,提高应用的运行效率。
本文希望通过系统地介绍这方面的有关概念,让大家能更好地规划使用数据空间,正确使用Oracle提供的有关功能特性,提高应用的执行效率。
一、有关概念
数据库的空间在逻辑上分为多个表空间,每个表空间则由系统中的一个或多个物理数据文件构成;Oracle存储数据的基本单位是块,其大小在建库时由DB_BLOCK_SIZE参数确定,一个或多个连续的块构成一个区片(EXTENT),它作为数据对象存储的基本单位来使用。在Oracle中,每个基本数据对象使用的空间称为段(Segment),段存放在唯一的表空间上,每个段实际上是一系列区片(更为准确地是数据块)的集合。每个简单数据对象对应一个段;对于分区对象如分区表、索引,则每个(子)分区对应一个段,由各个(子)分区共同构成一个完整的数据对象。因此,可以把表空间看作桶,里面放着许多段;一个段只能放在一个桶中,而不能跨越多个桶。
二、表空间的使用
表空间碎片的产生
由于同一个表空间中存放有多个数据段,各个数据段可以有不同的区片尺寸,不同段的区片可以交叉存放;当这些段中的区片经过分配(如创建表)、释放(如删除表)后,就可能使表空间中原本连续的空闲数据块变成不连续,而区片必须由连续的数据块构成,这时,当某一段需要分配新的区片时,就有可能出现虽然表空间空闲数据块的总和大于所需区片的大小,但却无法找到一串连续的块来供此区片分配使用。这种情况就称为表空间的Extent Fragmentation。我们经常会遇到这种情况,明明从DBA_FREE_SPACE中计算表空间还有几百MB,但其中的某一个表却无法再扩展几个MB的空间。
消除表空间碎片
Oracle在段的区片分配上为用户提供了很大的灵活性,然而如果未能正确使用创建表空间和数据对象的各个可选择参数,则在最后将不可避免的要面对区片碎片的问题。Oracle 的Bhaskar Himatsingka 和 Juan Loaiza,为此提出了SAFE(Simple Algorithm for Fragmentation Elimination.)配制规则,通过遵循这套规则,区片级碎片可以完全的避免。而实际上,Oracle8i引入的新特性:Local Managed Tablespace就是SAFE规则在Oracle Server的内置实现。SAFE原则概括起来包括:
1. 对每个表空间上的段使用相同的区片尺寸;段参数INITIAL=NEXT,PCTINCREASE=0;可以通过使用Create Tablespace 的‘ MINIMUM EXTENT’ 子句来确保分配的区片是此参数的倍数;
2. 仅在表空间级指定INITIAL、NEXT参数,在创建数据段时不要指定这些参数;
3. 区片的大小根据段大小来确定,原则是均衡顺序扫描的效率和空间的利用率,同时确保段的区片数目控制在1024之下;根据此原则,在进行相应测试之后,确定以下区片选取规则:
段大小(Oracle7)
区片大小(Oracle7)
段大小(Oracle8)
区片大小(Oracle8)
&160M
160K
&128M
128K
160M-5G
5M
128M-4G
4M
&5G
160M
&4G
128M
有此数据库中可以只使用三种区片大小的表空间;在对象创建之前需对其大小进行评估,并放到相应的表空间中;
4. Oracle815引入了本地管理表空间,它在管理和性能上都优于传统的字典管理表空间,它已融合了规则1、2、3 ;要使用此特性,在CREATE TABLESPACE语句中指定EXTENT MANAGEMENT LOCAL子句;
5. 段的区片数目上限应在4096之下,DML操作在此区片数目范围内不会有明显的性能差异;但某些DDL操作的速度则与区片的数目关系较大;因此合理的区片数目应保持在1024之下;对于持续不断扩展的段,应监控区片数目,在必要时移至其它表空间;
6. 对于特别大的数据段应控制在4G-128G(Oracle7为5G-160G)之间,它们应存放到单独的表空间上,同时对于这些特大段应考虑使用分区拉提高性能;
7. 用户的临时表空间应使用TEMPORARY类型;
8. 当系统的事务规模比较均衡时可以对回滚段使用OPTIMAL参数,否则应避免制定OPTIMAL参数,而定期监控回滚段的大小,并在必要时重建;
9. 临时段和回滚段绝对不要将用户数据存放到SYSTEM表空间,它是专为永远不会Drop和Truncate的系统数据对象而设计的;
10. 创建表空间时指定数据文件的大小应=区片整数倍+1数据块,对于Local Managed Tablespace则为区片整数倍+64K;
11. 当表空间使用统一的区片大小时,不要对其进行空间整理,重整的结果不仅耗费精力而且可能会使性能变差;对于未使用统一的区片尺寸的表空间应通过Export/Import重整;
12. 8i 提供了Alter Table …Move [Tablespace…]命令可用于快速重整表,Alter Index …Rebuild…[Tablespace…] 命令可用于快速重建索引;
有关使用单个区片的误导
在许多关于碎片整理的文档中建议在Export时使用Compress=Y选项,将表中的所有数据调整到一个区片中,期望在Import后获得良好性能。由此让许多人产生一个观点,认为当表中数据全部存放到一个区片中时,可以获得良好性能。实际上单区片段只在以下条件成立时,才具有优越性:
数据主要以(全段)扫描方式访问;
段所对应的数据块在物理磁盘上连续存放,Oracle可以发布较大的顺序磁盘读操作;
通过对这两条进行分析可以发现,一方面数据库中大部分表是通过索引来访问,另一方面现在的数据库文件一般在物理上使用了RAID5或RAID0+1技术,数据以条带化方式分布到多个物理磁盘上,逻辑上的单个区片和多区片在物理上并无本质上的区别;另外,从Oracle的角度来看,管理几百个区片的段是非常轻松的并不会有性能的下降。由此可见将整个段放到一个区片中并无明显好处,而这种做法却会导致表空间碎片的产生。
三、表数据段的使用
表空间的组织
Heap表的空间由一系列区片链接而成,每个数据块除块头外其余部分可用于存放数据,在创建表时可以指定以下参数:
PCTFREE:块中保留用于UPDATE操作的空间百分比,当数据占用的空间达到此上限时,新的数据将不能再插入到此块中;
PCTUSED:指定块中数据使用空间的最低百分比;当一个块在达到PCTFREE,之后经历了一些DELETE操作,在其空间使用下降到PCTUSED后便可以重新被用于INSERT数据;这就是PCTFREE/PCTUSED参数的含义;
调整PCTFREE、PCTUSED参数的目标一方面是提高性能,另一方面则主要是提高空间使用效率,避免出现块中存在有许多未用的空间,但却无法找到一个块可以被用于插入新数据行的情况发生。
PCTFREE的使用
在Oracle中表的每一行数据由唯一的ROWID标记;而Oracle支持的数据类型中有一些长度是可变的,如VARCHAR,当对这些数据进行UPDATE时,如果块中的可用空间不能容纳UPDATE后的数据行时,Oracle将会把此行移到其它数据块,同时保留此数据行的ROWID不变,并在原有块中建一指针指向行迁移后的位置。在这种情况下读取一行数据将需要访问2个数据块,从而导致性能下降。PCTFREE保留的空间就是为确保更改后的数据行可以仍存放于原有数据块中,避免行迁移的情况发生。
由此,如果PCTFREE设置不足时可能产生行迁移;而另一方面如果PCTFREE设置过高,将会造成空间浪费。因此正确设置PCTFREE需要对表中数据的使用进行分析。对于数据长度不会变化或极少更新的情况,可以采用较小的PCTFREE;对于其它大多数情况应采用稍大的PCTFREE(PCTFREE的缺省值是10,如果不好估计需预留的空间,可以使用15-25的范围),不要为节约块中的空间而使用较小的PCTFREE值。
PCTUSED的使用
当块的使用的空间下降到PCTUSED后,此块被重新放回空闲链表(Freelist)中,作为后续Insert的候选块。同样,设置PCTUSED需要视数据行的特性和Insert、Update、Delete的模式而定,但必须遵守的原则是:db_block_size * (100 - PCTFREE - PCTUSED)必须比行的长度大。
对于数据行长度变化较大的情况,应使用最大行长度来计算PCTUSED,并且应使用较低的PCTUSED值。因为在执行Insert时,如果数据块的可用空间不能装下一行数据,当块的使用的空间是在PCTUSED之上,Oracle将把此块从Freelist中移走;当块的使用的空间是在PCTUSED之下,Oracle将会扩展段空间。因此,PCTUSED如果设得过高,将导致段的不断扩展。
当数据行长度不大时,使用缺省的PCTUSED(40)是比较合适的;对于行长度较大的情况,最长的行有可能会占用半个以上的块空间,此时可设置PCTUSED为10。较小的PCTUSED仅在表中的数据以随机方式被删除,而且仍有一些行长时间保留在块中时,才会造成空间使用上的问题,因为这些块可能需要较长的时间才能或永远不能重新被用于存放新数据。在这种应用中,如果空间利用率一直处于较低水平,则需对PCTUSED进行分析和调整。
总的说来,可以使用以下公式来计算PCTUSED(结果为负数时取1):
PCTUSED = 100 - PCTFREE - max(10, (maximum-row-size/ blocksize)*100);
PCTFREE、PCTUSED在各种应用场合的选取原则
以Insert为主的应用:小的PCTFREE,缺省的PCTUSED;
以Update为主的应用:根据行长度的变化范围来确定PCTFREE,PCTUSED使用缺省值;
Insert后不久Delete:采用小的PCTFREE,非常小的PCTUSED;
随机Insert/Delete:根据行大小,使用上面的建议来设置;
使用Oracle8的分区技术来提高空间使用率
Oracle8中的表分区功能可用于历史性数据的应用场合,可使用时间或其它序列编号作为分区键,当旧的历史数据被删除后,表中剩下的数据仍可保持较紧凑的存放状态。
四、B*Tree索引段的使用
Btree索引段的结构
Btree索引段使用二叉树结构来组织和存放。典型的索引树由3层结构组成:根结点块—存放指针指向分支节点数据块,分支节点块-存放指针指向叶节点,叶节点块-存放真正的索引键值和对应数据行的ROWID。然而,最简单的索引段可以只有一个块:既是根结点又是叶节点,而庞大的索引可以有4层以上的结构。与表中的一行数据可以存放到任何空闲的数据块中不同,在二叉树的数据结构中,每个键的值将唯一确定它存放的叶节点位置;因此相对于常规表的链表数据块组织形式,索引段具有更为严格的数据结构,这也是索引段结构在DML操作中更容易变形的根源。
索引数据修改的细节
a) Insert:
索引数据根据键值插入到相应叶节点中。如果叶节点空间已满,将创建新的叶节点数据块,它称为叶节点的split,此时又分为两种情况:如果键值位于索引树的最尾部,则新叶节点只存放插入的新键值,它称为99-1分裂,否则将执行叶节点的50-50分裂,即新块和旧块各存放原有50%的索引数据。
b) Delete:
删除释放的空间可用于以后相应键值插入使用。单索引块中的数据完全被删除后,此块放入空闲链表,可以用于将来索引的split,但并不马上从索引树中删除,只有当此块在索引树的其它位置被重新使用时,它才会从原有位置上删除并移到新位置上。
c) Update:
索引中没有Update的操作,对键值的修改将对应到索引中的Delete+Insert操作。
创建索引的有关参数
PCTFREE:
与表的PCTFREE不同,它只在索引创建时有效,在以后的插入操作中,此参数将不再起作用,索引数据可以充满整个数据块。因此对于在已有一定数据的表上创建索引并且索引创建后存在随机的Insert、Update操作时,指定此参数才有意义。
INITTRANS:
指定初始分配给叶节点块的事务项槽数,每个事务(transaction)需要分配一个事务槽(slot);它应等于叶节点块可能的并发事务数。当块中还有空间且事务槽不够用时,Oracle会进行动态扩展。
MAXTRANS:
指定在叶节点中允许分配事务槽的最大值;它用于防止大量突发的并发事务占用过多的叶节点空间。
索引使用上的考虑
索引在进过一段时间的DML操作后,结构和空间使用情况可能会变得不均衡。在许多情况下重建索引一方面可以提高空间利用率,更为重要的是对于是用率较高的索引还可减少对数据库缓存Buffer Pool的需求、提高程序的响应速度。但重组索引需分析数据的使用模式,因为索引的结构与些数据访问模式直接相关,在一些场合中索引会保持在某种稳定状态,尽管此时的空间使用率并不高,因此重建索引并不会有太大帮助。例如,在比较均衡的随机插入-删除模式中,索引树在经过一段时间的运行后会趋向于一种稳定状态,空间利用率会保持在66%左右。
1) 创建参数:对于随机Insert且频度不大的索引,应根据预期插入的数据量预留一定的空间,即指定合理的PCTFREE,尽量减少叶节点的Split发生;
2) 需重建索引的情况:
如果Insert的键值位于各索引子树,由于50-50分割算法,经过一段时间的Insert后,索引块的空间利用率将下降,须考虑重建索引;
对于数据插入后又会删除,既FIFO模式,但删除不彻底,此时会有许多索引块只有极少的记录,定期重建可以提高空间利用率;
对于不均衡的随机插入-删除模式,需定期考虑重组;
重建索引可以使用Alter Index …Rebuild 命令,同时指定NOLOGGING参数,然后在创建完成后用Alter Index …Logging改回正常模式;这样可以生成极少的redo log,大大提高创建速度。