Oracle中HWM与数据库性能的探讨
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与数据库性能的探讨的更多相关文章
- Oracle中 HWM与数据库性能的探讨
链接:http://www.eygle.com/archives/2011/11/oracle_hwm_tuning.html 本文讨论的是oracle中关于table的HWM的内容,主要包括这样几个 ...
- Oracle中查询当前数据库中的所有表空间和对应的数据文件语句命令
Oracle中查询当前数据库中的所有表空间和对应的数据文件语句命令 ------------------------------------------------------------------ ...
- 探究 Oracle 高水位对数据库性能影响
在开始深入分析之前,让我们先来了解一下高水位线 HWM. 一. HWM 的基本原理 (概念) 在 Oracle 中,高水位线(High-warter mark, HWM)被用来形容数据块的使用位置,即 ...
- 诊断Java代码中常见的数据库性能热点问题应该这么做!
“你的Java应用程序的性能是怎样诊断和优化的?不妨看看这两位西医的方子.如果你有更好疗效的药方,也欢迎在评论区告诉我们. 当我在帮助一些开发者或架构师分析及优化Java应用程序的性能时,关键往往不在 ...
- Oracle中如何创建数据库
Oracle数据库的物理结构与MySQL以及SQLServer有着很大的不同.在使用MySQL或SQLServer时,我们不需要去关心它们的逻辑结构和物理结构. 但是在使用Oracle的时候,我们必须 ...
- 如何在oracle中导入dmp数据库文件
Oracle数据导入导出imp/exp就相当于oracle数据还原与备份.exp命令可以把数据从远程数据库服务器导出到本地的dmp文件,imp命令可以把dmp文件从本地导入到远处的数据库服务器中. 利 ...
- 基于mybatis向oracle中插入数据的性能对比
数据库表结构: 逐条插入sql语句: <insert id="insert" parameterType="com.Structure"> INSE ...
- 【转】Oracle Freelist和HWM原理及性能优化
文章转自:http://www.wzsky.net/html/Program/DataBase/74799.html 近期来,FreeList的重要作用逐渐为Oracle DBA所认识,网上也出现一些 ...
- 浅谈Oracle数据库性能优化的目标
Oracle性能优化保证了Oracle数据库的健壮性,为了保证Oracle数据库运行在最佳的性能状态下,在信息系统开发之前就应该考虑数据库的优化策略.从数据库性能优化的场景来区分,可以将性能优化分为如 ...
随机推荐
- linux网卡速率和双工模式的配置
linux网卡速率和双工模式的配置 (2012-09-06 14:39:57) 转载▼ 标签: 科技 网络接口 协商 网卡 工具 it 分类: Linux 改变网络接口的速度和协商方式的工具miito ...
- 如果使用得当,MySQL 也可以化身 NoSQL
[编者按]随着互联网和移动互联网的发展,各个机构都需要支撑远超过以往的数据.而在这个需求的刺激下,IT 领域出现了大量数据处理技术,其中之一就是 NoSQL .灵活的数据类型,高效的处理能力,让 No ...
- HDU4725 The Shortest Path in Nya Graph SPFA最短路
典型的最短路问题,但是多了一个条件,就是每个点属于一个layer,相邻的layer移动,如x层移到x+1层需要花费c. 一种显而易见的转化是我把这些边都建出来,但是最后可能会使得边变成O(n^2); ...
- 【leetcode】Median of Two Sorted Arrays(hard)★!!
There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted ...
- MySQL 语句级避免重复插入—— Insert Select Not Exist
想要插入一条数据,要避免重复插入,又不想折腾两回数据库连接操作,可以参考如下办法. INSERT INTO table(column1,column2,column3 ...columnN) SELE ...
- Hadoop格式化HDFS报错java.net.UnknownHostException: localhost.localdomain: localhost.localdomain
异常描述: 在对HDFS格式化,执行hadoop namenode -format命令时,出现未知的主机名的问题,异常信息如下所示: [shirdrn@localhost bin]$ hadoop n ...
- 李洪强iOS开发之initWithFrame,initWithCoder和aweakFormNib
1 initWithFrame 通过代码创建控件的话用这个方法设置 2 initWithCoder(先执行) 与从xib加载有关系的 在此方法里面设置原有子控件的值是不行的,因为还没有连好线 3 ...
- POJ1118 Lining Up
快弄死我了 最后的原因是abs和fabs的区别... 说点收获:1.cmp函数返回的是int,所以不要直接返回double相减的结果2.define inf 1e9和eps 1e-93.在整数相除得到 ...
- @Override在JDK1.5和JDK1.6中用法区别
@Override 注解在jdk1.5环境下,只能用于对基类(父类)的方法的重写.而不能用于对实现的接口的方法的实现.而在jdk1.6环境下,两者都适用.
- Shell脚本的编写
筛选后统计总数 cat logs | grep IconsendRedirect | wc -l >> bb.log 筛选后分类统计并且排序 cat logs | grep Iconsen ...