一、什么是水位线
所有的oracle段都会有一个在段内容纳数据的上线,把这个上限成为“high water mark”,这是一个标记,用来说明已经有多少没有使用的数据块分配给这个段,原则上high water mark只会增大,不会减小,即使delete将表中的数据全部删除,high water mark还是原值,这就使得high water mark就像水库的历史最高水位,但是如果使用truncate,该表的high water mark就会被重置成0.
二、数据库操作的影响
1)全表扫描通常要读出HWM标记的所有属于该表的数据库快,即使该表没有任何数据
2)即使HWM以下有空闲的数据库块,在插入时使用的是HWM以上的数据块,此时HWM会自动增大
三、如何知道一个表的HWM?
1)首先对表进行分析
ANALYZE TABLE <TABLENAME> ESTIMATE/COMPUTE STATISTICS
2)SELECT BLOCKS,EMPTY_BLOCKS,NUM_ROWS
FROM USER_TABLES
WHERE TABLE_NAME = <TABLENAME>
SELECT BLOCKS,--水位线
EMPTY_BLOCKS,--从来没有使用过的数据块
NUM_ROWS
FROM USER_TABLES
WHERE TABLE_NAME = 'TP_WLJR0005'
备注:blocks列代表该表中曾经使用过的数据库块的数目,即水位线
empty_blocks代表分配给该表,但是在水位线以上的数据库块,即从来没有使用过的数据块
三、oracle表段中的高水位线HWM
在oracle的存储中,高水位线在日常的增删改查中只会上涨,不会下降
1.先得明白select的查询特性:select查询时候,会对表中数据进行一次扫描,并不是说数据块有多少数据就扫描多少数据,其决定因素:高水位线以下的数据。
例:新建A表,这时高水位线是0,所以这时查询速度就极快,然后A表插入1000万条数据,这时高水位已经提到了1000W的级别,再次查询的时候,对表中数据扫描就会按照实时的高水位线以下进行扫描。如果delete把数据全部删除,但是高水位线依旧在1000W的级别,查询时候仍然会按照1000W的高水位线进行扫描。
这就是为什么有时候表中数据很少,但是查询速度很慢,其原因就是因为该表的高水位线决定的。
2.降低高水位线:使用truncate语句进行删除表中数据,相当于重新新建了表,不仅把数据清空,而且把高水位线拉低至0。
适用于数据量大的临时表
四、修正oracle的高水位线
在oracle中,执行delete操作并不会降低高水位线,导致查询性能降低。
降低高水位线的操作
1.执行表重建指令 alter table table_name move;
在线转移表空间ALTER TABLE ...MOVE TABLESPACE...,其中MOVE后面不跟着参数也可以,默认还是原来表空间,MOVE后记住重建索引。
如果以后还要继续向这个表增加数据,没有必要MOVE,只是释放出来的空间只能这个表用,其他的表或者segment无法使用该空间
2.执行alter table table_name shrink space.压缩碎片,回收高水位线。
注意:此命令为Oracle 10g新增功能,再执行该命令之前必须允许alter table table_name enable row monement;
3.赋值要保留的数据到临时表,drop原表,然后rename临时表为原表名。
4.alter table table_name deallocate unused
5.truncate
开发环境试验
选择降低高水位线的方式:shrink space
原因:move确实整理碎片的效率很高,但是不移动高水位线,而且还要重建索引;shrink space整理碎片不仅能降低高水位线,而且不需要重新建索引,但是使用shrink space之前一定要进行行迁移
move不能移动高水位线的原因:alter table move不能修改rowid,且shrink space能修改rowid,但是要先进行行迁移
1.先把碎片率高的表找出来
select 'drop table ' || segment_name || ' purge;',
sum(bytes) / 1024 / 1024 Mbytese
from user_segments a, user_tables b
where segment_type = 'TABLE'
and a.segment_name = b.TABLE_NAME
and b.COMPRESSION = 'DISABLED'
group by segment_name, COMPRESSION
order by sum(bytes) / 1024 / 1024 desc;
SELECT table_name,
ROUND((blocks * 8/1024), 2) "高水位空间 M",
ROUND((num_rows * avg_row_len / 1024/1024), 2) "真实使用空间 M",
ROUND((blocks * 10 / 100) * 8, 2) "预留空间(pctfree) M",
ROUND((blocks * 8 - (num_rows * avg_row_len / 1024) -blocks * 8 * 10 / 100), 2) "浪费空间 M",
((blocks * 8-(num_rows * avg_row_len / 1024))/1024)/(blocks * 8/1024) "浪费空间 %"
FROM user_tables
WHERE table_name = 'TP_WLJR0005';
在高水位和真实使用空间之间的差距=浪费空间,产生浪费空间的原因是高水位线的上涨,真实使用空间变小(大量的delete造成的),当浪费空间%达到25%就需要整理了。
2.开启行迁移
行迁移补充:在ORACLE中,当执行一条UPDATE 语句时候,可能会使一行的长度增加,从而其实不能放在一个数据块,这是Oracle就会自动寻找一个能容纳下该行的数据块,如果能找到,那么Oracle会把该行的数据全部搬到新的数据块中,只留下一个指针(Point)指向新的块,但是行标识(rowid)没有变化,依旧是之前的数据块。当访问这条记录时候,Oracle先会找到原来的数据块,然后通过指针指向新的数据块。
alter table TP_WLJR0005 enable row movement;--开启行迁移
3.碎片压缩
alter table table_name shrink space cascade;--进行压缩
4.关闭行迁移
alter table table_name disable row movement;--关闭行迁移
脚本:
--查看该用户下表段空间情况
select 'drop table ' || segment_name || ' purge;',
sum(bytes) / 1024 / 1024 Mbytese
from user_segments a, user_tables b
where segment_type = 'TABLE'
and a.segment_name = b.TABLE_NAME
and b.COMPRESSION = 'DISABLED'
group by segment_name, COMPRESSION
order by sum(bytes) / 1024 / 1024 desc;
--查看某个表的高水位线情况
SELECT table_name,
ROUND((blocks * 8/1024), 2) "高水位空间 M",
ROUND((num_rows * avg_row_len / 1024/1024), 2) "真实使用空间 M",
ROUND((blocks * 10 / 100) * 8, 2) "预留空间(pctfree) M",
ROUND((blocks * 8 - (num_rows * avg_row_len / 1024) -blocks * 8 * 10 / 100), 2) "浪费空间 M",
((blocks * 8-(num_rows * avg_row_len / 1024))/1024)/(blocks * 8/1024) "浪费空间 %"
FROM user_tables
WHERE table_name = 'TP_WLJR0005';
--开启行迁移
alter table tp_wljr0005 enable row movement;
--压缩碎片
ALTER TABLE TP_WLJR0005 SHRINK SPACE CASCADE;
--查看表段空间
select sum(bytes)/1024/1024 from user_segments where segment_name='TP_WLJR0005';
--收集统计信息
exec dbms_stats.gather_table_stats('TPS','TP_WLJR0005',CASCADE=>TRUE); --在toad里面执行
--关闭行迁移
alter table tp_wljr0005 disable row movement;