Oracle中HWM与数据库性能的探讨

一、什么是高水位

HWM(high water mark),高水标记,这个概念在segment的存储内容中是比较重要的.简单来说,HWM就是一个segment中已使用和未使用的block的分界线.

在oracle的concept中对于HWM的说明是这样的:在一个segment中,HWM是使用和未使用空间的分界线。HWM在插入数据时,当现有空间不足而进行空间的扩展时会向上移,但删除数据时不会往下移。这就好比是水库的水位,当涨水时,水位往上移,当水退出后,最高水位的痕迹还是清淅可见.考虑让我们看一个段,如一张表,其中填满了块,如图 1 所示。在正常操作过程中,删除了一些行,如图 2 所示。现有就有了许多浪费的空间:(I) 在表的上一个末端和现有的块之间,以及 (II) 在块内部,其中还有一些没有删除的行。

图1:分配给该表的块。用灰色正方形表示行

ORACLE 不会释放空间以供其他对象使用,有一条简单的理由:由于空间是为新插入的行保留的,并且要适应现有行的增长。被占用的最高空间称为最高使用标记 (HWM),如图 2 所示。

图2:行后面的块已经删除了;HWM 仍保持不变

HWM本身的信息是储存在段头.在段空间是手工管理方式时,ORACLE是通过FREELIST(一个单向链表)来管理段内的空间分配.在段空间是自动管理方式时(ASSM),ORACLE是通过BITMAP来管理段内的空间分配.

本文的实验环境windows2003,oracle 10.2.0.1.0,bolcksize 8K。

二、HWM对性能的影响

这里我们要先引入一个procedure(转自tom的《oracle高级专家编程》):

create or replace procedure show_space

( p_segname in varchar2,

p_owner  in varchar2 default user,

p_type   in varchar2 default 'TABLE',

p_partition in varchar2 default NULL )

as

l_total_blocks             number;

l_total_bytes              number;

l_unused_blocks            number;

l_unused_bytes             number;

l_LastUsedExtFileId        number;

l_LastUsedExtBlockId       number;

l_last_used_block          number;

procedure p( p_label in varchar2, p_num in number )

is

begin

dbms_output.put_line( rpad(p_label,40,'.') || p_num );

end;

begin

dbms_space.unused_space

( segment_owner    => p_owner,

segment_name     => p_segname,

segment_type     => p_type,

partition_name   => p_partition,

total_blocks     => l_total_blocks,

total_bytes      => l_total_bytes,

unused_blocks    => l_unused_blocks,

unused_bytes     => l_unused_bytes,

last_used_extent_file_id => l_LastUsedExtFileId,

last_used_extent_block_id => l_LastUsedExtBlockId,

last_used_block => l_last_used_block );

p( 'Total Blocks', l_total_blocks );

p( 'Total Bytes', l_total_bytes );

p( 'Unused Blocks', l_unused_blocks );

p( 'Unused Bytes', l_unused_bytes );

p( 'Last Used Ext FileId', l_LastUsedExtFileId );

p( 'Last Used Ext BlockId', l_LastUsedExtBlockId );

p( 'Last Used Block', l_last_used_block );

end;

/

通过这个procedure显示的结果,我们可以得到一个segment的HWM的位置。在sqlplus中,我们要看到这个procedure显示的结果,需要设置: set serveroutput on。

这里,HWM = total_blocks - Unused Blocks +1。

我们对一个table进行DML操作,主要是insert,update,delete这三种。当一个table进行了insert数据时,table的HWM会怎样?

我们先做如下实验:

创建表T1,查看高水位情况,插入5000条记录,查看高水位情况,再插入5000条,再查看高水位情况。

SQL> create table t1(id int,name varchar2(30));

表以创建

SQL> exec show_space('T1');

Total Blocks............................8

Total Bytes.............................65536

Unused Blocks...........................5

Unused Bytes............................40960

Last Used Ext FileId....................4

Last Used Ext BlockId...................1041

Last Used Block.........................3

PL/SQL过程已成功完成

SQL>begin

2  for i in 1..5000 loop

3  insert into t1 values (i,i||'__aa');

4  end loop;

5  commit;

6  end;

7  /

PL/SQL过程已成功完成

SQL> exec show_space('T1');

Total Blocks............................16

Total Bytes.............................131072

Unused Blocks...........................0

Unused Bytes............................0

Last Used Ext FileId....................4

Last Used Ext BlockId...................1049

Last Used Block.........................8

PL/SQL 过程已成功完成。

