Oracle后台专家解决library cache锁争用的终极武器

简介:
20160628053507414.jpg

 

11月19日,云汝网络科技合伙人宋日杰(Roger Song)在“DBA+东北群”进行了一场关于“使用Hotcopy缓解 library cache: mutex X 的争用”的线上主题分享。小编特别整理出其中精华内容,供大家学习交流。

 

 

嘉宾简介  

 

  • DBA+原创专家团成员

  • 超过13年IT及Oracle数据库经验

  • 历任中国联通系统工程师、维布络信息科技Oracle ERP管理顾问

  • 2008年加入Oracle全球技术支持(GCS),专注性能分析

  • 2012年协助建立Oracle集成系统中国支持团队(EEST),任首席技术支持工程师

  • 现任云汝网络科技(上海)有限公司合伙人,正在转型互联网解决方案

  • 对Oracle数据库全线产品模块有全面的深入了解,曾作为DSI讲师为Oracle国内原厂技术团队提供培训

  • 擅长Oracle原理,性能诊断,及复杂环境下的对问题的隔离与分析

 

演讲实录  

 

今天来给大家分享一个Oracle使用中的小技巧。 当某条SQL语句或者对象被反复访问,过多的软解析可能会造成大量的“library cache:mutex X”争用,有什么样的方法处理此类问题呢?这是个头疼的问题。 今天的话题,就是介绍如何利用hotcopy来缓解library cache中的热点争用。在oracle 11g中,“library cache:mutex X”是个有点特殊的mutex,因为它存在于几个不同的位置,需要进一步的信息进行分辨。这里还会简单描述library cache的结构,理解library cache对认识oracle是非常重要的。

 

20160628053519455.jpg

 

解决性能问题没有特别的方法,主要靠对数据库整体结构的准确理解。了解各种等待发生的原因,以及其在整个体系中的位置,才能做出正确的判断。 所以我会先大致介绍下10G和11G对library cache这个结构的保护方式。 这样你就会理解这个问题发生的可能原因。 然后,我们来尝试通过创建对象的hotcopy来缓解这类争用。

 

其中个别内容是我自己摸索和猜测出来的,难免有错误和不准确的地方,欢迎大家指正。

 

20160628053530309.jpg

 

也许不是所有的同学都清楚library cache的结构,先在这里简单描述一下。

 

我们知道,当编译过的SQL已经存在于library cache中的时候,oracle会进行软解析,重用这个结构。 那么oracle如何知道是否已经存在可用的SQL呢? 是通过如图中的Hash结构。所有已经编译过的library cache object会被组织在一个hash结构中。 当进行搜索时, Oracle会对SQL语句进行hash计算,根据结果找到相应的hash bucket,比如bucket 6, 然后再搜索链接在这个bucket上的所有handle,这里你可以认为每个handle对应唯一的SQL文本。

 

一种情况,如果“Handle X” 正好是要找的SQL,则会向内搜索“Handle X”包含的child,寻找可用的cursor。 如果有完全符合条件的可用child,则重用,完成软解析。 如果没有,则创建一个新的child,完成一次硬解析。

 

另一种情况,如果没有找到符合条件的handle,说明SQL 文本是新的,则需要创建一个新的handle和第一个child。 同样完成一次硬解析。

 

20160628053539809.jpg

 

那么为什么要在这个结构上进行锁保护呢?什么操作需要加锁?

 

一个很简单的例子,如果没有锁的话,假设process 1和process 2同时执行同一个SQL,当他们都没有找到匹配的Handle时,有可能重复添加针对同一SQL的Handle Y。

 

同样的,当两个进程同时搜索Handle X下是否有匹配的Child时,也有可能在此Handle下,添加两个相同的Child。

 

因此,为了避免这种情况发生,对此结构的锁是必要地。 由于这些操作都是快速的内存操作,此类锁不宜采用复杂的结构,没有共享模式和独占模式之分。 所以,只有一个进程可以对某个结构进行搜索和修改。也就是说,即使是软解析,在结构内进行搜索时,也是串行的。

 

在10g和11g,library cache锁的粒度是不同的。

 

20160628053546743.jpg

 

10G的时候,保护这个结构的是 latch: library cache。 这个latch的默认数量为大于CPU Count的最小质数,而默认最小的bucket个数为509,这就意味着每个latch要保护多个Hash Buckets以及相应的handle。 也就是说,对于一组bucket和handle,每次只有一个进程可以搜索修改。因此,这个latch经常成为热点。

 

20160628053553504.jpg

 

11G以后,此latch被替换为library cache: mutex X。锁的粒度变小,每个bucket和每个handle都被一个单独的mutex保护,大幅度减小了争用

 

然而,从latch名字本身,我们区分不出等待是发生在hash bucket上,还是handle上。需要通过p1来进行分辨。

 

20160628053601311.jpg

 

如果P1是bucket的编号,通常比较小。 如果下面查询返回结果,说明争用发生在hash bucket上。 返回结果为链接在此bucket上的对象。

 

