一、什么是水位线

所有的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;