返回列表 发帖

[医院杂谈] 有人执行了一下角色授权,全院都卡死了

本帖最后由 yonker 于 2018-3-28 09:48 编辑

某用户环境,昨天中午,有人执行了一次角色授权相关的操作,不但该操作“卡死”了,而且其他所有工作站的操作都卡死了,感觉整个系统都Hang住了。
最初,技术人员跟踪到当时的操作执行了一个存储过程,怀疑是过程中可能出现了级联调用,更新一张表的数据行时可能阻塞了,没有怀疑到授权语句上。
实际上,该过程执行时,会发生数百个对象(大量是表)的授权,正是由于这一系列对象的授权操作导致了整个系统Hang住了。
如果不清楚这方面的原理,可能需要走不少的弯路,也许要查很久才能将原因定位到授权操作上。

为什么授权操作有这么大的杀伤力?

很多年以前,我就从一些资料上知道了这个知识点,但并没有太在意,直到有一次用户环境发生了相关的问题,然后,反复查阅资料后,才从原理上更深刻地理解了导致并发操作卡死的原因。

记得很早以前就听到技术人员说过,如果在业务高峰期进行角色授权,操作会很卡。
后来,经过分析,开发人员对角色授权进行了一些优化,主要是减少授权执行次数。

再后来,有一次升级后,技术人员反馈:只要一执行角色授权,就会很卡,有时要几分钟才能执行完成。
通过分析代码的变动情况,发现是增加了一张表“业务消息清单”及关联的几张表的授权,而这些表在医护工作站的定时刷新SQL中被高频执行,再加上并发量大,所以,就出现了这种结果。
在查询各种资料后发现,这个问题无法完全避免,后来,我们在授权操作前给出了警告性提示,建议不要在业务高峰期执行。


下面,我们通过三个“为什么”来一层一层的分析其中的原理。

1.为什么对表进行授权,会影响并发环境下的操作卡死呢?

首先,我们来看一个授权操作执行期间的AWR报告截图

awr_grant.jpg
2018-3-28 09:35


这三个严重的等待事件都是跟SQL重新解析相关的。
授权会引起对象相关的SQL重新解析,我们通过几条简单的语句来验证一下:
Sql> s elect count(*) from zlsystems;

执行之后,在共享池中可以查到该SQL语句
S elect Sql_Text, First_Load_Time, Last_Load_Time, Last_Active_Time
From V$sqlarea
Where Sql_Text Like 's elect count(*) from zlsystems%';

执行下面的授权
Sql> grant s elect on zlsystems to zlhis;
再次执行前一SQL,发现共享池中找不到任何与对象“zlsystems”相关的查询语句了。

也就是说,凡是涉及授权对象“zlsystems”的 SQL语句都被清除了,需要重新硬解析一次。
可以想象,如果是对“人员表”、“部门表”这类在SQL大量使用的公共基础表进行授权,将会导致一大批SQL重新硬解析。
我们知道,硬解析是非常消耗CPU资源的,并且,大量并发操作进行硬解析,很容易引发内存锁的争用与阻塞,从而表现为前台操作卡死。


2.为什么授权操作会引发相关SQL重新解析呢?

因为Grant和Revoke操作是DDL语句,凡是DDL语句执行时,就需要在内存中获取该对象的独占排它锁,也就是授权语句期间不允许其他会话修改表的结构等并发的排它操作。
这个可以理解,如果不这样做,授权执行时,如果有人把表删除了,将会导致什么后果。

DDL执行后就会修改对象的LAST_DDL_TIME字段的值。
Oracle官方文档《Oracle Database Reference》中对LAST_DDL_TIME字段的含义解释,明确说明了GRANT和REVOKE会更新LAST_DDL_TIME的值。
Timestamp for the last modification of the object resulting from a DDL statement (including grants and revokes)

通过下面的SQL,我们可以看到,执行授权后,对象的LAST_DDL_TIME字段值确实变化了。
S elect Owner, Object_Name, Object_Type, Last_Ddl_Time, Status
From Dba_Objects Where Object_Name = 'ZLSYSTEMS'


3.为什么授权操作会修改LAST_DDL_TIME的值呢?
关于这一点,我也是百思不得其解,多次查阅相关资料,没有得到一个确切的解释。
MOS上在文档444560.1有一些简单的说明:
How are library cache pins and library load lock related?
只描述了授权会导致库缓存的并发争用,以及会修改LAST_DDL_TIME的值,未说明原因。
并且,建议:
对于经常使用存储的PL / SQL依赖的数据库对象修改,授予或撤销特权时要非常小心。事实上,解决这个问题主要取决于应用项目和系统维护实践。

推测,可能是最初Oracle在设计时,把Grant和Revoke统一归入了DDL语句,既然是DDL语句,执行了就要修改数据字典中LAST_DDL_TIME字段的值,那么依赖于这个对象的SQL,在DDL操作之后,就应该重新解析。

估计是统一原则和简化处理导致的这个结果,如果做精细化的设计,是不是可以把Grant和Revoke当成是一种特殊的DDL操作,不去更新LAST_DDL_TIME字段的值呢?因为它不像修改字段那样,会引起表结构的变化。
这样就不会导致相关的SQL执行计划失效了,也就不会导致大量的 SQL重新硬解析了。

当然,这只是一种推测,或许Oracle这样设计时有更多的考虑,只是我们不了解。例如:假设有数十万条SQL的执行计划在共享池中,以及对象间各种依赖关系,如果不让SQL失效,权限变化后,重新执行时,可能有更高的代价。也许Oracle的设计人员也是权衡之后采用了这一策略,它认为对象授权操作不应该发生在业务运行期间,是一个低频的维护任务。


额外的用法
对象授权会使该对象相关的SQL重新解析执行计划,利用这一特性,我们可以用来解决某些情况下的性能问题,通过它来刷新特定SQL的执行计划。
由于帖子限制了字数,这部分请查阅公众号文章

通过这个案例,我们清楚了授权操作导致并发操作卡死的原理。
重要的是,注意:
1.        开发方面,需从业务逻辑上分析,减少不必要的对象授权,以及减少执行次数。
这个案例引发的问题就是因为过程中有一个逻辑错误,执行了本来不应该执行的大量对象的授权语句。
2.        不要在业务高峰期执行授权操作。
想象一下业务繁忙期间,清空共享池缓存的执行计划的后果吧。
3.        即使在业务低峰期执行,也要保证能够快速完成。
前面介绍的这个案例,虽然是在中午1点至2点期间执行的,但是从数据库压力来看,由于存在各种SQL性能问题,很多条耗用CPU高达99%的SQL在执行大表全表扫描,整个系统压力非常大。此时,大量游标重新硬解析,CPU完全忙不过来,进一步导致latch的争用和阻塞,系统就容易卡死。
AWR上可以看到,当时用户环境的性能状态就像一个危重病人,突然来一个重感冒,病人一下就倒下了。如果是一个健康的病人,感冒的影响就不会这么大,也许就是卡一两分钟就挺过去了,不会引发全院的卡死。

----------------------------------------------------------------------------------------------------------------
如果你对医信系统的性能优化技术感兴趣,欢迎关注这个微信公众号:医信系统性能优化,一个纯技术交流的公众号。
收藏 0
医性系统性能优化交流。
http://blog.itpub.net/117319/

这么厉害的角色
自我就是自我

TOP

这得是什么角色啊。
走在国产医疗设备的道路上。

TOP

返回列表