本篇继续讨论HWM的影响和处理。
3、HWM对系统的影响
HWM是Oracle标记数据表曾经使用过的最多数据块位置。同水库中标记历史最高水位一样,HWM只是表示曾经使用过usable和从未使用过unusable数据块之前的分界。
在堆表(Heap Table)的情况下,每次普通的insert操作是不一定推动HWM移动的。因为Heap Table的随机存储机制,使得在HWM下,可能存在很多的空闲空间。默认情况下的insert操作,Oracle是会优先插入这些空闲空间,以后才会推高HWM。
对一般情况下,我们遇到HWM相关的影响只有一种,就是超高水位线数据表中数据量很少的情况。我们经常听到开发或者运维人员抱怨:“一张数据表,其中的数据没有几条,或者根本就是空表,但是select一下超慢无比”。
这种情况下,一般就可以考虑是由于HWM过高引起的问题。我们访问数据表大都是两个方式:Full Table Scan或者Access Table by rowid。两种方式下,Oracle的行为完全不同。
ü Access Table by rowid,是Oracle根据数据行记录的物理地址进行精确定位。Rowid是Oracle物理地址表述,包括文件号、对象号、块号和块中slot编号。这种方式被认为是目前最快定位数据行的方式;
ü Full Table Scan,也就是我们常说的全表扫描。Oracle处理FTS的流程是:先从数据字典表中获取到数据段头segment header块位置,读取其中的信息。段头块中就包括了该数据段所属的extent分配使用情况,以及HWM位置。根据这些信息,Oracle会逐个块的读取分配extents下的所有使用过的block。不管该块是否已经空闲,直到读取到HWM位置;
Access table by rowid是与HWM无关的操作访问类型。而FTS全表扫描的时候是受到HWM严重影响的。如果该数据表曾经很大,现在数据量很小,那么进行FTS的话,速度是很慢的。
SQL> set timing on;
SQL> explain plan for select count(*) from t;
Explained
Executed in 0.047 seconds
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2966233522
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 166 (1)| 00:00:02 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| T | 1 | 166 (1)| 00:00:02 |
-------------------------------------------------------------------
9 rows selected
Executed in 0.359 seconds
证明select count(*) from t执行FTS路径。
//当一个extent分配时;
SQL> select count(*) from dba_extents where wner='SYS' and segment_name='T';
COUNT(*)
----------
1
SQL> select count(*) from t;
COUNT(*)
----------
0
Executed in 0 seconds
//经过一系列添加后删除,分配出38个数据extents.
SQL> select count(*) from dba_extents where wner='SYS' and segment_name='T';
COUNT(*)
----------
38
Executed in 0.125 seconds
SQL> select count(*) from t;
COUNT(*)
----------
0
Executed in 0.016 seconds
可见,过高或者失控的HWM可能会引起一些性能问题。那么,在讨论如何处理过高HWM之前,我们先讨论下怎么看待HWM。笔者有如下观点:
首先,一般情况下的数据表,特别是OLTP系统,HWM在生产运行一段时间之后是维持在相对稳定的情况下。对online数据表而言,要控制累计数据和不活跃数据的数据量,就需要主动进行归档功能,来转移过期数据。所以,online数据表的数据量是可以维持在一个业务可预测的程度的,从而HWM出现一味上升的可能性不大。一般数据表的HWM应该是可以维持在一个稳定程度的。
如果出现HWM一味升高,频繁引起问题的情况,可以认为是两种可能。首先是系统可能就没有考虑到数据归档的情况,一味的增加online数据表,最终会引发整体的关键业务性能问题。另一种是数据表存在数据删除归档功能,但是在加载操作时,过多不合理的使用direct load方式加载数据,一味推高HWM。之后的归档又将HWM下的数据删除。这样就需要进行重新设计调整。
那么,有没有数据表HWM只增加,数据只增加不减少。我们认为通常生产环境下这种情况是很少的,而且无论是从系统设计规划还是运维管理上,这都是不合理的。数据是有优先级别的,一些老旧数据尽可能的分离出online关键业务,可以设置专门的查询模块。同时,对一些历史表,也不是一味的增加,在一些期限之后也要考虑使用磁带进行offline处理。
4、处理HWM问题的方法
最后我们讨论一下出现HWM之后,如何进行处理。HWM问题主要在于堆表内部空间空间过多,所以解决的核心在于将数据进行重排,“压紧”数据行。目前处理这种问题的方法有三个。
ü Truncate 数据表
HWM下调的解决是让数据段segment“交还”回分配的空间。Truncate是一个DDL语句,相当于将数据段重新构建。具体处理上,可以先将数据保存在一个备份表中,之后truncate目标数据表,再将备份数据还原。
SQL> select count(*) from t;
COUNT(*)
----------
9
SQL> select count(*) from dba_extents where wner='SYS' and segment_name='T';
COUNT(*)
----------
38
SQL> create table t_bak as select * from t;
Table created
SQL> truncate table t;
Table truncated
Executed in 0.125 seconds
SQL> insert into t select * from t_bak;
9 rows inserted
SQL> commit;
Commit complete
SQL> select count(*) from dba_extents where wner='SYS' and segment_name='T';
COUNT(*)
----------
1
Executed in 0.11 seconds
数据段从38个分区,下降为1个分区。
ü Move方式调整数据行
Move是对数据表进行表空间移动的命令,我们也可以使用该命令进行数据表HWM收缩操作。Move命令的效果是将数据表行记录进行移动,所以可以将分散的数据行加以整理。
SQL> select count(*) from dba_extents where wner='SYS' and segment_name='T';
COUNT(*)
----------
21
SQL> create index idx_t_id on t(object_id);
Index created
--此时其他会话insert数据,该数据表有行共享锁;
SQL> alter table t move;
alter table t move
ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源
SQL> alter table t move;
Table altered
SQL> select count(*) from dba_extents where wner='SYS' and segment_name='T';
COUNT(*)
----------
19
注意上面的代码段,提示资源繁忙。说明Move操作会以独占的方式持数据表锁,在进行move的时候是不允许其他会话进行DML/DDL操作的。此时,我们查看T上索引的情况。
SQL> select index_name,STATUS from dba_indexes where index_name='IDX_T_ID' and wner='SYS';
INDEX_NAME STATUS
------------------------------ --------
IDX_T_ID UNUSABLE
SQL> alter index idx_t_id rebuild;
Index altered
SQL> select index_name,STATUS from dba_indexes where index_name='IDX_T_ID' and wner='SYS';
INDEX_NAME STATUS
------------------------------ --------
IDX_T_ID VALID
可见这是一个move命令的注意点,由于move会引起数据行的变化,从而对应的数据行rowid也会发生变化。保存rowid在叶子节点上的索引也就随时失效,需要DBA手工的进行rebuild操作。
此外,move命令还有一个问题就是空间使用,要求在表空间上有额外的空间供使用。
ü Shrink space ASSM下的新手段
进入Oracle 9i之后,推出了ASSM(Automatic Segment Space Management)特性。到了10g之后,随之推出了shrink space功能,可以进行HWM收缩操作。
注意,shrink space使用是需要表空间使用ASSM管理方式,同时要开启数据表row movement功能。
SQL> select count(*) from dba_extents where wner='SYS' and segment_name='T';
COUNT(*)
----------
21
--第一次尝试shrink space
SQL> alter table t shrink space;
alter table t shrink space
ORA-10636: ROW MOVEMENT is not enabled
SQL> alter table t enable row movement;
Table altered
SQL> alter table t shrink space;
Table altered
SQL> select count(*) from dba_extents where wner='SYS' and segment_name='T';
COUNT(*)
----------
18
SQL> select index_name,STATUS from dba_indexes where index_name='IDX_T_ID' and wner='SYS';
INDEX_NAME STATUS
------------------------------ --------
IDX_T_ID VALID
使用shrink space,基本可以消除move操作带来的问题,如索引手工重建等。简单的说,shrink space是一个两步走操作:第一步是将数据行整理压实,第二步是下调水位线HWM。
通过不同的控制参数,我们是可以控制shrink space的操作步骤的。使用shrink space compact可以让Oracle只进行数据行压实,不继续HWM下调。
SQL> alter table t shrink space compact;
Table altered
5、结论
Oracle HWM是一个从存储角度对Oracle行为分析的重要内容,可以帮助我们了解Oracle在处理空间分配和使用时的规则。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/17203031/viewspace-707621/ ,如需转载,请注明出处,否则将追究法律责任。