什么是水线(high water mark)?
----------------------------
所有的oracle段(segments,在此,为了理解方便,建议把segment作为表的一个同义词) 都有一个在段内容纳数据的上限,我们把这个上限称为"high water mark"或hwm。这个hwm是一个标记,用来说明已经有多少没有使用的数据块分配给这个segment。hwm通常增长的幅度为一次5个数据块,原则上hwm只会增大,不会缩小,即使将表中的数据全部删除,hwm还是为原值,由于这个特点,使hwm很象一个水库的历史最高水位,这也就是hwm的原始含义,当然不能说一个水库没水了,就说该水库的历史最高水位为0。但是如果我们在表上使用了truncate命令,则该表的hwm会被重新置为0。
hwm数据库的操作有如下影响:
a) 全表扫描通常要读出直到hwm标记的所有的属于该表数据库块,即使该表中没有任何数据。
b) 即使hwm以下有空闲的数据库块,键入在插入数据时使用了append关键字,则在插入时使用hwm以上的数据块,此时hwm会自动增大。
如何知道一个表的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 extents
----------------------------- ----------------- ---------- -------
big_emp1 table 1024 2
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少个数据库块,这是因为有一个数据库块被保留用作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 extents
----------------------------- ----------------- ---------- -------
big_emp1 table 512 1
1 row selected.
注意:
truncate命令回收了由delete命令产生的空闲空间,注意该表分配的空间由原先的1024块降为512块。
为了保留由delete命令产生的空闲空间,可以使用
truncate table big_emp1 reuse storage
用此命令后,该表还会是原先的1024块。
************************************************************
oracle表段中的高水位线hwm
在oracle数据的存储中,可以把存储空间想象为一个水库,数据想象为水库中的水。水库中的水的位置有一条线叫做水位线,在oracle中,这条线被称为高水位线(high-warter mark, hwm)。在数据库表刚建立的时候,由于没有任何数据,所以这个时候水位线是空的,也就是说hwm为最低值。当插入了数据以后,高水位线就会上涨,但是这里也有一个特性,就是如果你采用delete语句删除数据的话,数据虽然被删除了,但是高水位线却没有降低,还是你刚才删除数据以前那么高的水位。也就是说,这条高水位线在日常的增删操作中只会上涨,不会下跌。
下面我们来谈一下oracle中select语句的特性。select语句会对表中的数据进行一次扫描,但是究竟扫描多少数据存储块呢,这个并不是说数据库中有多少数据,oracle就扫描这么大的数据块,而是oracle会扫描高水位线以下的数据块。现在来想象一下,如果刚才是一张刚刚建立的空表,你进行了一次select操作,那么由于高水位线hwm在最低的0位置上,所以没有数据块需要被扫描,扫描时间会极短。而如果这个时候你首先插入了一千万条数据,然后再用delete语句删除这一千万条数据。由于插入了一千万条数据,所以这个时候的高水位线就在一千万条数据这里。后来删除这一千万条数据的时候,由于delete语句不影响高水位线,所以高水位线依然在一千万条数据这里。这个时候再一次用select语句进行扫描,虽然这个时候表中没有数据,但是由于扫描是按照高水位线来的,所以需要把一千万条数据的存储空间都要扫描一次,也就是说这次扫描所需要的时间和扫描一千万条数据所需要的时间是一样多的。所以有时候有人总是经常说,怎么我的表中没有几条数据,但是还是这么慢呢,这个时候其实奥秘就是这里的高水位线了。
那有没有办法让高水位线下降呢,其实有一种比较简单的方法,那就是采用truncate语句进行删除数据。采用truncate语句删除一个表的数据的时候,类似于重新建立了表,不仅把数据都删除了,还把hwm给清空恢复为0。所以如果需要把表清空,在有可能利用truncate语句来删除数据的时候就利用truncate语句来删除表,特别是那种数据量有可能很大的临时存储表。
在手动段空间管理(manual segment space management)中,段中只有一个hwm,但是在oracle9irelease1才添加的自动段空间管理(automatic segment space management)中,又有了一个低hwm的概念出来。为什么有了hwm还又有一个低hwm呢,这个是因为自动段空间管理的特性造成的。在手段段空间管理中,当数据插入以后,如果是插入到新的数据块中,数据块就会被自动格式化等待数据访问。而在自动段空间管理中,数据插入到新的数据块以后,数据块并没有被格式化,而是在第一次在第一次访问这个数据块的时候才格式化这个块。所以我们又需要一条水位线,用来标示已经被格式化的块。这条水位线就叫做低hwm。一般来说,低hwm肯定是低于等于hwm的。
************************************************
修正oracle表的高水位线
在oracle中,执行对表的删除操作不会降低该表的高水位线。而全表扫描将始终读取一个段(extent)中所有低于高水位线标记的块。如果在执行删除操作后不降低高水位线标记,则将导致查询语句的性能低下。下面的方法都可以降低高水位线标记。
1.执行表重建指令 alter table table_name move;
(在线转移表空间alter table 。。。 move tablespace 。。。
alter table 。。。 move 后面不跟参数也行,
不跟参数表还是在原来的表空间,move后记住重建索引
如果以后还要继续向这个表增加数据,没有必要move,
只是释放出来的空间,只能这个表用,其他的表或者segment无法使用该空间
)