【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_编程语言


李华(网名:回忆未来)

云和恩墨高级技术顾问

本篇整理内容是本周四晚李华在“云和恩墨大讲堂”中授课的课件资料:故障分析 - library cache latch 竞争案例分享,如有疑问,可在文章下方留言。

背景介绍

客户的一套重要生产系统,出现了性能问题。这个问题涉及的信息如下:

月底时候数据库主机的 CPU 利用率长期在100%左右。

数据库中出现大量的 latch: library cache 竞争

系统概况


【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_02


该系统为 OLAP OLTP 混合系统,平时为交易型数据库。每个网点实时数据上传,月底会有统计类报表产生。

以下为数据库负载曲线,可以看到在月底复杂急剧上升,导致业务不能正常操作。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_python_03


【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_数据库_04

以下为故障时间点部分 AWR 截图。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_编程语言_05

从 LOAD PROFILE 看当前数据库每秒有158次的硬解析,总的解析在1082次。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_编程语言_06

这个时间点的 TOP 5 等待事件中 latch: library cache 与 kksfbc child completion 排在前列,library cachelatch 占到将近有 70%。

Latch: Oracle 用于控制内存并发的串行锁机制

共享池 latch 竞争一般导致的原因有以下集中:

  1. literal SQL 所谓的 literalSQL 就是没用使用绑定变量值的 SQL 比如 select * from enmo where id=100;
  2. 硬解析比如一个新执行的 SQL 没有在共享池中,那么就要经历一个硬解析的过程,关于过程这里就不在多讲
  3. SQL 不能共享,不能共享的原因有很多比如没有在同一个用户下面执行
  4. SQL VERSION 大量高版本 SQL 也会导致共享池的竞争
  5. 另外就是主机出现大量换页,比如在 AIX 环境下大量计算内存使用了 SWAP 会导致类似的问题
  6. 还有就是查询一些底层的视图比如 x$ksmsp 在某些版本下高并发的系统中直接查询这些视图会出现大量的 latch 竞争
  7. 还有就是 SGA 大量抖动或者模拟调整的时候也会导致此问题
  8. Oracle 各个版本上也存在相关的 BUG 会导致

根据以上几点我们去分析到底此问题出现在什么地方。

首先数据库等待事件除了 library cache latch 之后就是 kksfbc

K[Kernel]K[Kompile]S[Shared]F[Find]B[Best]C[Child]

该函数用以在软解析时找寻合适的子游标,是否该故障是由于大量 VERSION COUNT 引起呢?

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_07

从这个时间点 AWR 来看没有看到大量 version count 的SQL出现。

分析 latch 的时候 AWR 有一个非常重要的数据。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_大数据_08

从 Latch Miss Source 的数据可以看到,绝大多数都是对于 shared pool latch 的 sleeps,

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_数据库_09


【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_编程语言_10


从 AWR Sleep 来看 shared pool 排在了第一位。从调用的函数来看都是发生在硬解析这个过程中。

以下为一些常见函数的功能:

Kghfrunp: KGH: Ask client to freeunpinned space

Kghdmp : x$ksmsp is a fixed table based onkgh metadata.  The number of latch  sleeps for "kghdmp" will increaseif x$ksmsp if an installation  selectsfrom this fixed table too frequently.

kghupr1 : un-pin recreatable

kghalo             KGH: main allocation entry point

kghgex             KGH: Get a new extent

kghalf             KGH: Non-recoverably allocate afreeable chunk of memory

有很多函数这里就不一一列举。

当前现在也可以排除人为查询底层视图导致的 latch 竞争因为没有看到相关函数出现,插播一个类似的案例。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_编程语言_11


【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_大数据_12

像这种情况很明显就是有人查询了底层的视图导致的 shared pool 竞争。

从主机最早的信息来看也是没有 SWAP 竞争出现的。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_编程语言_13

SGA 没有大量的 resize 也可以排除掉由于 SGA 组件抖动引起的。

