日期:2014-05-16  浏览次数:20502 次

Oracle磁盘碎片处理
在数据库创建之后,磁盘的空间是连续的,但是随着对数据的DML操作,在数据库的数据块中就会出现一些磁盘碎片。磁盘碎片会影响磁盘I/O操作,浪费磁盘空间,就拿链化现象来说吧,通常链化行都会跨越不止一个数据块,所以当读取链化行时,所需要的磁盘I/O就需要读取不止一个数据块,从而增大了磁盘I/O操作,影响数据库性能。
对于数据库的磁盘碎片处理,可以总结为三个处理阶段:预防、监控、清理。

第一阶段:预防
对于磁盘碎片的预防阶段,主要是在对数据库、表空间或表的设计是所作的一些操作。对于表空间的设计,在设计时,如果可以预测表空间的段将会增大到多大的尺寸,那么最好使用同一的盘区分配,而不是使用自动的盘区分配。并且在确定数据库块大小的时候,将数据库块大小指定为操作系统块大小的倍数,这有利于磁盘的I/O操作,同时指定表空间的盘区大小为数据库块大小的倍数,这样在盘区中就可以存放刚好的整数个数据库块,而不会因为盘区剩余空间的大小不能放下一个数据库块而纠结。最好是使用ASSM来管理表空间,因为这可以减少管理员的工作,而且这也可以减少空闲列表的使用而带来的性能影响,比如空闲列表的争用。
而且,在指定表空间盘区大小的时候,如果可以的话,先预测一下段的大小,对于盘区过多的段,可以将盘区尺寸设计的大一点以适合于段,一个太多盘区的段的性能要比一个有适当盘区大小和盘区数量的段的性能要低。
除此之外,链化现象也会导致磁盘碎片,链化现象的出现是因为磁盘的数据块中没有足够的空间用以存储DML操作对表中行的更改,当在磁盘数据块中的空闲空间不足时,被更改的行就会链接到另外的数据块以存储信息,就这样,链化行就产生了。链化行会极大的降低数据库的性能,降低磁盘I/O性能,从未降低了对数据的查询性能,而且带了了磁盘碎片。要预防链化带来的磁盘碎片,那么,在设计表的时候,就应该正确的预测数据块的空闲空间,以适应数据块中的行的更改或增长,在默认情况下空闲空间是数据块的10%,但是,如果表的DML操作非常频繁,那么或许就需要更大的空闲空间了,但是如果是只读块,则需要更少的空闲空间。

第二阶段:监控
就算在磁盘碎片的预防阶段做的多么的好,我想谁也不能绝对的保证你的磁盘就不会出现或极少出现磁盘碎片吧,就像有些DBA所担忧的一样,谁也不想在梦中做着白马王子的时候,现实中的灾难却悄悄降临。
所以定时的监控是很有必要的,虽然我没有尝试过半夜接到客户投诉的电话,但是听起来那是一件糟糕的事情。
对表空间的碎片的监控可以用自由空间碎片索引值来显示,也就是通常所说的FSFI值:
FSFI=100*SQRT(max(extent)/sum (extents))*1/SQRT(SQRT(count(extents)))
通过查找就算FSFI值就可以知道碎片的产生。而且对于链化现象,可以通过查看statspack报表或awr报表的fetch by continued row的值,或者是通过查看v$sysstat的table fetch continued row的值,如果有链化行就会有值。
SQL:select tablespace_name,sqrt(max(blocks)/sum(blocks))*
(100/sqrt(sqrt(count(blocks)))) FSFI
from dba_free_space
group by tablespace_name order by 1;
--value值越小越好
select * from  v$sysstat where NAME = 'table fetch continued row';
统计出了数据库的FSFI值,就可以把它作为一个可比参数。在一个有着足够有效自由空间,且FSFI值超过30的表空间中,很少会遇见有效自由空间的问题。当一个空间将要接近可比参数时,就需要做碎片整理了。



