Oracle library cache 等待怎么查?Lock、Pin、Mutex 一次讲清楚

前言

前面写 Oracle 等待事件那篇文章时,留言区有用户提到了 library cache,希望我能详细聊聊,其实这类等待和普通的 I/O、行锁等待不太一样。

比如 db file sequential read,可以从读取量和 I/O 延迟入手;enq: TX - row lock contention,通常先找阻塞会话。

但如果 AWR 里面出现下面几个事件:

text 复制代码
library cache lock
library cache pin
library cache load lock
library cache: mutex X
cursor: pin S wait on X

排查起来就没那么直接了。

这些事件虽然都和 Library Cache 有关,但等待的资源并不相同。尤其是 library cache lock 和 library cache pin,名字只有一个单词的区别,涉及的机制却不一样。

这篇把相关原理和常用排查方法整理一下,以 Oracle 19c 为例。

一、先搞清楚 Library Cache 是什么

Oracle 的 SGA 中有一块 Shared Pool,而 Library Cache 是 Shared Pool 的重要组成部分。

SQL 解析后的游标、执行计划,以及 PL/SQL 程序单元等相关信息,都可能保存在 Library Cache 中。

比如应用执行:

sql 复制代码
SELECT * FROM TEST_TABLE WHERE ID = :1;

Oracle 需要解析 SQL,查找是否存在可以复用的游标,并检查对象、权限及相关依赖。如果能够复用,就不需要每次都重新完成硬解析。

不过,这些缓存对象可能同时被多个会话使用。

假设一个会话正在执行某个存储过程,另一个会话准备重新编译这个过程,Oracle 就需要协调对相关 Library Cache 对象的访问。

这也是 Library Cache 中存在 Lock、Pin 和 Mutex 等机制的原因。

二、library cache lock 和 pin 有什么区别?

这是最容易混淆的两个事件。

  1. 先看 library cache lock:

它主要用于协调 Library Cache 对象句柄的访问,以及维护对象之间的依赖关系。

比如某个会话正在使用一个对象,另一个会话准备修改该对象,可能需要等待相关的 Library Cache Lock。

  1. 再看 library cache pin:

它主要用于保护对象在 Library Cache 中的具体内容。会话需要访问或修改对象内容时,通常需要在取得相应 Lock 后,再取得 Pin。

可以简单理解为:

  • Lock 主要协调对象的访问及依赖关系。
  • Pin 主要协调对象内容的使用和修改。

当然,Oracle 内部实现比这个复杂,不能简单把 Lock 理解成表锁,也不能把 Pin 理解成行锁。

这两种等待也不属于 enq: TX 那套事务行锁机制。

官方文档:Oracle Database 19c --- Descriptions of Wait Events

对应章节:

  • C.3.89 --- library cache lock
  • C.3.90 --- library cache pin

三、哪些操作容易引起 Library Cache 等待?

比较常见的场景是对象发生变更。

例如一个存储过程正在被应用调用,同时有人执行:

sql 复制代码
ALTER PROCEDURE TEST_PROC COMPILE;

如果对象正在被其他会话使用,重新编译就可能需要等待相关 Library Cache 资源。反过来,如果某个会话正在编译或修改对象,其他需要访问该对象的会话也可能受到影响。

类似情况还可能出现在:

  • 应用运行期间执行 DDL。
  • 存储过程、Package 频繁失效或重新编译。
  • SQL 硬解析或游标管理竞争。
  • 大量会话并发访问相同的 Library Cache 资源。

不过,具体出现哪一种等待事件,要看当时争用的资源和操作,不能简单认为执行 DDL 就一定产生 library cache lock。

library cache load lock 主要出现在对象加载过程中。Oracle 为避免多个进程同时加载同一个对象,需要获取独占的 Load Lock。如果其他会话正在加载该对象,当前会话就可能等待。它和普通的 library cache lock 不是同一种机制。

四、Mutex 和 Lock、Pin 又有什么不同?

Oracle 还有一些比较常见的相关等待:

text 复制代码
library cache: mutex X
cursor: pin S wait on X

这里涉及 Mutex(互斥机制)。

library cache: mutex X 表示会话在获取 Library Cache 相关结构的独占 Mutex 时发生等待,可能涉及对象查找、游标管理及并发解析等操作。实际排查时,还需要结合等待参数和 SQL 解析情况判断具体争用点。

cursor: pin S wait on X 表示当前会话需要获取游标的共享 Mutex Pin,但其他会话正以独占模式持有该资源,因此只能等待。这个事件通常需要结合 SQL 解析、游标共享和并发访问情况进一步分析。

官方文档:Oracle Database 19c --- Descriptions of Wait Events

