latch: shared pool 和 latch: library cache 争用根源在于高频硬解析,首要定位未绑定变量的 sql;可通过 ash 实时捕获正在争抢 latch 的活体语句(如查 v$active_session_history 中 event='latch: shared pool' 的 top sql),再结合 v$sql 验证其字面量特征、低执行高加载等典型现象,最终通过应用层绑定变量改造根治,禁用 cursor_sharing=similar,避免 flush shared_pool 等加剧争用的操作。
latch: shared pool 和 latch: library cache 争用不是配置调大就能解决的问题,核心得先定位到“谁在反复硬解析”,而最常见、最隐蔽的元凶就是没用绑定变量的 sql —— 这类语句在 v$sqlarea 里会大量堆积相似但 sql_id 不同的记录,每条都触发一次硬解析,直接把 latch 撑爆。
用 ASH 快速抓出高频非绑定变量 SQL
别再只查 v$sqlarea 的 FORCE_MATCHING_SIGNATURE,ASH(v$active_session_history)能告诉你“此刻正在争抢 latch 的到底是哪几条 SQL”,更贴近真实压力点。
- 查最近 1 小时内导致最多
latch: shared pool等待的 SQL:
SELECT sql_id, sql_opname, event, COUNT(*) cnt FROM v$active_session_history WHERE event = 'latch: shared pool' AND sample_time > SYSDATE - 1/24 GROUP BY sql_id, sql_opname, event ORDER BY cnt DESC FETCH FIRST 10 ROWS ONLY;
- 对命中结果里的
sql_id,再查它的文本和字面量特征:
SELECT sql_text,
CASE WHEN sql_text LIKE '%''%''%' THEN 'has literal' ELSE 'no literal' END has_literal,
parsing_schema_name,
executions,
loads,
invalidations
FROM v$sql
WHERE sql_id = '&input_sql_id';
- 关键看
sql_text里是否含连续单引号(如WHERE name='JOHN'),且executions低、loads高、invalidations多——这是典型“每次换值就重编译”的信号。
为什么 ASH 比 v$sqlarea 更准
v$sqlarea 只反映“曾经进过 shared pool 的语句”,但很多 literal SQL 根本没缓存成功(被 ORA-04031 踢出去了),或者刚硬解析完就被刷掉;而 ASH 是采样正在运行的 session,它捕获的是“正在争 latch 的活体 SQL”,哪怕只执行一次、没进库缓存,也能被抓到。
- ASH 的
sql_id是真实执行时生成的,不受CURSOR_SHARING参数干扰; - 如果应用用了 JDBC 的
prepareStatement却没设 bind,ASH 里仍能看到带具体值的完整sql_text; - 注意:ASH 默认只保留 1 小时(
_ash_sampling_interval影响精度),若问题偶发,需配合 AWR 抓快照或延长采样窗口。
确认后怎么改:三步封堵硬解析源头
找到语句只是开始,改代码或驱动才是根治。以下操作顺序不能反:
- 先确认该 SQL 是否真能参数化:比如
SELECT * FROM orders WHERE status='SHIPPED'可以改成WHERE status=:status;但SELECT * FROM tab WHERE rownum 这类固定值语句,即使不加 bind 也不会产生硬解析风暴,不用强改; - 检查应用层是否禁用了 bind:某些旧版 ODBC/JDBC 驱动默认关闭 bind variable peeking,或 ORM 框架(如早期 Hibernate)生成 SQL 时拼接字符串,需升级或配
useBindVariables=true; - 临时兜底(仅限紧急):设
CURSOR_SHARING=FORCE,但注意它可能让优化器误判数据分布,导致执行计划劣化;CURSOR_SHARING=SIMILAR在 11g 已废弃,不要用。
容易被忽略的陷阱
很多人查到 literal SQL 就立刻改应用,却漏掉了两个静默杀手:
-
DBMS_SHARED_POOL.KEEP对这类语句无效:keep 的是已存在的游标,而 literal SQL 每次都是新sql_id,根本 keep 不住; - 清空 shared pool(
ALTER SYSTEM FLUSH SHARED_POOL)反而加剧争用:清空后所有 SQL 都要重新硬解析,瞬间放大 latch 压力; - 表统计信息频繁收集、DDL 变更、甚至
ANALYZE TABLE都会导致相关游标失效(invalidations上升),间接诱发软解析升级为硬解析——查v$sql时务必连带看last_active_time和invalidations。











