inst_id分区避免rac gc buffer busy
解决思路
原因/解决方法
-
热点块(hot block)
在AWR中Segments by Global Cache Buffer Busy 记录了访问频繁的gc buffer.
解决方法可以根据热点块的类型采取不同的解决方法,比如采取分区表,分区索引,反向index等等。这点与单机数据库中的buffer busy waits类似。 -
低效SQL语句
低效SQL语句会导致不必要的buffer被请求访问,增加了buffer busy的机会。在AWR中可以找到TOP SQL。解决方法可以优化SQL语句减少buffer访问。这点与单机数据库中的buffer busy waits类似。 -
数据交叉访问。
RAC数据库,同一数据在不同数据库实例上被请求访问。
如果应用程序可以实现,那么我们建议不同的应用功能/模块数据分布在不同的数据库实例上被访问,避免同一数据被多个实例交叉访问,可以减少buffer的争用,避免gc等待。 -
Oracle bug
建议安装Oracle推荐的最新Patch Set和PSU。
Patch set和PSU信息请参考:Oracle Recommended Patches – Oracle Database (Doc ID 756671.1)
通过ASH报告,我看到刚才系统HANG住的时候,主要的等待事件是buffer busy wait:
Avg Active
Event Event Class % Activity Sessions
----------------------------------- --------------- ---------- ----------
buffer busy waits Concurrency 72.55 9.31
enq: HW - contention Configuration 14.21 1.82
gc buffer busy Cluster 7.67 0.98
CPU + Wait for CPU CPU 2.13 0.27
-------------------------------------------------------------
看样子是由于热块冲突导致系统HANG住的,而且高水位推进相关的锁等待也很严重,应该系统中有大量的数据插入。从这个情况看想简单的通过调整参数来解决问题是不大可能了,必须从应用的特点去分析,才有可能找到解决方案。于是我问郝经理:"你的应用是什么样的,是不是有大量的插入操作?"
郝经理告诉我这个系统是企业应用集成系统,是从其他系统中接收数据,经过处理后存储在本系统中,供经营分析系统使用。目前的压力测试中的主要操作是对3、4张大表进行大批量的数据插入,其中还有张表里带LOB字段。
我和郝经理研究了一下,对这些数据的访问,除了大规模的数据插入外,一般来说是按主键访问,或者按某些分类条件的小范围查询,每次范围查询顶多占整个表数据量的几万分之一,并且范围查询的比例相对较小,仅占整个系统查询数量的10%不到。这种情况是典型的可以使用HASH分区的,通过HASH分区既可以解决BUFFER BUSY WAIT的问题,又可以解决enq: HW -contention的问题。于是我建议郝经理把这几张表按照主键MSG_ID做hash分区,先尝试一下16个分区,看看能不能解决问题。
按照我的建议做了HASH分区后,压力测试重新开始了,这回看样子效果很不错,从em控制台上看,buffer busy wait等待已经少了很多,而且enq: HW - contention等待也消失了。
Avg Active
Event Event Class % Activity Sessions
----------------------------------- --------------- ---------- ----------
gc buffer busy Cluster 90.69 14.83
gc current request Cluster 6.05 0.99
CPU + Wait for CPU CPU 1.05 0.17
目前的主要等待是gc buffer busy,由于目前在连个节点上各有40个并发插入的会话在进行数据插入,因此全局BUFFER等待的问题肯定是存在的,目前压力测试已经进行了1个多小时了,从em观察到的情况来说系统一直还很平稳。现在的时间已经是中午的12点半了,郝经理建议我们先去吃饭,等吃完饭回来再看看结果。
在下面的食堂里简单吃了点东西,我们就回到了办公室,现在是下午的13点20分了,压力测试也已经开始了差不多2个小时了,系统的状态依然和我们吃饭前一样,还没有出现hang住的现象。不过从平均事务响应时间来看,现在比一个小时前要慢了40%,这是一个十分不好的现象,如果这种趋势延续下去的话,很可能会再次出现系统HANG住的现象。
看着郝经理他们兴奋的样子,我给他们泼了盆冷水,可能问题还是没彻底解决,从目前的趋势上看,有可能系统还会HANG住。我正在和他解释为什么我会有这种预感,突然从EM上看到系统活跃情况突然下降了,系统可能又HANG住了。
我连忙做了一个3级的hanganalyze,从trace上看:
Open chains found:
Chain 1 : :
<0/786/9/0xff010e58/9474/gc buffer busy>
-- <0/785/26/0xff011e38/9512/gc current request>
Chain 2 : :
<0/791/32/0xff00e6a8/9404/gc buffer busy>
-- <0/785/26/0xff011e38/9512/gc current request>
Chain 3 : :
<0/792/12/0xff00ee98/9408/gc buffer busy>
-- <0/785/26/0xff011e38/9512/gc current request>
Chain 4 : :
<0/794/19/0xff00d6c8/9334/gc buffer busy>
-- <0/785/26/0xff011e38/9512/gc current request>
大量的gc buffer busy导致了大量的gc current request等待。很多会话由于gc buffer busy被HANG住一段时间,实际上系统并没有真正的HANG住,而是变得十分慢而已。不过如果这种情况的范围如果扩大的话,就会导致应用软件出现问题,产生丢失数据的现象。过了几分钟,郝经理那边也确认了这次系统HANG住,1号节点的应用被挂起了35秒钟,丢失了70多条数据,实验再次失败了。
看样子GC BUFFER BUSY最终还是导致了严重的问题。由于他们的应用没有针对RAC进行优化,同一个应用分别在2个节点上跑,所以导致了严重的节点间的热块冲突。也许把这个应用集中在一个节点上就能够解决问题。我把我的想法和郝经理说了,郝经理觉得可以试试,不过他们的系统发展起来特别快,所以从最初做设计的时候就考虑要使用RAC,而且今后不仅要支持双节点,还可能扩展为3节点甚至4节点。目前的业务有可能单节点能够支持,不过今后一个节点可能无法支撑所有的负载。
我考虑了一下,刚才应用放在两个节点上跑的时候,两个服务器的CPU使用率都已经超过60%了,如果跑在一个节点上,确实负载挺大的,一个节点还真有可能支持不了,更不要说今后系统扩展性的问题了。看样子必须从底层结构上进行调整了。我问郝经理,如果在表里面加一个字段,并且每次插入的时候给这个字段赋一个常量,这样修改程序他们的工作量大不大。郝经理想了想说应该问题不大,修改一下也就是10分钟的事情。
我说如果这样就好办,我们可以对表结构做一个调整,来彻底解决这个问题。我们重新对表进行分区,使之成为一个复合分区表,在这张表上加入一个字段inst_id,也就是实例编号,这个字段作为主分区字段,按照这个分区做范围分区后,再设置msg_id的hash分区。调整后的表结构如下:
CREATE TABLE QSTORE
(
MSG_ID VARCHAR2(64 BYTE) NOT NULL,
DOCE_ID VARCHAR2(100 BYTE) NOT NULL,
DOC_KEY VARCHAR2(100 BYTE) NOT NULL,
BUS_NAME VARCHAR2(100 BYTE),
MODULE_NAME VARCHAR2(100 BYTE),
CATEGORY VARCHAR2(64 BYTE),
SUB_CATEGORY VARCHAR2(64 BYTE),
DOC_SIZE NUMBER(11) DEFAULT 0,
CREATE_TIME DATE,
MODIFY_TIME DATE,
EXPIRED_TIME DATE,
DOC_FLAG NUMBER(2),
Inst_id number
) initrans 20 pctfree 20 tablespace TS_DATA_EAI
PARTITION BY RANGE(inst_id)
SUBPARTITION BY HASH(msg_id) SUBPARTITIONS 8
(PARTITION p1 VALUES LESS THAN (2),
PARTITION p2 VALUES LESS THAN (3));;
Inst_id的值就是插入进程连接到的实例的ID,这样调整后,在1号节点上插入的数据将全部被插入到inst_id=1的分区,插入操作就不会产生大量的global cache request了。这种做法也是在RAC上解决GC BUFFER BUSY的常用方法之一。
郝经理很快就明白了我的意思,上楼去安排开发人员修改应用了。我也趁机休息休息,准备下面的测试了。这时候老储的电话又打了进来,那个问题终于解决了,通过hcheck对数据字典进行检查,发现有一张表的数据字典出现了不一致,在tab$中还有记录,但是在obj$里的记录缺失了,查找这个表空间中有什么对象找不到,但是删除tablespace的时候又删除不了。把tab$中的数据手工删除后,表空间就成功的删除了。
刚和老储通完电话,郝经理的电话就打了进来,他说应用已经修改好了,他已经开始了测试,大概10分钟后压力就会加载到数据库上,让我监控一下有没有什么问题。从测试刚开市的情况看,gc buffer busy基本上消失了,系统也比较平稳,从EM的监控情况来看,系统一切都很平稳,系统已经运行了一个多小时,从awr报告来看,现在的平均事务响应时间,和系统刚刚启动时差别不大,并且gc buffer busy 等待事件也已经不在top 5等待时间里了。看样子通过这么优化,系统趋于稳定了。郝经理建议我们先回去,压力测试做一晚上,明天上午10点再来看结果。如果那时候发现系统一切正常,那么说明问题已经解决了。
select t.INSTANCE_NUMBER into p_inst_id from v$instance t
通过procedure进行插入,在执行的开始,取得instance_id
gc buffer busy: 产生这个等待事件是由于实例间产生热块争用造成的,要解决这个问题有两种方法:
第一、分割应用:把应用集中在一个节点上运行
第二、对表添加一个字段,这个字段存放实例的id号,如果在节点1执行的那么存放节点1的实例id号, 如果在节点2执行的那么存放节点2的实例id号,在执行之前首现获取实例的id号。