因此,虽然它们都可能出现在 SQL 解析或执行过程中,但不能把 Mutex 等待和 library cache lock、library cache pin 混为一谈。

实际排查时,如果是 Mutex 类等待,我会更关注 SQL 解析频率、游标共享、子游标数量以及是否存在大量并发解析。如果是 Lock 或 Pin 等待,则需要进一步检查对象变更、失效、重新编译及持有相关资源的会话。

五、生产环境怎么查?

下面整理几条 Oracle 19c 常用的查询语句,先从实时会话入手。

1. 查看正在发生的 Library Cache 等待

sql 复制代码
SELECT inst_id,
       sid,
       serial#,
       username,
       event,
       state,
       ROUND(wait_time_micro / 1000000, 3) AS wait_sec,
       sql_id,
       blocking_instance,
       blocking_session,
       module,
       program
FROM gv$session
WHERE state = 'WAITING'
  AND (
       event LIKE 'library cache%'
       OR event LIKE 'cursor: pin%'
  )
ORDER BY wait_time_micro DESC;

先查看当前有哪些会话正在等待 Library Cache 相关资源,以及等待已经持续了多久。

如果多个会话集中在同一个等待事件或 SQL_ID 上,可以进一步检查等待参数和阻塞关系。BLOCKING_SESSION 可以作为参考,但并不是所有 Library Cache 等待都能直接通过它找到持有者。

2. 查看等待参数

sql 复制代码
SELECT inst_id,
       sid,
       event,
       p1text,
       p1,
       p1raw,
       p2text,
       p2,
       p2raw,
       p3text,
       p3,
       sql_id
FROM gv$session
WHERE state = 'WAITING'
  AND event IN (
      'library cache lock',
      'library cache pin',
      'library cache load lock'
  );

对于 library cache lock 和 library cache pin,P1 通常是对象的 Handle Address。这个地址非常重要,因为后面可以尝试通过它关联到具体 Library Cache 对象。

但需要注意,不同等待事件的 P1、P2、P3 含义并不相同,不能把 Mutex 类等待的参数也按照相同方式解释。

3. 根据 Handle 定位对象和相关会话

Oracle 的 V$DB_OBJECT_CACHE 可以查看 Library Cache 中的对象信息:

sql 复制代码
SELECT s.inst_id,
       s.sid,
       s.serial#,
       s.event,
       s.sql_id,
       RAWTOHEX(s.p1raw) AS wait_handle,
       o.owner,
       o.name,
       o.type,
       o.namespace
FROM gv$session s
LEFT JOIN gv$db_object_cache o
       ON o.inst_id = s.inst_id
      AND o.addr = s.p1raw
WHERE s.state = 'WAITING'
  AND s.event IN (
      'library cache lock',
      'library cache pin'
  )
ORDER BY s.inst_id, s.sid;

这条 SQL 通过等待会话的 P1RAW 与 GV$DB_OBJECT_CACHE.ADDR 关联,尝试找到发生等待的 Library Cache 对象。对于 RAC,需要通过 INST_ID 确保关联的是同一个实例的数据。

sql 复制代码
SELECT l.inst_id,
       l.type,
       RAWTOHEX(l.object_handle) AS object_handle,
       RAWTOHEX(l.holding_session) AS holding_session,
       RAWTOHEX(l.holding_user_session) AS holding_user_session,
       s.sid,
       s.serial#,
       s.username,
       s.status,
       s.sql_id,
       s.event,
       l.mode_held,
       l.mode_requested
FROM gv$libcache_locks l
LEFT JOIN gv$session s
       ON s.inst_id = l.inst_id
      AND s.saddr = l.holding_user_session
WHERE l.inst_id = :inst_id
  AND l.object_handle = HEXTORAW(:handle_hex)
ORDER BY l.type,
         l.mode_requested DESC,
         l.mode_held DESC;

这条 SQL 根据对象 Handle 查询相关的 Library Cache Lock/Pin,查看资源的持有模式和请求模式,并尝试关联到具体会话。

HOLDING_SESSION 和 HOLDING_USER_SESSION 都是内部地址,不是 SID。

这里使用 HOLDING_USER_SESSION 与 GV$SESSION.SADDR 关联,但并不保证所有记录都能找到对应会话。如果关联不到,还需要结合内部会话地址和其他诊断信息进一步分析。

另外,RAC 中这些内存地址属于具体实例,不能拿实例 1 的地址去实例 2 中查。

4. 检查对象是否频繁失效

sql 复制代码
SELECT owner,
       name,
       type,
       loads,
       executions,
       invalidations
FROM v$db_object_cache
WHERE invalidations > 0
ORDER BY invalidations DESC
FETCH FIRST 30 ROWS ONLY;