select KGLNAHSH, substr(KGLNAOBJ, 1, 30) Name,

         KGLHDNSP Namespace, KGLNAHSV Hash

from x$kglob where KGLHDBID = &p1;

 

这种情况下,锁是在bucket上,即对handle进行访问操作时发生的。

 

实际上,这种情况并不常见,因为每个bucket上链接到的handle通常不会很多。 为了提高hash表的效率,当bucket上平均handle数量超过2时,oracle会将bucket的个数翻倍,重建hash表,用空间交换时间。 在链不长的情况下,一般handle上会先遇到瓶颈。

 

20160628053609349.jpg

 

更常见的争用位置,是在 handle上,这说明某个特定cursor或者object被搜索时发生争用。

 

如果下面查询返回结果,则说明是这种等待。返回结果为handle所对应的对象。 可能是cursor,也可能是object

 

    select kglnaown, kglnaobj

      from x$kglob

     where kglnahsh = &p1;

 

20160628053615897.jpg

 

明白了大致的原理之后,就可以想象到这个mutex争用的可能原因。

 

首先,它有可能是硬解析造成的。 当发生硬解析时,无论要创建一个新的handle,还是要创建一个新的Child,都需要额外大量的时间来完成,相应地,对这个mutex的持有时间会明显变长,造成争用。

 

由于硬解析会向shared pool申请大量的内存,因此这种情况会伴随一系列其他等待如 latch: shared pool, library cache load lock。 解决办法就是分析硬解析过多,或者version count过高的问题。

 

还有一种情况,就是OS资源不足,特别是CPU资源紧张。 这种情况下,oracle进程无法获取足够的资源去完成相应的工作,无法及时释放此mutex,造成请求的堆积。

 

20160628053625196.jpg

 

再有,就是某个持有者长时间不释放此mutex,造成请求的积压。这是不正常的, 需要对mutex持有者的当前状态进行分析。 Errorstack, process dump。 如果持有者被其他进程阻塞,则通过hanganalyze等工具继续查找最终持有者。

 

另外,个别Oracle bug也会造成此类问题。

 

如果,以上原因都被排除掉,那么,对某热点SQL或热点object的频繁访问,也会造成此问题。 也就是我们之前说过的,哪怕是软解析,也会在查找library cache结构的时候串行。 这类问题比较让人头疼,一方面,SQL语句来自业务,我们不能人为减少SQL的执行次数和效率。 另一方面,如果SQL的文本相同,就一定会被hash到一个特定的handle上,这个是无法改变的。从前遇到这类问题非常让人头疼,一般可能的解决办法见下一页。

 

20160628053633792.jpg

 

那么接下来该怎么办呢?

 

首先,可以查下是否有连接风暴。 连接风暴可能造成某些PLSQL对象的频繁执行,是造成此类问题的常见原因之一。

 

Version count如果很高,也会引发这个问题,因为找到handle以后, 对child的搜索是用遍历的方式,如果version很高child很多,则每次搜索的时间很可能会变长。会造成对handle上的锁,持有的时间也变长。所以,这也是一个可能的原因。

 

另外,和开发联系,研究SQL或某个对象如此高的并发是否正常的。 如果SQL来自不同的模块,则可以试着修改来自不同位置的SQL文本(比如加空格,或无用注释),使他们hash到不同的handle上。

 

增加session_cache_cursors是个可能的尝试,如果cursor被cache住,child的位置会被保存在pga中,那么查找child的速度会明显加快,对Mutex的持有时间也会变小。

 

如果以上方法均不适用,我们最后可以尝试创建hotcopy来改善此问题。

 

20160628053642570.jpg

 

对于资源争用的问题,解决办法只有两个,要么减小需求,要么增加资源数量。而hotcopy的原理,实际就是在hash SQL文本的时候,加上PID的部分。 这样,哪怕执行相同的SQL文本,不同进程就有可能会被hash到不同的handle上。 这实际增加了资源的数量。当然,copy的数量是可控的。

 

制作了hotcopy以后,相同的对象和child可能多次出现在library cache的不同位置,造成了一部分空间浪费,但却可以解决library cache中热点对象或热点cursor的争用。也是一种用空间换时间的办法。

 

20160628053651965.jpg

 

hotcopy这个功能是通过fix 9239863和fix 9282521引入的。 11.2.0.2以上版本可以直接使用。 低版本数据库需要安装相应地补丁。 针对11.2之前版本的数据库,需要设置两个隐含参数。 slide里有详细的描述。针对cursor 和 object对应不同的方法。

 

20160628053658173.jpg

 

11.2之后,可以通过标准API进行设置。

 

20160628053706202.jpg

 

下面我们用一个简单的实验,查看到底发生了什么。 在试验中,我模拟了一个场景,大量session反复执行某一个SQL。

 

从查询结果来看,内存中ASH捕捉到的等待事件, library cache: mutex X 占据了很高的比例。

 