SQL> begin

2  for i in 5001..1000 loop

3  insert into t1 values(i,i||'__aaa');

4  end loop;

5  commit;

6  end;

7  /

PL/SQL 过程已成功完成。

SQL> exec show_space('T1');

Total Blocks............................32

Total Bytes.............................262144

Unused Blocks...........................0

Unused Bytes............................0

Last Used Ext FileId....................4

Last Used Ext BlockId...................1073

Last Used Block.........................8

T1表创建后         HWM=8-5+1=4;

插入5000条记录后   HWM=16-0+1=17;

再插入5000条记录后 HWM=32-0+1=33;

我们可以看到表在进行数据插入时HWM会不停地提升。现在我们来这样一种情况:如果在这期间我们对这个table进行了大量的delete操作,这是table的HWM会不会随着数据量的减少而下降呢?我们将通过一个实验来说明这个问题:

删除5000条记录:

SQL> DELETE FROM T1 WHERE ROWNUM<=5000;

已删除5000行

SQL>cimmit;

提交完成

SQL> exec show_space('T1');

Total Blocks............................32

Total Bytes.............................262144

Unused Blocks...........................0

Unused Bytes............................0

Last Used Ext FileId....................4

Last Used Ext BlockId...................1073

Last Used Block.........................8

现在我们再来观察HWM的结果,可以看到:这里HWM=32 - 0 + 1 = 33。

HWM的位置并没有发生变化。这说明对TABLE T1删除了5000行数据后,并不会改变HWM的位置。

那么,HWM过高会对数据库的性能有什么样的影响呢?

这里我们以全表扫描为例,来讨论HWM过高的不良影响。

同样,我们也通过一个实验来看full table scan在delete前后访问的block数量的情况:

SQL> set autotrace traceonly

SQL> select count(1) FROM T1;

执行计划

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

Plan hash value: 3724264953

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

| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      |     1 |     9   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE    |      |     1 |            |          |

|   2 |   TABLE ACCESS FULL| T1   | 10000 |     9   (0)| 00:00:01 |

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

Note

-----

- dynamic sampling used for this statement

统计信息

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

1  recursive calls

0  db block gets

31  consistent gets

0  physical reads

0  redo size

408  bytes sent via SQL*Net to client

385  bytes received via SQL*Net from client

2  SQL*Net roundtrips to/from client

0  sorts (memory)

0  sorts (disk)

1  rows processed

我们可以看到表在删除了5000条记录后进行全表扫描时,仍然要扫描31个数据块。

我们将表的数据清空,看访问的block是否会减少。

SQL> delete from t1;

已删除5000行

SQL> commit;

提交完成

在这里,我们把oracle先shutdown,然后在startup,以便清空cache中的数据。

SQL> select count(1) FROM T1;

执行计划

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

Plan hash value: 3724264953

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

| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      |     1 |     9   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE    |      |     1 |            |          |

|   2 |   TABLE ACCESS FULL| T1   | 10000 |     9   (0)| 00:00:01 |

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

Note

-----

- dynamic sampling used for this statement

统计信息

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

0  recursive calls

0  db block gets

0  consistent gets

0  physical reads

0  redo size

0  bytes sent via SQL*Net to client

0  bytes received via SQL*Net from client

0  SQL*Net roundtrips to/from client

0  sorts (memory)

0  sorts (disk)

1  rows processed

怎么会是0呢,再少也不应该是0啊。通过不断的百度,终于发现原因是一个bug。如果重启过系统且没有退出SQLPLUS,再次登陆后,启用AUTOTRACE后,除了处理行数外的其他统计信息均为0。

我们退出sqlplus后,重新登录后

SQL> set autotrace traceonly

SQL> select count(1) FROM T1;

执行计划

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

Plan hash value: 3724264953

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

| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      |     1 |     9   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE    |      |     1 |            |          |

|   2 |   TABLE ACCESS FULL| T1   | 10000 |     9   (0)| 00:00:01 |

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

Note

-----

- dynamic sampling used for this statement

统计信息

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

1  recursive calls

0  db block gets

31  consistent gets

0  physical reads

0  redo size

408  bytes sent via SQL*Net to client

385  bytes received via SQL*Net from client

2  SQL*Net roundtrips to/from client

0  sorts (memory)

0  sorts (disk)

1  rows processed