从以上信息,我们没有找到想要的结果,那么问题出现在哪里。

把上面几个原因都排除掉了,难道真是遇到 Oracle BUG 了么。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_14

有的时候分析问题会陷入一些误区,比如一个数据库出现大量的 latch 竞争导致会话飙升然后把 process 撑满,从 time mode 里面来看的话可以发现 95%都是花费在了连接上面,那么到底是大量不正常的连接(比如连接泄漏)导致了数据库出现竞争呢,还是数据库出现问题导致会话不能等了然后不停的重连导致了问题呢。

从这个库这个时间点的 time mode 可以发现 75%的 db time 都是花费在了解析上面,这也是没有问题的因为这个时间点数据库竞争就出现在解析上面,但是为什么其中有 38%的 db time 发生在解析失败上面呢,也就是总共解析的一般时间都是错误的解析。硬解析只有5%左右。

我们来看一张正常时间点的 time mode 。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_15

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_大数据_16

从这个趋势图库看到解析失败一直是跟着硬解析的次数而增加,并且每天都在上班之后开始发生。

数据库正常时间点硬解析也只有不到 5%左右,也就是硬解析没有大的变化,但是解析失败确认翻了几倍。是什么原因导致这么多的解析失败呢?另外解析失败的 SQL 是否会导致大量 latch 竞争?解析失败的 SQL 是否会在共享池中存储?怎么查询到解析失败的 SQL?

很多时候我们会有这样一个误区,既然语法错误或者对象不存在应该在语法语义检查这个步骤就挂了怎么还好存在共享吃里面呢?带着这个几个问题我们做几个简单的测试。

我们先了解下什么是解析失败的 SQL。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_17

那么怎么证明就是解析失败的 SQL 存在共享池中并且在解析的时候持有 library cache latch 呢?

做下面测试之前我们先回顾一个 Oracle 一些基本概念。

Library cache 是 shared pool 中的一块内存区域,主要作用就是缓存执行过的 SQL 语句所对应的执行计划信息等信息。当同样的 SQL 再次执行时候可以直接利用已经缓存的相关对象不需要再从头解析。

Library cache 对象句柄是以 hashtable 的方式存储的,存储方式如下图:

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_python_18

当 sql 执行时候,首先会对 sql 文本进行 hash 运算然后根据 hash 值去相关 hash bucket 中遍历,如果找到了就直接用该 sql 缓存的执行计划等,如果找不到则从头解析,并把解析后执行计划等缓存在 hash bucket 中。

下面这几张图片展示了一个 SQL 解析的过程。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_19

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_20

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_python_21

SQL 的内存结构

我们知道 SQL 语句必须至少是一个父游标一个子游标存在的,当然生产中很多情况下都是一父多子的情况。

父游标与子游标结构是一样的,区别在于 sql 文本存储在父游标对应的对象句柄中,而 sql 的执行计划等信息存储在子游标对应的库缓存对象句柄 heap 6 中。另外父游标的 heap 0 中存储着子游标的句柄地址。如果解析错误的 SQL 在共享池中存储的话那么必然要产生一个父游标然后父游标里面存储的有 SQL 文本之类的信息,但是子游标的?既然解析失败那么就没有产生执行计划。

关于 heap 0 中信息可以参考如下图:

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_22

父游标句柄对地址可以在 x$kglob 视图中查询到,KGLHDPAR=KGLHDADR 的记录为父游标

X$KGLOB

该视图定义为 [K]ernel[G]eneric [L]ibrary Cache Manager

KGLHDADR        RAW(4|8)             Address of kglhd for this object

该地址 000007FF11937C90 为 select * from enmo SQL 的父游标的句柄地址。

可以看到:

KGLOBHD0          RAW(4|8)             Address of heap 0 descriptor

KGLOBHD6        RAW(4|8)        Address of heap 6 descriptor

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_编程语言_23

上面查到的就是该 SQL 父游标的信息,父游标的 kglobhd0 的地址为 0000000075489AE8

