动态增加CPU带来的数据库重启风险--技术人生系列第四十六期-我和数据中心的故事
线上的生产系统,因为一些原因,时不时会出现一些意想不到的性能问题,当紧急问题出现时,我们如果无法立即解决,有时通过调整系统硬件资源是一种快速有效的解决应急方式,确保系统能正常运行后,再做根因分析,从根本上解决问题 ;
最近老K遇到了客户的应急策略导致了数据库重启的案例,翻了翻邮件历史,竟是N年前就已经遇到过了;问题本身不算复杂,然而却可以造成数据库直接重启,影响生产,这里特别分享出来,给大家一个风险提示 ;
今天老K为大家分享的这么一个案例 :
某系统增加新业务,在数据库正常运行,原业务不停业的情况下,在数据库上完成版本投产的相关操作,结果投产过程偏慢,应用维护团队担心投产窗口无法正常完成投产动作,需要系统维护团队帮助分析加快投产动作;然而,在针对这一问题应急之后,数据库却发生了重启,反而更严重的影响了投产动作。
那么,是谁造成了重启呢?我们应急过程中做出了什么错误的判断吗?我们需要怎样避免再次发生类似的悲剧呢?本次分享从最后的故障出发,给大家带来一次隐蔽的应急深坑,同时文末也给出我们总结的一些应急手段引入新问题的经验教训!
Oracle 数据库问题,还是找不到原因?
不妨找中亦科技试试,我们将尽最大努力为您找到导致故障和性能问题的根本原因。
问题来了
周六,正是某个客户的投产日,投产时间为晚间19:30开始,持续到翌日早上九点,老K负责现场支持任务。
投产系统多,七点半刚过,各种咨询接踵而至,老K正忙的不亦乐乎,电话过来了:"老K,快来这边帮帮忙看下,应用维护团队说投产动作比较慢,看看有没有什么办法加快下速度?"类似的电话接了不少,问题总要一个一个处理,但是刚刚电话涉及的系统还是比较重要的系统,投产窗口有限,肯定是需要更重视一些,于是答应处理完手边的这个问题紧接着下一个去看看这个系统;
但是,几分钟后电话再次响起:"老K,不好了,这个系统数据库重启了!快来看看吧!" 这就尴尬了,难道是因为我没有及时响应他们的问题,系统就撑不住重启了?如果真是这样,那这罪过可就大了!赶紧去到事故现场来处理这个问题。
简单分析,简单结论
1
明确情况
问题现场,各路同事都在讨论着这事。 这个 系统是一套两节点的 RAC 数据库,数据库版本为 11g ,这次重启实际上是数据库两个节点的实例都发生了重启;
首先看看 alert 日志里有没有什么信息显示:
很显然,数据库在 19:41 时被 ASMB 进程给终止了,在终止前产生了一些 trace 文件; ASMB 进程是什么进程呢?顾名思义, ASMB 进程就是数据库实例与 ASM 实例进行交互的进程,从这里的报错也可以到, ASMB 已经无法连接到 ASM 实例了;
一般来说,出现这种情况,通常的可能是 CRS 发生了重启,然后这里我们通过 ps -ef 查看 CRS 关键进程发现并未发生过重启;这样看来与 ASM 有关;于是我们再看 ASM 的 alert 日志,关键部分如下 :
可以看到 ASM 实例发生了重启,而重启的原因则是:
参数 parallel_max_servers 被设置为 4505 ,而这个参数值是不合法的,该值的设定范围应该是 到 3600 之间;在发现了这个错误后 CKPT 进程终止了 ASM 实例。
似乎问题就很简单了, parallel_max_servers 被置为 4505 , ASM 实例终止,进而导致 DB 实例终止 。 那么,这里我们就需要搞清楚几个问题:
parallel_max_servers 设置范围是 到 3600 ,这是硬性规定吗?还是说是因为在设定某些值之后导致的?
参数设定错误通常报错即可,为何这里会导致 ASM 实例重启呢?
修改参数的操作来自于谁呢?与他们的应急操作是否有关系呢?
2
分别突破
1) parallel_max_servers 参数的限定
对于这种硬性设定,我们不妨先查一下官方手册,查看该参数是否有上限值:
可以看到,该值设定的上限就是 3600 ;
2) 参数修改为什么导致 ASM 实例重启呢 ?
正常情况下,如果我们在数据库中的 修改 参数, 修改失败直接抛错即可,显然并不会导致数据库实例的重启;
而这里发生的重启,我们可以看到导致重启的进程是 CKPT 进程:
我们可以认为, CKPT 进程是数据库实例的关键进程,当它遇到错误时,它直接终止了整个实例;
3) 谁触发了 ASM 实例上参数的改变?
通常来说,修改(调大)parallel_max_servers参数的目的是允许系统启动更多的并行进程;而对于ASM实例来说,修改这个参数本身似乎并没有太大的意义,毕竟ASM实例都不会open,基本只服务于Database(可能还有一些监控/管理类的语句);那么谁会来修改ASM实例上的参数呢?难道是应急的时候错把ASM实例当做Database了?
看到这里,不妨思考一下?原因其实很简单 …..
3
真相其实很简单
我们在来看看ASM的日志,相关的信息值得思考:
我们可以看到,在参数调整之前,日志中实例发现了CPU count变成了228,紧接着系统就调整了parallel_max_servers值的大小,随后报错宕实例;
这里我们首先需要确认,CPU数的调整会引起parallel_max_servers参数的调整吗?我们再来看看这个参数的相关属性,其默认值确实就是与CPU_COUNT有关的,而且参数本身就是可以动态调整的;
很显然,这里Oracle的代码中似乎在系统调整CPU进而导致parallel_max_servers参数的动态调整时,忘记了该考虑parallel_max_servers还有一个3600的上限,这应该定义为一个bug;我们上MOS核查也很快能找到这个bug:
问题发生在11.2.0.2和11.2.0.3的版本上,在11.2.0.4版本上已经修复;
原来,在版本投产过程中应用维护团队发现相关任务的速度达不到预期时,他们就与相关系统负责人沟通,希望通过增加资源的方式来加快其任务的执行速度,于是系统负责人在请示领导后给系统动态增加了内存和CPU(使用的IBM小机,可以动态调整硬件资源),并准备在投产完成后再收回资源,然而在调整CPU的过程中,出现了上述错误,导致数据库重启,投产动作也受到影响 !
3
那些真相以外的真相
遇到了问题,要想怎么规避这样的问题,怎么样总结一套发现类似问题的方法,养成这样的良好习惯可以帮助我思考的更多更深;
--老K
① 若干疑问
看着问题是得到了圆满的分析,然而还有不少疑惑萦绕在我的心头,也是客户所关注的,所以这里先来解答这些问题;
1. 为什么 Database 实例没有因为 parallel_max_servers 的参数调整而直接重启呢?
这里我们可以再回过头来看 database instance 的 alert 日志信息 :
同样也看到了 CPUcount 的变化,调整到了 228 ;但是并没有出现 parallel_max_servers 参数的动态调整,其实这里主要的原因是,在 Database 实例中,我们已经在参数文件中指定了 parallel_max_servers 参数的大小了,在指定了参数的情况下,系统不再根据 CPU 数动态调整 parallel_max_servers 参数的大小;
2. 为什么 ASM 实例启动时,没有再因为该参数而无法启动?
在 ASM 再次重启的时候是怎么计算 parallel_max_servers 参数值的呢?实际上我们搜索 asm 的 alert 日志发现启动实例是并没有设定 parallel_max_servers 参数;而我们手动设定 asm 的 parallel_max_servers 参数值也会报错:
我们发现,在 ASM 实例中压根就无法设置这个 parallel_max_servers 参数,哪怕是设定到 spfile 中也不允许;实际上在启动时不计算、不检查这个参数也就合乎情理了;
3. 刚刚调整上去的 CPU 我再调回来还会宕机吗?
我们从原理上看,这里数据库并不关心 CPU count 是变大还是变小,一次是变十个还是变一个,它都会重新来计算 parallel_max_servers 参数的值,那么显然,这里如果再次调整 CPU count ,如果不是将 CPU count 调整到足够小,那么再次调整只要调整后的 CPU count 数计算 parallel_max_servers 参数的值大于 3600 ,问题将再次出现 !
4. 如何预防这样的问题呢?
已经知道了这是 bug ,那么对应的打上相应的补丁一定是根本的解决之道;
而数据中心 11.2.0.3 版本的系统还非常多,而动态调整 CPU 也是常见动作,统一安装补丁也需要时间,同时还需要考虑时间窗口,肯定需要一定的周期;那么在此之前我们是否还有临时的预防之策呢?
根据 bug 文档描述的 workaround 是手动指定 parallel_max_servers 参数为小于 3600 的值,不过我们上面的核查能很明白的看到, ASM 实例无法调整该参数,看来官方文档没有考虑 ASM 实例的情况,也有纰漏,那么我们该怎么办呢 ?
该如何是好呢?如果是你,你会考虑一些什么来规避这类问题呢?
② 更多思考,更多发现
要从根本上来回答上面的这些问题,其实需要了解数据库到底是如何计算 parallel_max_servers 参数的;实际上我们可以看看如下描述:
从上述描述我们可以看到影响参数的若干变量,包括:
PARALLEL_THREADS_PER_CPU :该参数基于操作系统,通常是 2;
CPU_COUNT :该参数基于数据库的实际 CPU 数,但是可以调整;
concurrent_parallel_users :该参数基于实例的内存管理模式配置,默认系数为 1/2/4 不等;
PROCESSES :该参数值来实例启动时的 PROCESSES 参数;
可以看到, parallel_max_servers 的参数值通过两方面的计算取 min 值,也就是取自 min(parallel_threads_per_cpu*cpu_count*concurrent_parallel_users * 5,processes-15) ;这两个方面来看,其中一部分确实与 CPU 有关,而另一方面似乎只与 processes 有关,我们再查 11.2 的 references:
processes 参数的值默认是 100 ;如果没有人调整过 ASM 实例的 process 参数的值的话,那么这里计算 parallel_max_servers 的值应该是 min( PARALLEL_THREADS_PER_CPU * CPU_COUNT * concurrent_parallel_users * 5,100-15 ) , 这里即使前面计算的值再大,这里计算出来的值也只应该是 85 而已 ,也就远不会超过 3600 ;
难道 真的是又有谁修改了 ASM 实例的 PROCESSES 参数值吗?还是官方的 reference 是错误的呢?如果是你,你会怎么进一步核查这个参数呢?
③ 最后一部分真相
遇到这样的情况,我们首先检查并没有发现两个节点 alert 日志存在修改记录;那么难道 reference 真的有误?事实上我们看的是 11.2 的 reference ,当前的 11.2.0.3 版本发布时间在 reference 之后很久,我们不妨再通过 MOS 核查确认 processes 值默认值的计算方法:
在 MOS 文档 " 升级到 11.2.0.3/11.2.04 Grid/ASM 所需要知道的事 " 中指出, ASM 的 processes 参数的默认值计算是" available CPU cores * 80 + 40 ",当然,这里 processes 参数不会随着 CPU 数动态改变,只会在实例启动时计算;
我们截取之前时段 AWR 报告中拿到的 CPU cores 数 :
那么这里, parallel_max_servers 的值计算就是:
min(PARALLEL_THREADS_PER_CPU*CPU_COUNT*concurrent_parallel_users*5,last_CPU_cores*80+40-15)
代入实际值即为:
min ( 2*228*4*5, 56*80+40-15 )即
后续针对所有11.2.0.3,打上对应的补丁,避免再次中招;
在补丁打上之前,在database实例确保手动设置parallel_max_servers为小于3600的值,在ASM实例设置processes值为小于3600的值;
如无必要,我们在没有充足理由调整系统CPU/内存的时候,不要轻易调整系统资源 ;
⑤ 真的需要调整CPU吗?
后续再了解整个情况后,我们再回过头看系统投产过程中所谓慢的情况,其实只是大量新插入记录的表未及时收集统计信息导致执行计划不够好而造成的执行缓慢;虽然当时 CPU 使用率相较之前确实偏高,但是在这种情况下,不通过收集统计信息,重新执行语句的情况下,通过增加 CPU/ 内存这两种常用的应急手段实际上毫无帮助;在本次 CASE 中,增加 CPU 的动作反而造成了数据库宕机!
我们遭遇了什么
我们再来回顾整个事情的前后,以及数据库本身的动作:
应用团队认为 SQL 执行缓慢,系统 CPU 使用率较高,建议扩容 CPU/ 内存;
在没有及时分析问题根本原因的情况下,满足应用团队需求扩容系统 CPU/ 内存,其中 CPU 数扩容到 228 ;
数据库发现 CPU 扩容到 228 , Database 实例因为手动设置了 parallel_max_servers 参数,不再调整该参数; ASM 实例没有设置 parallel_max_servers 参数,于是在代码中调整了 parallel_max_servers 参数;
调整的目标值是 min(PARALLEL_THREADS_PER_CPU*CPU_COUNT*concurrent_parallel_users*5,last_CPU_cores*80+40-15) ,即 min ( 2*228*4*5, 56*80+40-15 )即 min(9120,4505)=4505 ;
CKPT 进程在设置该值之前,因为遭遇 bug ,未能校验 4505 值是否超过了 parallel_max_servers 的硬性上限值 3600 ;
CKPT 进程在设置 parallel_max_servers 值为 4505 时,遭遇错误 ora-00068 ; CKPT 作为系统关键进程,遇到无法完成的错误,终止 ASM 实例;
ASM 实例终止后, Database 实例无法访问 ASM ,同时终止;
CRS 发现 Database 实例和 ASM 实例都终止,于是恢复 ASM 实例和 Database 实例;
在再次启动 ASM 实例时,不需要考虑 parallel_max_servers 参数, ASM 实例正常启动, Database 实例正常启动;整个重启完成。
从这里看,一个看似有益无害、多多益善的决定,让一套系统发生了重启,所以,应急需谨慎,最好做到对症下药,避免出现意想不到的结果。
风险提示
在数据库版本为 11.2.0.2/11.2.0.3 的情况下,对于数据库实例,建议手动设置 parallel_max_servers 参数,对于 ASM 实例,建议手动设置 processes 值为小于 3600 的值,避免出现 CPU 数量过高的情况下,动态调整 CPU 数造成的 Database/ASM 实例重启的情况。
本文转载于中亦安图