我们可以看到oracle访问的block并没有减少。这说明进行table full scan时,实际上是对HWM下所有的block进行访问。我们知道,访问的block数量越多,代表需要消耗的资源越多。那么,当一个table在进行了大量的delete操作后,或者说,当一个table在HWM之下的block上的数据不饱和时,我们应该考虑采用一些方法来降低该表的HWM,以减小table full scan时需要访问的block数量。

三、如何降低HWM

oracle8i以前的版本,如果我们需要降低segment的HWM,只能采用两种方法:EXP/IMP和CTAS。

从8i开始我们可以使用move来降低高水位,实现原理是将一个table segment从一个tablespace移动到另一个tablespace。

从9i开始我们可以使用DBMS_REDEFINITION,我们可以通过这种方法在线地重组table,来移动table中的数据,降低HWM,修改table的存储参数,分区等等。

从10g开始,oracle开始提供Shrink的命令,假如我们的表空间中支持自动段空间管理(ASSM),就可以使用这个特性缩小段,即降低HWM。这里需要强调一点,10g的这个新特性,仅对ASSM表空间有效,否则会报 ORA-10635: Invalid segment or tablespace type。

现在公司使用的主要是10g的数据块,所以我们只对shrink做讨论。

我们只考虑ASSM的情况,首先创建一个procedure(摘自盖国强的深入解析ORACLE)来得到table的blocks使用情况。

create or replace procedure show_space_assm(

p_segname in varchar2,

p_owner in varchar2 default user,

p_type in varchar2 default 'TABLE' )

as

l_fs1_bytes number;

l_fs2_bytes number;

l_fs3_bytes number;

l_fs4_bytes number;

l_fs1_blocks number;

l_fs2_blocks number;

l_fs3_blocks number;

l_fs4_blocks number;

l_full_bytes number;

l_full_blocks number;

l_unformatted_bytes number;

l_unformatted_blocks number;

procedure p( p_label in varchar2, p_num in number )

is

begin

dbms_output.put_line( rpad(p_label,40,'.') ||p_num );

end;

begin

dbms_space.space_usage(

segment_owner      => p_owner,

segment_name       => p_segname,

segment_type       => p_type,

fs1_bytes          => l_fs1_bytes,

fs1_blocks         => l_fs1_blocks,

fs2_bytes          => l_fs2_bytes,

fs2_blocks         => l_fs2_blocks,

fs3_bytes          => l_fs3_bytes,

fs3_blocks         => l_fs3_blocks,

fs4_bytes          => l_fs4_bytes,

fs4_blocks         => l_fs4_blocks,

full_bytes         => l_full_bytes,

full_blocks        => l_full_blocks,

unformatted_blocks => l_unformatted_blocks,

unformatted_bytes  => l_unformatted_bytes);

p('free space 0-25% Blocks:',l_fs1_blocks);

p('free space 25-50% Blocks:',l_fs2_blocks);

p('free space 50-75% Blocks:',l_fs3_blocks);

p('free space 75-100% Blocks:',l_fs4_blocks);

p('Full Blocks:',l_full_blocks);

p('Unformatted blocks:',l_unformatted_blocks);

end;

/

简单地介绍一下rowid的相关知识:

ROWID在磁盘上需要10个字节的存储空间并使用18个字符来显示它包含下列组件:

数据对象编号:每个数据对象如表或索引在创建时分配,并且此编号在数据库中是唯一的;

相关文件编号:此编号对于一个表空间中的每个文件是唯一的;

块编号:表示包含此行的块在文件中的位置;

行编号:标识块头中行目录位置的位置;

在内部数据对象编号需要32位,相关文件编号需要10位,块编号需要22,位行编号需要16位,加起来总共是80位或10个字节,ROWID使用以64进制数的编码方案来显示该方案将六个位置用于数据对象,编号三个位置用于相关文件编号六个位置用于块编号三个位置用于行编号以64进制数的编码方案使用字符A-Z a-z 0-9 +和/共64个字符。

借助dbms_rowid 包函数我们可以很清楚的知道一条记录的存储位置

create or replace function get_rowid

(l_rowid in varchar2)

return varchar2

is

ls_my_rowid     varchar2(200);

rowid_type     number;

object_number     number;

relative_fno     number;

block_number     number;

row_number     number;

begin

dbms_rowid.rowid_info(l_rowid,rowid_type,object_number,relative_fno, block_number, row_number);

ls_my_rowid := 'Object# is      :'||to_char(object_number)||chr(10)||

'Relative_fno is :'||to_char(relative_fno)||chr(10)||

'Block number is :'||to_char(block_number)||chr(10)||

'Row number is   :'||to_char(row_number);

return ls_my_rowid ;

