Oracle 高水位线详解(HWM)

HWM:高水位线,

可用空间与已用空间的分界线,标记着段空间使用情况。

所有的oracle段(segments,在此,为了理解方便,建议把segment作为表的一个同义词) 都有一个在段内容纳数据的上限,我们把这个上限称为"high water mark"或HWM。这个HWM是一个标记,用来说明已经有多少没有使用的数据块分配给这个segment。HWM通常增长的幅度为一次5个数据块,原则上HWM只会增大,不会缩小,即使将表中的数据全部删除,HWM还是为原值,由于这个特点,使HWM很象一个水库的历史最高水位,这也就是HWM的原始含义,当然不能说一个水库没水了,就说该水库的历史最高水位为0。但是如果我们在表上使用了truncate命令,则该表的HWM会被重新置为0。

HWM首先位于新创表的表中第一个块。过了一段时间,随着表中插入数据的增多,而且使用的块越来越多,HWM会升高。

如果我们删除了表中的一些行,可能就会有很多块不在包含数据,但是他们仍在HWM之下,直到重建或截除(truncate)或收缩这个对象(shrink)。

oracle在扫描段时会扫描HWM之下的所有快,即使其中不包含数据,如果HWM之下大多数是空块,这会影响扫描的性能。

假如说,100000行的表,你执行select count(*) from tab;下面在delete from tab,将表中的所有行删掉,再次执行select count(*) from tab;结果显示只有0行,但执行该语句所花的时间和统计出100000行所用的时间一样多。


HWM数据库的操作有如下影响:

a) 全表扫描通常要读出直到HWM标记的所有的属于该表数据库块,即使该表中没有任何数据。

b) 即使HWM以下有空闲的数据库块,键入在插入数据时使用了append关键字,则在插入时使用HWM以上的数据块,此时HWM会自动增大。 当使用直接路径机制加载的数据被删除后,有可能会浪费表中大量未使用空间。


跟踪检测高水位线标记下方的空间:

使用这个简单的测试,也可以检查高水位线下方是否有未使用空间。

1. set autot trace

2.  执行全表扫描的查询命令

3.  对比已处理的行号和I/O

如果已处理的行号很低,但I/O逻辑标号较高,高水位线标记下方的空间中就可能浪费了一些数据块。

使用 DBMS_SPACE软件包也检查高水位线标记下方的空间(自行查阅)


查看数据字典的分区视图:

查看DBA_extents视图也可以检查表的高水位线标记问题。

如果表拥有大量的区,但是没有数据,表明有大量的数据从表中删除:

Select count(*) from user_extents where segment_name=’INV’;

然后检查表中的行号:

Select count(*) from inv;


降低高水位线标记的方法:

1. Truncate

2. Alter table ...shrink space

3. Alter table ...move

 

1. 收缩表:

要调整高水位线,必须先为表开启行移动功能,

然后才能使用 alter table ...shrink space 语句。

表所在的表空间必须启用自动段空间管理功能。

查看是否为自动段管理模式:

Select tablespace_name,segment_space_management from dba_tablespaces


为表开启行移动功能:

Alter table inv enable row movement;

 

收缩该表使用的空间:

Alter table inv shrink space;

Alter table inv shrink space cascade;

通过cascade子句还可以收缩索引段使用的空间;


2. 移动表:

移动表是指在当前表空间中重建该表,或者在其他表空间中创建该表。

如果当前表空间出现了磁盘存储空间问题,或者需要降低表的高水位线,就需要移动表。

Alter table inv move tablespace users


查询user_tables视图可以进行验证;

Select table_name,tablespace_name from user_tables where table_name = ‘INV’;

 

在执行alter table ... move语句时,Oracle不允许执行DML语句。

 

在移动表时还可以设置nologging功能:

Alter table inv move tablespace users nologging;

通过nologging子句移动表,可以减少大量的重做日志。

缺点: 如果在移动了表后失效时间立刻出现(因而没有备份),就无法恢复表的内容。

如果表中含有关键数据,在移动表时不应使用nologging子句。

 

移动表后,rowid发生变化,所有索引都会含有不正确的信息。

需要手动重建索引,alter index ... rebuild命令。

Rowid伪劣,不是存储在数据库中的。当你查询它时,Oracle才会计算出它的值。

 

3. 临时表

CREATE TABLE table_name_1 AS SELECT  *  FROM  table_name;

