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 <tablename> ESTIMATE/COMPUTE STATISTICS;
b) SELECT blocks, empty_blocks, num_rows
FROM user_tables
WHERE table_name = <tablename>;
说明:
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块。