end;

/

现在我们重新插入T1表10000条记录,然后保留第一条和最后一条

SQL> begin

2  for i in 1..10000 loop

3  insert into t1 values(i,i||'__aa');

4  end loop;

5  commit;

6  end;

7  /

PL/SQL 过程已成功完成。

SQL> delete from t1 where id between 2 and 9999;

已删除9998行。

SQL> commit;

提交完成。

SQL> exec show_space_ASSM('T1');

free space 0-25% Blocks:................0

free space 25-50% Blocks:...............0

free space 50-75% Blocks:...............0

free space 75-100% Blocks:..............28

Full Blocks:............................0

Unformatted blocks:.....................0

PL/SQL 过程已成功完成。

SQL> select get_rowid(rowid) from t1;

GET_ROWID(ROWID)

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

Object# is      :52967

Relative_fno is :4

Block number is :1131

Row number is   :259

Object# is      :52967

Relative_fno is :4

Block number is :1146

Row number is   :0

我们可以看到所有的块free space都在75-100%之间。且两条记录分散在不同的块中。

要使用ASSM上的shrink,首先我们需要使该表支持行移动,可以用这样的命令来完成:

alter table t1 enable row movement;

现在,就可以来降低my_objects的HWM,回收空间了,使用命令:

alter table t1 shrink space;

我们具体的看一下实验的结果:

SQL> alter table t1 enable row movement;

表已更改。

SQL> alter table t1 shrink space;

表已更改。

SQL> exec show_space_ASSM('T1');

free space 0-25% Blocks:................0

free space 25-50% Blocks:...............0

free space 50-75% Blocks:...............0

free space 75-100% Blocks:..............1

Full Blocks:............................0

Unformatted blocks:.....................0

PL/SQL 过程已成功完成。

SQL> select get_rowid(rowid) from t1;

GET_ROWID(ROWID)

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

Object# is      :52967

Relative_fno is :4

Block number is :1124

Row number is   :403

Object# is      :52967

Relative_fno is :4

Block number is :1124

Row number is   :404

当执行了shrink操作后,我们看到两条记录的rowid都发生了变化,且集中到了同一个block中,且都不在原始的block中。

我们还可以在shrink table的同时shrink这个table上的index:

alter table my_objects shrink space cascade;

同样地,这个操作只有当table上的index也是ASSM时,才能使用。

segment shrink分为两个阶段:

1、数据重组(compact):通过一系列insert、delete操作,将数据尽量排列在段的前面。在这个过程中需要在表上加RX锁,即只在需要移动的行上加锁。由于涉及到rowid的改变,需要enable row movement.同时要disable基于rowid的trigger.这一过程对业务影响比较小。

2、HWM调整:第二阶段是调整HWM位置,释放空闲数据块。此过程需要在表上加X锁,会造成表上的所有DML语句阻塞。在业务特别繁忙的系统上可能造成比较大的影响。

shrink space语句两个阶段都执行。

shrink space compact只执行第一个阶段。

如果系统业务比较繁忙,可以先执行shrink space compact重组数据,然后在业务不忙的时候再执行shrink space降低HWM释放空闲数据块。

shrink必须开启行迁移功能。

alter table table_name enable row movement ;

注意:alter table XXX enable row movement语句会造成引用表XXX的对象(如存储过程、包、视图等)变为无效。

四、其余移动HWM的操作

还有几种操作是可以移动HWM的:insert append,truncate。

当我们使用insert /*+ append */ into向一个table中插入数据时,oracle不会在HWM以下寻找空间,而是直接移动HWM,从EMPTY_BLOCKS中获得要使用的block空间,来满足这一操作的blocks的需要。

truncate table一般和delete作比较,delete产生rollback,如果删除大数据量的表速度会很慢,同时会占用很多的rollback segments。truncate 是DDL操作,不产生rollback,释放除了MINEXTENTS分配的空间外的所有空间。速度快一些。