如果某些对象存在较多 Invalidations,可以进一步检查近期是否发生过 DDL、对象重新编译或依赖对象变更。但 INVALIDATIONS 是累计计数,不能只凭数值高就判断它是当前等待的原因。

5. 查看解析情况

sql 复制代码
SELECT name,
       value
FROM v$sysstat
WHERE name IN (
    'parse count (total)',
    'parse count (hard)',
    'execute count'
)
ORDER BY name;

这几个指标也是累计值,最好结合 AWR 的快照增量,判断异常时段解析次数是否明显增加。

如果出现大量 library cache: mutex X 或 cursor: pin S wait on X,可以进一步检查 V$SQL 中的版本数、解析调用次数和 SQL 共享情况。

六、如果是历史问题怎么办?

实时查询有一个明显的问题:等 DBA 开始排查时,等待可能已经消失了。

这时候就需要使用 ASH。

例如检查某个时间段内的 Library Cache 相关等待:

sql 复制代码
SELECT instance_number,
       sample_time,
       session_id,
       session_serial#,
       sql_id,
       event,
       blocking_inst_id,
       blocking_session,
       p1text,
       p1,
       p2,
       p3
FROM dba_hist_active_sess_history
WHERE sample_time >=
      TO_TIMESTAMP(:begin_time, 'YYYY-MM-DD HH24:MI:SS')
  AND sample_time <
      TO_TIMESTAMP(:end_time, 'YYYY-MM-DD HH24:MI:SS')
  AND session_state = 'WAITING'
  AND (
       event LIKE 'library cache%'
       OR event LIKE 'cursor: pin%'
  )
ORDER BY sample_time, instance_number;

这条 SQL 用于查看指定时间段内的历史 ASH 记录,包括等待事件、SQL_ID、会话信息、等待参数以及可能存在的阻塞会话。

需要注意,ASH 采用采样方式记录活跃会话,并不会保存每一次等待。DBA_HIST_ACTIVE_SESS_HISTORY 中的数据也可能经过进一步抽样,因此短时间内发生的等待不一定能查到。

如果发现某个时间段内有大量会话集中等待同一个事件,可以继续结合 SQL_ID、等待参数、阻塞会话和业务操作时间,进一步缩小排查范围。

七、排查时还有几个容易踩的坑

第一个是看到 Library Cache 等待就增加 Shared Pool。

Shared Pool 空间不足确实可能带来解析和缓存管理问题,但 Library Cache 等待也可能是对象变更、依赖关系或游标并发竞争造成的。

没有证据之前,直接增加 shared_pool_size 不一定有效。

第二个是发现等待会话后就准备 Kill Session。

Library Cache 等待的持有者不一定能通过 GV$SESSION.BLOCKING_SESSION 直接找到。即使找到相关会话,也需要先确认它正在执行什么操作。

如果正在进行对象编译、DDL 或重要业务操作,贸然终止可能带来额外影响。

第三个是直接执行 ALTER SYSTEM FLUSH SHARED_POOL。

这类操作可能导致大量游标失效和后续硬解析,生产环境不应该把它作为常规处理手段。

最后

Library Cache 相关等待比普通 I/O 等待复杂一些,主要是因为它涉及对象依赖、解析、编译以及多个会话之间的并发访问。

我觉得排查时可以先把事件分清楚:

  1. library cache lock、library cache pin 重点关注对象访问和变更;
  2. library cache: mutex X、cursor: pin S wait on X 则需要更多关注游标和解析相关的并发竞争。

后面再通过等待参数、对象 Handle、SQL_ID 和 ASH 逐步定位。

至于具体是什么原因,还是要结合现场数据判断,不能只根据等待事件名称下结论。

相关推荐
茉莉玫瑰花茶4 小时前
GO [ context 原理解析 ]
数据库·oracle·iphone
小羊没烦恼!6 小时前
浅谈SQL Server中的事务日志(二)----事务日志在修改数据时的角色
前端·数据库·后端·python·web安全·oracle
大侠归来1 天前
事务能回滚吗?Pipeline 算事务吗?
java·数据库·oracle
夕除1 天前
PostgreSQL基础
数据库·oracle
Eyfcom1 天前
〖实战〗快运系统 Oracle 性能优化:从“能用”到“扛住双11”的 Java + Spring Boot 实战
java·oracle·性能优化·快递管理系统
程序 代码狂人1 天前
SQL COUNT 统计 NULL 与空字符串 ‘‘ 差异(Oracle & Impala)
数据库·sql·oracle
晴天¥1 天前
Oracle Data Guard入门指南
数据库·oracle·oracle dg入门指南
oradh1 天前
Oracle 物化视图维护总结(原理+用途+案例)
数据库·oracle