TRUNCATE TABLE table_name;

INSERT INTO table_name SELECT * FROM table_name_1;

或者删除源表,将CTS新建的表进行重命名。


4. 数据泵

将数据导出再导入。


校验方法:

通过分析比对表块删除数据前后占用变化.

SELECT num_rows ,blocks, empty_blocks  FROM dba_tables WHERE owner='SDBMGR' AND table_name ='TABLE_NAME';


测试:

如何知道一个表的HWM?

a) 首先对表进行分析:

ANALYZE TABLE ESTIMATE/COMPUTE STATISTICS;

b) SELECT blocks, empty_blocks, num_rows

FROM user_tables

WHERE table_name = ;

说明:

BLOCKS 列代表该表中曾经使用过得数据库块的数目,即水线。

EMPTY_BLOCKS 代表分配给该表,但是在水线以上的数据库块,即从来没有使用的数据块。

            

让我们以一个有28672行的BIG_EMP1表为例进行说明:

1) SQL> SELECT segment_name, segment_type, blocks

FROM dba_segments

WHERE segment_name='BIG_EMP1';

SEGMENT_NAME       SEGMENT_TYPE    BLOCKS     

-----------------  --------------  --------- 

BIG_EMP1           TABLE           1024       

1 row selected.             

                

2) SQL> ANALYZE TABLE big_emp1 ESTIMATE STATISTICS;

Statement processed.

           

3) SQL> SELECT table_name,num_rows,blocks,empty_blocks

FROM user_tables

WHERE table_name='BIG_EMP1';

TABLE_NAME  NUM_ROWS  BLOCKS     EMPTY_BLOCKS

----------  --------  -------    -------------

BIG_EMP1    28672     700        323

1 row selected.

注意:

BLOCKS + EMPTY_BLOCKS (700+323=1023)比DBA_SEGMENTS.BLOCKS少1个数据库块,这是因为有一个数据库块被保留用作segment header。DBA_SEGMENTS.BLOCKS 表示分配给这个表的所有的数据库块的数目。USER_TABLES.BLOCKS表示已经使用过的数据库块的数目。

             

4) SQL> SELECT COUNT (DISTINCT

DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)||

DBMS_ROWID.ROWID_RELATIVE_FNO(rowid)) "Used"

FROM big_emp1;

Used

----------

700

1 row selected.

              

5) SQL> delete from big_emp1;

28672 rows processed.

          

6) SQL> commit;

Statement processed.

 

7) SQL> ANALYZE TABLE big_emp1 ESTIMATE STATISTICS;

Statement processed.

 

8) SQL> SELECT table_name,num_rows,blocks,empty_blocks

FROM user_tables

WHERE table_name='BIG_EMP1';

TABLE_NAME  NUM_ROWS   BLOCKS   EMPTY_BLOCKS

---------   --------   -------  ----------

BIG_EMP1    0          700      323

1 row selected.

 

9) SQL> SELECT COUNT (DISTINCT

DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)||

DBMS_ROWID.ROWID_RELATIVE_FNO(rowid)) "Used"

FROM big_emp1;

Used

----------

0 ----这表名没有任何数据库块容纳数据,即表中无数据

1 row selected.

                        

10) SQL> TRUNCATE TABLE big_emp1;

Statement processed.

                   

11) SQL> ANALYZE TABLE big_emp1 ESTIMATE STATISTICS;

Statement processed.

                   

12) SQL> SELECT table_name,num_rows,blocks,empty_blocks

2> FROM user_tables

3> WHERE table_name='BIG_EMP1';

TABLE_NAME  NUM_ROWS  BLOCKS    EMPTY_BLOCKS

----------  --------  --------  ------------

BIG_EMP1    0         0         511

1 row selected.

                               

13) SQL> SELECT segment_name,segment_type,blocks

FROM dba_segments

WHERE segment_name='BIG_EMP1';

SEGMENT_NAME  SEGMENT_TYPE  BLOCKS

------------  ------------- ------

BIG_EMP1      TABLE         512

1 row selected.

注意:

TRUNCATE命令回收了由delete命令产生的空闲空间,注意该表分配的空间由原先的1024块降为512块。

为了保留由delete命令产生的空闲空间,可以使用TRUNCATE TABLE big_emp1 REUSE STORAGE.

用此命令后,该表还会是原先的1024块。




请使用浏览器的分享功能分享到微信等