20160628053713350.jpg

 

AWR报告反应了类似的内容。等待进入了top 1。

 

20160628053721638.jpg

 

经查询,library cache: mutex X等待的P1非常集中。

 

20160628053728506.jpg

 

经过验证,P1的值指向该条cursor的handle。 其实你看这个数字的长度,就知道不可能是hash bucket。 这个handle所在的bucket是14503。

 

20160628053738292.jpg

 

接下来, 我们用之前介绍的方法来激活基于此cursor的hotcopy。

 

由于我的实验机器CPU太少,所以 我提前设置了隐含参数”_kgl_hot_object_copies”=4来指定copy的个数。

 

在这里我们使用了11.2提供的API进行hotcopy的实施,针对cursor,需要完整的hash,可以从X$KGLOB.KGLNAHSV中获取。

 

然后再次提交同样的负载。

 

20160628053747397.jpg

 

这一次,我们看到,ASH捕捉到的mutex等待开始分散到多个P1上,总数量在变少(忽略32127143,这是第一次试验遗留的结果)。 通过进一步验证能够确认,每个handle的内容都是这个cursor。

 

20160628053755400.jpg

 

20160628053802536.jpg

 

20160628053810748.jpg

 

这里我也注意到14503这个hash bucket上的争用增加,如果你还记得之前的输出,这其实是修改前那个cursor所在的hash bucket。 我猜测即使做了hotcopy,请求仍旧会经由这个bucket跳转。由于handle上的请求处理速度变快了, bucket上的争用开始显现,从library cache的结构来看,这些是合理的。

 

20160628053817189.jpg

 

从library cache dump 中验证,的确增加额外4个handle,相同的对象名。

 

20160628053824157.jpg

 

在新生成的AWR报告中, library cache: mutex X 等待时间下降了50%, 从1882秒,下降到942秒.  由此可以验证,针对纯粹的library cache的热点争用,hotcopy是可以进行缓解的。

 

20160628053831403.jpg

 

最后做一个简单地小结。

 

首先,从library cache的结构看, 即使是软解析,对child和handle的搜索,也是串行的。 虽然这个操作速度很快,但仍会造成热点争用。

 

另外,造成library cache: mutex X争用的原因很多,只有纯粹的热点争用才需要用hotcopy来解决。

 

最后,以我的经验来看,解决数据库性能问题,没有固定的套路。 Oracle是个极其庞大复杂的体系,几乎不可能通过几个简单的等待事件推测出根本原因,不会有这样的“神”。 正确的分析方法是,尽可能多的收集证据,然后利用自己的知识和对整体结构的理解,推导出一个“故事”把所有证据串联起来,有了“故事”以后,再收集更多的证据,反过来来验证故事的正确性。


本文来自云栖社区合作伙伴"DBAplus",原文发布时间:2015-11-21

目录
相关文章
|
监控 Oracle 安全
Oracle数据库用户频繁被锁问题原因排查及解决
由于应用环境下Oracle用户总是频繁被锁,经常不能执行数据库事务操作,严重影响了系统运行效率。通过问题原因分析及排查,发现了原因,在此记录一下。
4006 0
Oracle数据库用户频繁被锁问题原因排查及解决
|
1天前
|
SQL Oracle 关系型数据库
【ORACLE】 事务 | 锁 | 约束 | 权限、角色与用户管理
【ORACLE】 事务 | 锁 | 约束 | 权限、角色与用户管理
6 1
|
25天前
|
SQL Oracle 安全
Oracle数据库中的事务和锁
【4月更文挑战第19天】Oracle数据库的事务和锁是确保数据完整性和并发控制的核心机制。事务遵循ACID原则,保证操作的原子性、一致性、隔离性和持久性。通过COMMIT或ROLLBACK来管理事务更改。锁包括共享锁(读)、排他锁(写)、行级锁和表级锁,用于控制并发访问。自动锁机制在DML操作时生效,防止数据冲突。事务和锁共同维护数据库的稳定和安全。
|
4月前
|
SQL Oracle 关系型数据库
Oracle-锁解读
Oracle-锁解读
59 0
|
6月前
|
Oracle 关系型数据库 数据库
Navicat连接Oracle报错:Oracle library is not loaded
Navicat连接Oracle报错:Oracle library is not loaded
121 0
|
11月前
|
SQL Oracle 前端开发
Oracle更改数据后后台无法操作 一直加载
今天用dbutils操作Oracle,在SQL developer里面测试执行完update语句后,在后台操作,前端页面一直加载,
|
12月前
|
运维 Oracle 关系型数据库
Oracle优化02-锁和阻塞
Oracle优化02-锁和阻塞
88 0
|
SQL 存储 Oracle
Oracle事务和锁机制
Oracle事务和锁机制
92 0
|
Oracle 关系型数据库 数据库
navicat提示oracle library is not loaded
navicat提示oracle library is not loaded
3151 0
navicat提示oracle library is not loaded
|
存储 缓存 分布式计算