6月8日 ITL等待引发的RAC性能问题
从这几天的情况来看,虽然说系统性能还很不错,不过我从IO和CPU的性能趋势上,已经感觉到了不妙。我习惯于每天把STATSPACK报告中的关键数据采集到一个EXECL表格里,定期生成一个折线图来查看趋势。这几天我明显看到折线的斜率在持续增加,以我的经验,这是很不好的先兆。今天早上和老万在QQ上聊了几句,和他说了我的担心。老万随后又咨询了一下Richard,Richard认为目前IO上升只是因为这几天的系统负载在不断增长,IO系统经过前一段时间的优化已经有了数倍的提升,肯定可以确保不会成为瓶颈。
看样子老万也是被最近良好的系统状态迷住了眼睛,对我的判断也开始将信将疑了。在这种情况下我也不好多说什么,反正主动权也不在我手里,就由着Richard折腾吧,如果折腾好了,那大家都消停了,如果出问题了,我再出面吧。
老万和我聊了几句就出去开会了,我正准备离开,突然小吴给我的MSN发来一个消息:
老白:帮下忙,我这里出大问题了。一套3节点的RAC系统,最近这几天总是会莫名其妙的变慢甚至HANG住,有时候几分钟,有时候10多分钟。重启应用后也不能解决问题,过一段时间又出问题了。
今天关了一个节点,只剩下两个节点,系统反而稳定了,一上午都没有出现HANG主的现象,不过本来三个节点的业务压在两个节点上,两个节点的的CPU都快100%了,内存也有点不够用。用户很着急,想尽快找到问题的原因,尽快解决了,然后把第三个节点也用起来。
我马上看了看他发过来的AWR报告:
Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Buffer Nowait %: 99.57 Redo NoWait %: 99.99
Buffer Hit %: 98.06 In-memory Sort %: 100.00
Library Hit %: 99.81 Soft Parse %: 99.73
Execute to Parse %: 21.28Latch Hit %: 99.86
Parse CPU to Parse Elapsd %: 57.16 % Non-Parse CPU: 97.01。
从命中率上看,各项指标都还算正常,BUFFER NOWAIT的比例略微偏低一些。TOP 5 EVENTS是:
Top 5 Timed Events
~~~~~~~~~~~~~~~~~~ % Total
Event Waits Time (s) Ela Time
-------------------------------------------- ------------ -------------------
enqueue 1,872,325 85,528 39.87
buffer busy global cache 218,188 34,053 15.88
db file sequential read 1,366,797 20,368 9.50
log file sync 499,127 20,099 9.37
buffer busy waits 216,884 9,241 8.97
从TOP 5 EVENTS上来看,ENQUEUE等待占近40%,buffer busy global cache等待占15.88%,这是典型的全局热块冲突现象。看样子解决热块冲突是最终解决问题的关键。我看了一下热块冲突相关的等待情况:
Class | Waits | Total Wait Time (s) | Avg Time (ms) | |||
data block | 108,5360 | 10200 | 9 | |||
1st level bmb | 7930 | 10 | 1 | |||
undo header | 2820 | 0 | 0 | |||
2nd level bmb | 560 | 0 | 0 | |||
segment header | 520 | 0 | 0 | |||
undo block | 3 | 0 | 0 |
看样子热块冲突主要集中在数据块上,看样子又免不了要调整表的存储结构。我又往下看了看锁等待情况的分析:
Avg Wt Wait
Eq Requests Succ Gets Failed Gets Waits Time (ms) Time (s)
-- ------------ ------------ ----------- ----------- ------------- ------------
TX 568,022 568,098 5 81,688 1,031.33 84,247
US 1,170,363 1,170,362 0 806,425 1.12 901
SQ 247 247 0 25 11,396.24 285
CF 6,252 6,249 3 190 256.92 49
HW 2,787 2,787 0 1,162 2.10 2
从锁等待的情况来看,主要的等待还是TX锁,从这里也可以为我刚才的热块冲突导致问题提供一个旁证。锁等待过高的原因不是因为Oracle内部锁,而是因为TX事务锁导致。小吴他们在故障发生的时候还走了HANGANALYZE和SYSTEMSTATE DUMP。从HANGANALYZE上我们看不到任何异常的地方,把SYSTEMSTATE DUMP用ass.awk分析后也没有发现很明显的问题。主要的等待是TX和BUFFER BUSY GLOBAL CR和BUFFER BUSY WAIT。
我问小吴故障出现时有没有检查过V$LOCK,小吴说当时都看了,主要都是TX锁。我问小吴当时有没有留意TX锁的LMODE是多少,小吴马上翻看了一下日志,说大多数是6,不过也有不少是4。LMODE=4是共享模式,LMODE=6是排他模式。如果存在大量的LMODE=4的锁请求,那说明可能ITL存在等待现象,因为LMODE为4一般来说可能是ITL等待或者等待索引页节点分裂。于是我让小吴检查一下ITL等待的情况:
set line 132
col statistic_name format a30 trunc
SELECT T.OWNER, T.OBJECT_NAME, T.OBJECT_TYPE, STATISTIC_NAME, T.VALUE value
FROM v$segment_statistics T
WHERE t.STATISTIC_NAME = 'ITL waits'
AND t.VALUE > 0 order by value;
这个查询排在前几位的对象都是我们需要关注的。从查询的结果来看,排在前面的10几张表和索引都有大量的ITL等待。于是小吴检查了一下这几张表,发现这些表的存储参数中initrans都是2,pctfree都是缺省的10。看样子问题很明显了,initrans是导致问题的元凶。我建议他们把这些表和索引的inittans加大为10,并且最好把相关的表和索引都重建一下。小吴说其中有几张表基本上只做insert ,不会做UPDATE和DELETE操作,是不是只需要修改参数,不需要重建了,那几张表都是分区表,最小的也有7、8个G。
我想了想,INITRANS参数修改后,新的数据块中这个参数就会起作用,而老的数据块不会生效,所以需要对表进行重组才能彻底解决问题。而像小吴说的那种情况,老的数据块很少会做DML操作,表不重组问题也不大。不过索引还是需要REBUILD的,否则索引上的ITL争用也可能会导致问题。
小吴马上修改了这些表和索引的参数。索引重建和部分表的重组工作只能等晚上停了应用再做了。和小吴分析了半天才发现已经是下午的3点多钟了,午饭还没吃,不过一点饥饿的感觉都没有,看样子也没必要去吃午餐,只能晚上多吃点了。这样的生活习惯,人不胖才怪呢。
6月9日 ORA-8104
昨天和小吴处理了一个RAC的性能故障,昨晚上小吴他们重建了部分表和索引,上午10点多钟小吴给我打了电话,他说今天3个节点都开了,从上午的ASH和AWR报告情况来看,BUFFER BUSY GLOBAL CR和ENQUEUE等待得到了很大的改善,现在排在TOP 5 EVENTS第一位的是CPU TIME了。
小吴看到今天系统的状态,觉得RAC优化方面需要考虑的问题太多了。小小的ITL居然会引起如此严重的问题。确实是,在单机环境下,这些等待往往只是导致系统变慢而已,但是在RAC环境下,如果这些等待严重了,后果要严重的多。在RAC环境下,除了INITRANS外,FREELISTS ,FREELIST GROUPS,PCTFREE等参数都是需要关注的,如果设置不合理,都可能导致严重的性能问题。
今 天没什么事,和小吴聊了会天,随便在楼下吃了点午饭,想想下午也没什么事了,就跑到旁边的游泳池游了会泳。由于是下午,泳池里没几个人,水也挺干净,不过 少了养眼的漂亮妹妹,游起来也觉得比较枯燥。刚游了两个来回就觉得有点困了,于是在躺椅上眯了一会。醒来一看,已经是下午4点多钟了,急忙换好衣服,一边拿着棉签抠着耳朵,一边拿出手机看了看了一下,发现有10个未接电话。
我还没来得及查看哪来的电话,又有一个电话打了进来。电话是小吴打来的,他们上午的时候发现了一个索引的ITL等待十分严重,于是趁着中午业务比较少,对这个索引做了一个在线重建(REBUILD ONLINE),两点多钟的时候,他们发现业务有点慢,怕是这个索引重建引起的,于是就杀掉了做REBUILD索引的进程。没想到杀掉进程后麻烦就大了,有一个业务不停的报ORA-8106,他们判断这个报错可能和中断的REBUILD ONLINE有关,于是想再次重建这个索引,可是一做REBUILD ONLINE,就报ORA-8104错误,这个索引无法REBUILD了,而且想删除掉后重建也不行了。这个错误已经持续了快3个小时了,目前他们让前台暂时手工处理那个业务,等修复后补录数据。
ORA-8104是一个十分常见的错误,总有一些心急的DBA半路杀掉正在做REBUILD ONLINE的会话,而每次杀掉REBUILD ONLINE的会话,都会经历一个噩梦。由于在做索引在线重建的时候,可能相关的表还在变化,Oracle需要记录这个索引的相关变化,因此Oracle会创建一张临时表来记录这些变化,等索引重建完成后再删除这张临时表,这张临时表的名字为SYS_JOURNAL_<INDEX的OBJECT_ID>。REBUILD ONLINE刚刚开始的时候就会去创建这张日志表,但是如果创建日志表的时候,发现这张表已经存在了,就可能会报ORA-8104,并无法继续做REBUILD ONLIE(普通的REBUILD会检查索引的FLAG标志和这张表,如果冲突,也会失败)。如果REBUILD ONLINE被中途杀掉了,那么这张表和IND$中的FLAGS不会被自动清除,必须由SMON来清除。而SMON每个小时会进行一次类似的清除工作,SMON做清除前首先要锁住日志表,如果这个索引相关的表还在变化,那么SMON可能无法锁住这张表,如果SMON锁表失败,就会放弃这次清理工作,等一个小时后再来清理。这样一来,在业务较为繁忙的生产系统上,可能SMON永远都没有机会清除这张日志表。我就曾经碰到一个客户,这个错误持续了3、4天才恢复。
在ORACLE 10G中,Oracle提供了一个存储过程来代替SMON做手工清除(DBMS_REPAIR.ONLINE_INDEX_CLEAN),只要不停的重复执行这个存储过程,就可能清除失败的索引。不过如果是9i的数据库就很麻烦了。去年我曾经碰到一个客户,他们也是做了核小吴他们类似的操作,导致系统出现故障,没办法重启了数据库都没有解决问题。后来我建议他们手工清除了日志表,并且修改了索引的FLAGS。手工解决这个问题分为两个步骤:
1、手工删除日志表:
首先找到这个索引的OBJECT_ID:
Select object_id from dba_objects where owner=<owner> and object_name=<index name>;
找到OBJECT_ID后,就可以知道表的名字了(SYS_JOURNAL_<OBJECT_ID>),直接DROP这张表。不过如果这张表上的DML比较频繁,DROP操作可能不会一次成功,需要不停的重试。
2、手工修改IND$:
UPDATE IND$ SET FLAGS=FLAGS-512 WHERE OBJ#=<OBJECT_ID>;
手工清理要十分小心,一旦出错会导致数据字典错误。还有一种稳妥的办法是把应用停了,然后重启数据库,这样相关表上没有了DML操作,很快SMON就会完成自动清理。
幸运的是小吴他们是10g 的数据库,不需要冒险去手工修改数据字典表,使用哪个存储过程很快就把索引清理了,并且重新做了一次REBUILD ONLINE。事后小吴又打电话过来表示感谢,他也十分感慨,原来重建索引都有那么大的风险,做DBA真的会越做胆子越小。
处理完小吴的事情,我才发现自己在家门口站了好半天了,刚才在电话里指导小吴操作,连房门都没打开。回到家后收了收电子邮件,老万他们那里的STATSPACK报告已经发过来了。我打开看了看,今天可能是周六,业务量不大,IO负载不重,系统的各项指标都很好。不过下周系统能有什么样的表现就不好说了,我预感,这个系统快出问题了。
参考至:http://www.oraclefans.cn/forum/showblog.jsp?rootid=14720
如有错误,欢迎指正
邮箱:czmcj@163.com