问题概述
enq:HW-contention等待事件争用主要发生在数据插入、修改或手工对该对象allocate/deallocate空间时,为防止多个进程同时修改HWM而提供的锁叫做HW锁,想要移动HWM的进程必须获取HW锁。
若在获取HW锁过程中发生争用,则等待enq:HW-contention 事件。在并发量过高的情况下,有可能引发严重的数据库性能问题。
问题原因
通过ASH找到,基本上是INSERT 语句导致的这个等待事件。
和客户交流,这个对象是TG_APP_POOL。 另外从AWR报告中也找到类似的SQLID, 比如
另外一种现象, insert 一条数据执行效率是 200S/次。 综合比较,insert 语句产生大量的HW等待事件,再次跟客户要了这个表的表结构。
表结构上面有3个lob 字段, 这边很容易误解为clob 字段是导致, lob 是可能导致这个等待事件的。
HW等待事件产生的原因, 这里不是lob 字段导致的。
结合实际数据得出:insert 数据的时候,这个时候会持有排他锁。因此只有一个session 去推高水位。 其他session 就会经历图中的这个等待事件。因此表现的是insert into tg_app_pool这SQl执行越多, 执行越慢,这个等待事件就会越长。
解决方案
- 让表空间一次多分配点空间
这个表重新配置参数“next 1M”参数。尝试调整 到 5M , 也就是一次给分配5M, 那么分配空间就没有那么频繁了, 所以这个等待事件就会减少。
- 提前分配空间
可以在不忙的时候,给推高水位。比如执行批量的insert 数据,然后再回滚掉。水位自然也就高了。
选择了第一种方案.
效果:
实施后,客户反馈在差不多业务量下。
优化之前:127ms。
优化之后:24ms。
还是有点高,但是证明方向是对的。
跑批大批量数据insert导致enq等待事件问题优化
问题原因
分析思路结合AWR和等待事件,将2:30~3:30的等待事件和AWR捞出来进行分析
围绕上述同一张表批量insert产生buffer busy waits,gc index operation,latch: cache buffers chains,enq:HW-contention等待事件
注:
由于是RAC会产生gc当无需将“_gc_fast_index_split_wait”=0 这个不是本质原因,当设为0后gc index operation等待事件消除转而成为
enq:TX-index contention,其本质还是insert导致索引分裂竞争,会吃到内存
解决方案
处理此跑批问题有2个方向
1.取消不必要的索引分裂即删除不使用的索引和多余索引从而减小或消除buffer busy waits,gc index operation,latch: cache buffers chains
2.shrink space,取消高水位enq:HW-contention 处理完毕后第二天跑批时间大幅度缩减已超额达标
只剩log file sync较高,检查优化参数“_use_adaptive_log_file_sync”和“_use_single_log_writer”没问题后,请开发同事调整insert commit笔数亦解决
故障排除enq:HW contention等待事件
enq:HW-contention等待事件争用主要发生在数据插入、修改或手工对该对象allocate/deallocate空间时,为防止多个进程同时修改HWM而提供的锁叫做HW锁,想要移动HWM的进程必须获取HW锁。
若在获取HW锁过程中发生争用,则等待enq:HW-contention 事件。在并发量过高的情况下,有可能引发严重的数据库性能问题。在此,我们通过一个故障实例来描述风险发生的具体场景。
问题描述
某客户生产系统多个核心库发生告警,业务反馈对前台影响严重。查看故障期间的等待事件信息,发现当时主要的等待事件为enq: HW - contention等待。
问题分析
进一步查看故障期间的TOP SQL信息,发现对应的SQL主要是SQL_ID dugcxypa8u957。
对应SQL是往T_XXX表中插入数据。观察该表的DDL语句得知是T_XXX表按照天进行interval分区。
采样故障期间三分钟的ASH数据进行分析:
SQL> select DBMS_UTILITY.data_block_address_file(to_number(3841531793)) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(TO_NUMBER(3841531793))
-----------------------------------------------------------
915
SQL> select DBMS_UTILITY.data_block_address_block(to_number(3841531793)) from dual;
DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(TO_NUMBER(3841531793))
------------------------------------------------------------
3743633
根据等待事件enq:HW-contention参数p2、p3可以查出发生enq:HW-contention等待事件的对象是T_XXX表的分区SYS_Pxxx的extent_id 0对象上,也就是在新创建每天的分区SYS_Pxxx分配extent_id 0的初始段大小和HW后,随着数据的插入导致需要申请更多的空间来移动高水位,从而并发插入数据就会出现enq:HW-contention等待。
造成HW竞争的根本原因为数据段的High Water Mark高水位线移动时,每次移动都要获取独占锁。在大并发的insert 环境下最容易出现该问题。
问题解决
对于此类情况,可以考虑以下几种处理方式:
1)手工推进高水位线,此方法比较安全。提前预估每天的数据量,在分区创建的同时直接手工给足够,这样的好处是不会再去持有HW锁了因为高水位线已经足够高了,坏处就是该操作为DDL 操作,OGG 与logical standby 需要处理下,另外也可能会浪费一定空间。
ALTER TABLE Table_name MODIFY partition Partition_name (allocate extent (size 30M));
2)采取hash分区方式对数据表进行打散,可以对T_XXX表按照ID字段进行hash分区,分区数量建议采用8或者16即可,Hash分区对于数据插入时不用担心热点数据导致某个数据表或者索引出现空间不足而需要重新申请空间。
目前数据表采用的是range时间字段的分区,对于每天插入数据新生成分区,新增分区依然会存在热点数据,并发环境对于热点数据极容易出现enq索引分裂、ITL事务槽、HW高水位线等竞争。