Oracle HWM(High Water Mark高水位线)是Oracle数据表存储结构的一个重要内容。简单的说,HWM就是存储分配的一个最高界限,标志着该数据存储对象使用空间的最高位置。
作为一个数据段segment对象,Oracle会根据系统的初始化设置和增大的空间请求,不断将各自独立的分区extent分配给segment用于存储。但是,分配的空间不一定立即使用满,HWM高水位线就是划分使用过used和未使用过unused界限的一条分界线。
本篇我们仔细分析HWM在存储中的作用,以及对Oracle数据库行为的影响。最后,我们分析一下解决HWM问题的方法。
1、HWM和存储分配
HWM高水位线是与Oracle数据段segment存储密切相关的一个基本概念。在Oracle中,存储逻辑结构是按照表空间tablespace、段对象segment、分区extent和数据块block进行分级别组织起来的。
一般我们的数据表就是对应一个段对象segment(分区表除外)。在我们创建一个数据表的时候,Oracle会创建出一个数据段对应。注意:在Oracle 11g中,提供了segment create deferred特性,不在本篇的讨论范畴。
我们使用Oracle 10gR2作为实验版本。
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 – Production
SQL> create table t tablespace users as select * from dba_objects where 1=0;
Table created
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
此时,数据表T中没有任何数据。
SQL> select segment_name, HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where segment_name='T' and wner='SYS';
SEGMENT_NA HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
---------- ----------- ------------ ---------- ---------- ----------
T 4 5203 65536 8 1
SQL> select segment_name, EXTENT_ID, FILE_ID, BLOCK_ID, BYTES, BLOCKS, RELATIVE_FNO from dba_extents where wner='SYS' and segment_name='T';
SEGMENT_NA EXTENT_ID FILE_ID BLOCK_ID BYTES BLOCKS RELATIVE_FNO
---------- ---------- ---------- ---------- ---------- ---------- ------------
T 0 4 5201 65536 8 4
对数据表T,Oracle分配了一个分区作为初始分区。该extent编号(内部)为0,位于file_id=4, block_id=5201的位置上。但是从数据段的角度看,数据表的头块是从5203开始,有两个数据块作为描述信息单独使用。注意,extent是连续存储的数据块block,该extent包括8个数据块,也就是覆盖编号从5201~5208范围。
SQL> select table_name, NUM_ROWS, BLOCKS, EMPTY_BLOCKS,AVG_SPACE from dba_tables where table_name='T' and wner='SYS';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS AVG_SPACE
---------- ---------- ---------- ------------ ----------
T 0 0 8 0
此时,如果我们向其中插入一些数据。
SQL> insert into t select * from dba_objects where object_id<10 order by object_id;
8 rows inserted
SQL> commit;
Commit complete
SQL> select dbms_rowid.rowid_relative_fno(t.rowid) fno, dbms_rowid.rowid_block_number(t.rowid) block_num, dbms_rowid.rowid_row_number(t.rowid) row_num, object_id from t;
FNO BLOCK_NUM ROW_NUM OBJECT_ID
---------- ---------- ---------- ----------
4 5205 0 2
4 5205 1 3
4 5205 2 4
4 5205 3 5
4 5205 4 6
4 5205 5 7
4 5205 6 8
4 5205 7 9
8 rows selected
从上面我们可以看到一些现象。我们已经将extent分配给了segment T,其中的8个数据块是已经属于T的范围。当我们插入8条数据的时候,发现所在的都是一个数据块(fno=4, block_num=5205)中。
SQL> analyze table t compute statistics;
Table analyzed
SQL> select table_name, NUM_ROWS, BLOCKS, EMPTY_BLOCKS,AVG_SPACE from dba_tables where table_name='T' and wner='SYS';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS AVG_SPACE
---------- ---------- ---------- ------------ ----------
T 8 5 3 7947
归纳一下,我们看到的情况,分区extent的起始位置是5201,而数据段segment的头块是5203,第一条数据插入的位置是5205。剩余所谓的3个empty_block的编号是:5206~5208。总数据的5个block,实际上是从5204~5208。但是5204用于段头信息保存(如extent分配bitmap等)。
这个时候,虽然5206~5208从隶属上已经划分给T,但是处在unused没用过的状态。数据块5205,显然是已经使用过的。HWM就在5205高度,表示当前达到的数据表最大存储使用位置。
随着数据的增加,数据块5205不能容纳的时候,就会向segment管理要求一个新的空闲块使用,此时就会分配5204~5208范围。此时,HWM就会推进到新的一个数据块中。
SQL> select distinct(dbms_rowid.rowid_relative_fno(t.rowid) ||'-'|| dbms_rowid.rowid_block_number(t.rowid)) from t;
(DBMS_ROWID.ROWID_RELATIVE_FNO
--------------------------------------------------------------------------------
4-5205
4-5206
此时,我们的数据是分布在两个数据块上,使用了两个数据块。此时,区分unused和used分界的HWM就在5206块上。
此处我们得到结论,一味的插入数据,会将更多的空间分配和使用给数据段,从而不断的推高水位线HWM。
如果我们删除数据,Oracle是否会自动将水位线下降呢?
SQL> insert into t select * from dba_objects;
50721 rows inserted
SQL> commit;
Commit complete
SQL> analyze table t compute statistics;
Table analyzed
SQL> select table_name, NUM_ROWS, BLOCKS, EMPTY_BLOCKS,AVG_SPACE from dba_tables where table_name='T' and wner='SYS';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS AVG_SPACE
---------- ---------- ---------- ------------ ----------
T 50721 748 20 1234
SQL> delete t;
50721 rows deleted
SQL> analyze table t compute statistics;
Table analyzed
SQL> select table_name, NUM_ROWS, BLOCKS, EMPTY_BLOCKS,AVG_SPACE from dba_tables where table_name='T' and wner='SYS';
TABLE_NAME NUM_ROWS BLOCKS EMPTY_BLOCKS AVG_SPACE
---------- ---------- ---------- ------------ ----------
T 0 748 20 7932
SQL> select count(*), sum(bytes)/1024/1024 mb from dba_extents where segment_name='T' and wner='SYS';
COUNT(*) MB
---------- ----------
21 6
这就意味着,当我们不断插入数据的时候,Oracle会不断分配空间extents和数据块给segment进行填充,同时不断的推高HWM到一个新的位置。
但是我们的删除delete操作,无论是有条件的删除,还是全部删除,都是不会将已经分配、已经使用过的空间回收reclaim的。空间不能回收的原因在与堆表的数据行是分散保存的,回收空间要保证该空间上没有任何数据,也就是要保证对数据行进行移动。这点对于delete操作是不可行的。
由于Oracle空间分配和存储上的特性,引起了在DML操作中,HWM一般都是只增加、不会轻易下降的现象。
2、推高HWM
下面说说堆表heap table的结构。堆表Heap Table是我们最常使用的一种数据表类型,它的特点就是存储随机,读取顺序。Heap Table的高水位线就相当一个数据框,限定了数据表存储的空间范围。
对Heap Table而言,当一般的insert操作进行时,Oracle首先会去寻找HWM下的空间有无空闲存在,如果有的话,就设法使用这些空间进行保存。这些空间是由于过去数据被删除,该物理位置就留出现空间可以使用。
如果在HWM下没有找到相应的位置,Oracle就会设法分配新的数据块使用,从而推高HWM。
在整个流程中,Oracle实际上在不断尝试寻找合适的存储。有三种方式,可以让Oracle插入数据时候会避开尝试在HWM下找空间的过程,直接推高水位线,获取较好的效率。
ü 顺序化的direct_load操作,使用hint /*+ append*/就可以在insert语句执行的时候,直接推高HWM;
ü 并行化parallel直接加载操作,对一个数据表设置并行度,采用多Slave Process处理操作;
ü SQL Load进行加载时,参数direct设置为Y,表示直接加载;
上述三种操作,都会直接推高HWM,而且是绕开SGA的Buffer Cache进行操作。
本篇中,我们简单介绍了HWM的特性。下面我们介绍HWM会给日常Oracle操作带来什么影响,以及出现HWM问题后一些常见的处理方式。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/17203031/viewspace-707432/ ,如需转载,请注明出处,否则将追究法律责任。