Oracle中HWM与数据库性能的探讨的更多相关文章

  1. Oracle中 HWM与数据库性能的探讨

    链接:http://www.eygle.com/archives/2011/11/oracle_hwm_tuning.html 本文讨论的是oracle中关于table的HWM的内容,主要包括这样几个 ...

  2. Oracle中查询当前数据库中的所有表空间和对应的数据文件语句命令

    Oracle中查询当前数据库中的所有表空间和对应的数据文件语句命令 ------------------------------------------------------------------ ...

  3. 探究 Oracle 高水位对数据库性能影响

    在开始深入分析之前,让我们先来了解一下高水位线 HWM. 一. HWM 的基本原理 (概念) 在 Oracle 中,高水位线(High-warter mark, HWM)被用来形容数据块的使用位置,即 ...

  4. 诊断Java代码中常见的数据库性能热点问题应该这么做!

    “你的Java应用程序的性能是怎样诊断和优化的?不妨看看这两位西医的方子.如果你有更好疗效的药方,也欢迎在评论区告诉我们. 当我在帮助一些开发者或架构师分析及优化Java应用程序的性能时,关键往往不在 ...

  5. Oracle中如何创建数据库

    Oracle数据库的物理结构与MySQL以及SQLServer有着很大的不同.在使用MySQL或SQLServer时,我们不需要去关心它们的逻辑结构和物理结构. 但是在使用Oracle的时候,我们必须 ...

  6. 如何在oracle中导入dmp数据库文件

    Oracle数据导入导出imp/exp就相当于oracle数据还原与备份.exp命令可以把数据从远程数据库服务器导出到本地的dmp文件,imp命令可以把dmp文件从本地导入到远处的数据库服务器中. 利 ...

  7. 基于mybatis向oracle中插入数据的性能对比

    数据库表结构: 逐条插入sql语句: <insert id="insert" parameterType="com.Structure"> INSE ...

  8. 【转】Oracle Freelist和HWM原理及性能优化

    文章转自:http://www.wzsky.net/html/Program/DataBase/74799.html 近期来,FreeList的重要作用逐渐为Oracle DBA所认识,网上也出现一些 ...

  9. 浅谈Oracle数据库性能优化的目标

    Oracle性能优化保证了Oracle数据库的健壮性,为了保证Oracle数据库运行在最佳的性能状态下,在信息系统开发之前就应该考虑数据库的优化策略.从数据库性能优化的场景来区分,可以将性能优化分为如 ...

随机推荐

  1. 微信变声器(WeChat Voice)会是营销新利器吗

    微信变声器(WeChat Voice)2.0 Android版开始内测了,时间从2015年5月20日 - 2015年6月20日,使用微信变声器改变你的声音,并分享给好友! 无论你是想装可爱还是恶搞,微 ...

  2. sql over()---转载

    1.使用over子句与rows_number()以及聚合函数进行使用,可以进行编号以及各种操作.而且利用over子句的分组效率比group by子句的效率更高. 2.在订单表(order)中统计中,生 ...

  3. 暑假集训单切赛第二场 UVA 11988 Broken Keyboard (a.k.a. Beiju Text)(字符串处理)

    一开始不懂啊,什么Home键,什么End键,还以为相当于括号,[]里的东西先打印出来呢.后来果断百度了一下. 悲催啊... 题意:给定一个字符串,内部含有'['和']'光标转移指令,'['代表光标移向 ...

  4. hdu 4412 Sky Soldiers DP

    动态规划,主要是用单调性求区间的最小期望. 代码如下: #include<iostream> #include<stdio.h> #include<algorithm&g ...

  5. Openfire 代码部署报错: Variable references non-existent resource:${workspace_loc:openfire_src}

    Variable references non-existent resource:${workspace_loc:openfire_src} -DopenfireHome=“${workspace_ ...

  6. Linux多线程编程和Linux 2.6下的NPTL

    Linux多线程编程和Linux 2.6下的NPTL 在Linux 上,从内核角度而言,基本没有什么线程和进程的区别--大家都是进程.一个进程的多个线程只是多个特殊的进程他们虽然有各自的进程描述结构, ...

  7. mysql建表时拆分出常用字段和不常用字段

    一对一 一张表的一条记录一定只能与另外一张表的一条记录进行对应,反之亦然. 学生表:姓名,性别,年龄,身高,体重,籍贯,家庭住址,紧急联系人 其中姓名.性别.年龄.身高,体重属于常用数据,但是籍贯.住 ...

  8. 检测系统是X86系统,还是X64系统

    function IsWin64: Boolean; var Kernel32Handle: THandle; IsWow64Process: function(Handle: Windows.THa ...

  9. Nim语言:Pascal的语法,Python的缩进

    http://nim-lang.org/ 德国人Andreas Rumpf的作品,原因是他对过去使用的每种语言都不满意(Pascal也不满意?).以前叫Nimrod语言,从0.96版本开始改名为Nim ...

  10. 56. Merge Intervals

    题目: Given a collection of intervals, merge all overlapping intervals. For example,Given [1,3],[2,6], ...