高水位线是Oracle段对象的一个重要参数。对Oracle Segment对象而言,HWM(High Water Mark)标记着数据使用过的最高位置,有时也称为格式化过的数据位置。
当Oracle数据表进行FTS(全表扫描)操作的时候,会从段头一直检索到HWM位置。本篇中,我们一起从逻辑结构分析的角度,来研究一下HWM的确切位置。
1、环境准备
我们选择在Oracle 10g下进行试验,准备数据表T。
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
SQL> show user
User is "SYS"
SQL> create table t as select * from dba_objects;
Table created
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
2、原始HWM位置分析
根据Oracle“表-段-区-块”的层次顺序,分析一下对应数据表的情况。
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SYS' and segment_name='T';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
1 63057 6291456 768 21
对应768个数据块,包括21个分区中。
SQL> select EXTENT_ID, FILE_ID, BLOCK_ID, BYTES, BLOCKS from dba_extents where wner='SYS' and segment_name='T';
EXTENT_ID FILE_ID BLOCK_ID BYTES BLOCKS
---------- ---------- ---------- ---------- ----------
0 1 63057 65536 8
(篇幅原因,有省略……)
19 1 63881 1048576 128
20 1 64009 1048576 128
21 rows selected
Oracle对segment的空间分配,是按照一个extent一个extent进行的。当当前的extent使用完,并且没有可以使用的空间时,Oracle会给segment一个新的extent使用,分配新的extent_id编号。
HWM记录的位置是在数据段segment的段头块中。我们通过dba_segments视图,可以知道对应的段头块为fno=1,blockno=63057。我们使用dump命令,可以将其逻辑结构展现出来。
SQL> select f_get_trace_name from dual;
F_GET_TRACE_NAME
--------------------------------------------------------------------------------
D:\ADMIN\ORCL\UDUMP\orcl_ora_1984.trc
SQL> alter system dump datafile 1 block 63057;
System altered
在trace文件中,我们找到HWM位置记录和对应的Extent分配map。
Extent Header:: spare1: 0 spare2: 0 #extents: 21 #blocks: 767
last map 0x00000000 #maps: 0 offset: 4128
Highwater:: 0x0040fa42 ext#: 20 blk#: 57 ext size: 128
#blocks in seg. hdr's freelists: 0
#blocks below: 696
mapblk 0x00000000 offset: 20
Unlocked
Map Header:: next 0x00000000 #extents: 21 obj#: 56436 flag: 0x40000000
Extent Map
-----------------------------------------------------------------
0x0040f652 length: 7
0x0040f659 length: 8
(篇幅原因,省略…..)
0x0040f909 length: 128
0x0040f989 length: 128
0x0040fa09 length: 128
从extent allocation map中,我们可以看到对应的21个extents使用分配情况。段头块中记录着HWM的位置地址,其中的(Highwater:: 0x0040fa42)就表示这个具体位置。
注意:当Oracle要进行FTS的时候,首先访问的就是段头segment header块。从其中,可以获得分配的分区信息(Map Header)和Scan截止位置HWM。
下面考虑解析HWM地址。在Oracle 10g中,我们可以使用dbms_utility包方法进行解析。
SQL> select to_number('40fa42','xxxxxx') from dual;
TO_NUMBER('40FA42','XXXXXX')
----------------------------
4258370
SQL> select dbms_utility.data_block_address_file(4258370) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRES
------------------------------
1
SQL> select dbms_utility.data_block_address_block(4258370) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRES
------------------------------
64066
可以知道,对应HWM指向的位置是在文件编号为1的文件中,对应块号为64066。
那么,我们检查一下这个位置在哪里。
SQL> select EXTENT_ID, FILE_ID, BLOCK_ID, BYTES, BLOCKS from dba_extents where wner='SYS' and segment_name='T' and (block_id<=64066 and block_id+blocks-1>=64066) and file_id=1;
EXTENT_ID FILE_ID BLOCK_ID BYTES BLOCKS
---------- ---------- ---------- ---------- ----------
20 1 64009 1048576 128
对应的blockno=64066为最大extent编号下的一个数据块。我们可以看一下对应的相邻数据块中内容。
–高水位线对应数据块
SQL> select count(*) from t where dbms_rowid.rowid_relative_fno(t.rowid)=1 and dbms_rowid.rowid_block_number(t.rowid)=64066;
COUNT(*)
----------
0
-高水位线对应上一个数据块
SQL> select count(*) from t where dbms_rowid.rowid_relative_fno(t.rowid)=1 and dbms_rowid.rowid_block_number(t.rowid)=64065;
COUNT(*)
----------
1
---高水位线对应上上个数据块
SQL> select count(*) from t where dbms_rowid.rowid_relative_fno(t.rowid)=1 and dbms_rowid.rowid_block_number(t.rowid)=64064;
COUNT(*)
----------
65
---高水位线对应下一个数据块
SQL> select count(*) from t where dbms_rowid.rowid_relative_fno(t.rowid)=1 and dbms_rowid.rowid_block_number(t.rowid)=64067;
COUNT(*)
----------
0
从上面的数据,我们可以清晰的看到HWM的对应。HWM位于segment编号最大的extent中的一个数据块。在Oracle中,HWM对应的数据块是那个没有使用过的数据块。HWM上面的就是正在使用的最大数据块。
那么,我们观察一下,在添加和删除过程中,HWM的变化情况。
3、HWM变化
下面我们观察一下HWM在insert,delete和move操作时候的变化。
当进行insert操作时,如果是一个heap表的话,首先会在堆中寻找空位插入。如果没有找到,就推高HWM到一个新的块位置。
我们继续在上面实验的基础上,进行试验。
SQL> insert into t select * from dba_objects where wner='SCOTT';
25 rows inserted
SQL> commit;
Commit complete
此时,我们dump出数据块查看HWM情况。
Extent Header:: spare1: 0 spare2: 0 #extents: 21 #blocks: 767
last map 0x00000000 #maps: 0 offset: 4128
Highwater:: 0x0040fa47 ext#: 20 blk#: 62 ext size: 128
#blocks in seg. hdr's freelists: 5
#blocks below: 701
水位线位置从原来的0x0040fa42变化到0x0040fa47。真实对应的位置如下:
SQL> select to_number('40fa47','xxxxxx') from dual;
TO_NUMBER('40FA47','XXXXXX')
----------------------------
4258375
SQL> select dbms_utility.data_block_address_file(4258375) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRES
------------------------------
1
SQL> select dbms_utility.data_block_address_block(4258375) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRES
------------------------------
64071
对应从原来的64066块偏移到了64071块,移动了5个数据块。结论:确实insert操作可能会推高水位线位置。
那么,一般的delete操作呢?
SQL> delete t where wner='SCOTT';
50 rows deleted
SQL> commit;
Commit complete
SQL> alter system dump datafile 1 block 63057;
System altered
此时,HWM位置为:
Extent Control Header
-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 21 #blocks: 767
last map 0x00000000 #maps: 0 offset: 4128
Highwater:: 0x0040fa47 ext#: 20 blk#: 62 ext size: 128
#blocks in seg. hdr's freelists: 6
#blocks below: 701
mapblk 0x00000000 offset: 20
Unlocked
Map Header:: next 0x00000000 #extents: 21 obj#: 56436 flag: 0x40000000
数据HWM位置没有发生变化,说明HWM确实不会在delete的时候下降。
影响HWM下降的两个常见命令,Move和Truncate效果如何呢?
SQL> alter table t move;
Table altered
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SYS' and segment_name='T';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
1 63441 6291456 768 21
此时,头块位置已经发生改变!!
SQL> alter system dump datafile 1 block 63441;
System altered
Move操作影响到了头块位置,HWM位置必然发生变化。
-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 21 #blocks: 767
last map 0x00000000 #maps: 0 offset: 4128
Highwater:: 0x0040fd41 ext#: 20 blk#: 56 ext size: 128
#blocks in seg. hdr's freelists: 0
#blocks below: 695
mapblk 0x00000000 offset: 20
Disk Lock:: Locked by xid: 0x0006.007.000005a6
Map Header:: next 0x00000000 #extents: 21 obj#: 56512 flag: 0x40000000
最后,我们一起看一下truncate table命令。
SQL> truncate table t;
Table truncated
SQL> select HEADER_FILE, HEADER_BLOCK, BYTES, BLOCKS, EXTENTS from dba_segments where wner='SYS' and segment_name='T';
HEADER_FILE HEADER_BLOCK BYTES BLOCKS EXTENTS
----------- ------------ ---------- ---------- ----------
1 63441 65536 8 1
注意:truncated table命令是不会影响到段头块的。
我们dump出头块情况。
SQL> alter system dump datafile 1 block 63441;
System altered
Dump出的数据块结构如下:
-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 1 #blocks: 7
last map 0x00000000 #maps: 0 offset: 4128
Highwater:: 0x0040f7d2 ext#: 0 blk#: 0 ext size: 7
#blocks in seg. hdr's freelists: 0
#blocks below: 0
mapblk 0x00000000 offset: 0
Disk Lock:: Locked by xid: 0x0008.001.0000058b
Map Header:: next 0x00000000 #extents: 1 obj#: 56513 flag: 0x40000000
Extent Map
-----------------------------------------------------------------
0x0040f7d2 length: 7
nfl = 1, nfb = 1 typ = 1 nxf = 0 ccnt = 0
SEG LST:: flg: UNUSED lhd: 0x00000000 ltl: 0x00000000
水位线下降到什么位置呢?我们计算一下这个:0x0040f7d2。
SQL> select to_number('40f7d2','xxxxxx') from dual;
TO_NUMBER('40F7D2','XXXXXX')
----------------------------
4257746
SQL> select dbms_utility.data_block_address_block(4257746) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRES
------------------------------
63442
SQL> select dbms_utility.data_block_address_file(4257746) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRES
------------------------------
1
注意:这里的HWM指向块号为63442。我们的数据段头块为63441。说明:在truncate table的时候,HWM要重置到头块后面的第一个数据块上。
4、结论
经过上面的讨论,我们已经可以清晰的看到Oracle HWM的位置和工作原则。对segment对象而言,HWM的作用十分重要,需要我们深刻理解。
原创文章,转载请注明: 转载自Hello,Oracle
本文链接地址: 高水位线HWM到底在哪儿?
文章的脚注信息由WordPress的wp-posturl插件自动生成