该句柄地址记录的信息很多包含了子游标的信息。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_24

找下该 SQL 子游标的信息:

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_大数据_25

子游标 heap 6 的地址为 000000007625FBF8 句柄中存储的也就是执行计划相关的信息。

通过以上测试我们很容易找到 sql 的父游标的句柄还有子游标的句柄在内存中的地址。

下面做另外一个简单的测试解析错误的 SQL 是否有父游标还有子游标生成。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_编程语言_26

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_python_27

可以看到是可以查询到信息的,也就是有父游标的句柄为 00000000754453B8  heap 0 的地址为 0000000075485620.

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_编程语言_28

可以看到是有错误的文本信息的内存地址,但是子游标呢?

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_大数据_29

可以看到是没有子游标产生的,因为该 SQL 执行错误不会有执行计划相关信息出现。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_编程语言_30

从 x$kglob 也可以查到 kglobhd0  kglobhd6 都为空。

在 x$kglcursor_child 视图也查不到任何信息的,v$sql  v$sqlare 类似的视图也就查不到解析错误的 SQL 了。

关于解析错误的 SQL 是否需要获取 latch 其实从上面的测试已经证明了还是要获取 shared pool 的 latch 的因为生成了父游标。

回顾以下SQL 硬解析过程中需要获取的latch.

首先持有 library cache lath,在 library cache 相关 hash bucket 中扫描已经缓存的对象句柄,查找是否有匹配的父游标,没有找到释放 library cache latch.

接着持有 library cache latch 然后不释放情况下持有 shared pool latch 从 shared pool 中申请分配内存成功后是否 shared pool latch 再是否 library cache latch.

还以上面那个错误的 SQL为例做一个简单的测试。

首先获取 library cache latch 然后运行 sql 查询。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_数据库_31

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_python_32

这个时候会话已经 hang 了。

怎么找到解析失败的 SQL?

  1. 通过关联 x$kglcursor x$kglcursor_child_sqlid  这两个视图是可以找到解析失败的 SQL
  2. 通过使用 Oracle 10035 event 事件也是可以找到解析失败的SQL
  3. 通过 oracle systemdump 也可以找到解析失败 SQL

当然最后该问题定位到了相关解析失败的 SQL,该 SQL 主要是在月底某一模块批量跑的时候大量的执行,最后修改应用程序代码解决了问题。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_33

通过这个简单的案例可以看到不规范的开发习惯给数据库带了严重的性能影响。像类似这种解析出错的 SQL 在很多客户核心系统中比比皆是但是由于种种原因不能及时去除类似的 SQL 最终将带来灾难性的影响。

问答环节

1. : SGA 抖动是什么意思?

    :各个组件来回的增加减少,看下图比较直观一些。

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_34

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_编程语言_35

2. :如果在不知道原理的情况下,直接查找失败的故障时段的大量失败 SQL ,是不是也能初步发现问题 ?

    :如果不使用10035 event 关于失败的SQL很难找到,因为在数据层面除非查底层的视图不然是查不到解析错误的SQL 的。

3. :把cursor_sharing这个值设置成SIMILAR能缓解这个问题吗?

    :当然不能,因为这个是解析错误 这个参数可以说是对他没有效果,cursor_sharing=SIMILAR  风险更大,其实这个库最早的时候就是设置成 SIMILAR 的,调整EXACT之后稍微好一些,但是之前解析错误的 SQL一直没有发现。

    :风险在于哪里?

    :这个是这个库cursor_sharing=SIMILAR 的时候的 version count,

【云和恩墨大讲堂】故障分析 | library cache latch 竞争案例分享_java_36


有直方图就是灾难。

4. :老师,你这个案例很好,就是关于最后定位具体 SQL 谈得很少,原理说的很多。如果我们也遇到这样的问题,关于最后定位还是有疑惑哦~

    :关于怎么找解析错误的SQL 我后面有总结的,当时主要是通过10035 event 还有dump 找到,如果没有搞清楚解析失败SQL的原理 可能就不会去找..