第三阶段:清理
如果在磁盘中出现了严重的磁盘碎片化现象,那么接下来的工作应该是处理磁盘碎片,从而挺高磁盘I/O性能和节省空间。
在有一天我发现我的磁盘出现了大量的磁盘碎片,但是当进一步查看是发现出现磁盘碎片的是其中的一张表。这时我就可以针对这一张表来处理磁盘碎片,首先我选择的方法是使用CTAS方法来创建一张表,原来的表是book,所以可以使
用CTAS先创建一张表book1:
Create table book1
As
Select * from book
Nologging;
使用nologging子句可以在创建book1是不产生日志信息,从而可以加快创建速度。创建好book1后接下来就是删除book表,删除之后再重名民book1为book:
Alter table rename book1 to book;
除了使用CTAS方法,可以用另外一种方法,就是使用expdp、impdp导入/导出表的方法,而且这样好像比使用CTAS方法要更快一点,但是必须保证在导出完表然后删除数据库中的表之后、导入表之前,导出文件是健康的。
但是在我的另外一次发现中,我发现磁盘碎片并不是在某个表的数据块上,而是在表空间中的很多数据文件的磁盘中都有挺多的磁盘碎片,所以这时就需要对整个表空间进行碎片处理,我使用的方法是使用expdp、impdp数据泵进行表空间的导出/导入。
此方法为四步骤:导出表空间、删除表空间、创建表空间、导入表空间
第一步:使用expdp将表空间导出:
Expdp bookinfo/password
datafile=dir1:tbs_book_01.dmp
logfile=dir2:tbs_book_exp_logfile_01.log
tablespaces=tbs_book
第二步:删除被导出的表空间
(如果表空间中存储了其他表空间的表分区,需
先将分区移动打其他表空间。而且不能删除默
认的表空间)
Drop tablespace tbs_book including
contentsand datafiles;
第三步:创建一个同名的表空间
Create tablespace tbs_book
Datafile ‘/u01/oradata/db1/tbs_book_01.
dbf’ size 45M autoextend on
next 5M maxsize 200M
Extent management local
Segment space management auto
Blocksize 8k;
第四步:导入表空间
Impdp bookinfo/password
datafile=dir1:tbs_book_01.dmp
logfile=dir2:tbs_book_imp_logfile_01.log
tablespaces=tbs_book
如果这一切对你来说都还不够,其实重建数据库也是一种方法,不过这将耗费大量的时间和系统性能,而且不会带来太多的性能改善,据说很有风险,但至少我没有试过,尽管是在实验环境上。


4、自由范围的碎片整理

(1)表空间的pctincrease值为非0
可以将表空间的缺省存储参数pctincrease改为非0。一般将其设为1,如:
alter tablespace temp
default storage(pctincrease 1);
这样SMON便会将自由范围自动合并。也可以手工合并自由范围:
alter tablespace temp coalesce;

5、段的碎片整理
我们知道,段由范围组成。在有些情况下,有必要对段的碎片进行整理。要查看段的有关信息,可查看数据字典dba_segments,范围的信息可查看数据字典dba_extents。如果段的碎片过多, 将其数据压缩到一个范围的最简单方法便是用正确的存储参数将这个段重建,然后将旧表中的数据插入到新表,同时删除旧表。这个过程可以用Import/Export(输入/输出)工具来完成。

Export()命令有一个(压缩)标志,这个标志在读表时会引发Export确定该表所分配的物理空间量,它会向输出转储文件写入一个新的初始化存储参数--等于全部所分配空间。若这个表关闭, 则使用Import()工具重新生成。这样,它的数据会放入一个新的、较大的初始段中。例如:

exp user/password file=exp.dmp compress=Y grants=Y indexes=Y
tables=(table1,table2);

若输出成功,则从库中删除已输出的表,然后从输出转储文件中输入表: