应通过v$active_session_history查最近1分钟event='latch: shared pool'的sql,按sql_id+sql_opname分组聚合,再结合v$sql中loads>executions、unbound_cursor='y'及sql_text含连续单引号等特征确认未绑定变量。
直接查正在抢 latch 的 sql,别等 awr 汇总
ash 是唯一能抓到“正在硬解析、正在抢 shared pool latch”的活体证据。awr 里看到的硬解析总数只是结果,而 v$active_session_history 采样的是真实执行中的会话,不经过聚合、不依赖缓存。
关键不是看谁执行得多,而是看谁卡在 latch: shared pool 上——这说明它正试图插入或查找 cursor,但被锁住了。
- 必须加时间过滤:
SAMPLE_TIME > SYSDATE - 1/1440(最近 1 分钟),否则默认查全历史,慢且干扰多 - 按
sql_id和sql_opname聚合,避免把 SELECT/INSERT 混在一起;sql_opname能帮你快速区分是查询还是 DML 引发的解析 - 示例语句可直接跑:
SELECT sql_id, sql_opname, COUNT(*) cnt FROM v$active_session_history WHERE event = 'latch: shared pool' AND sample_time > SYSDATE - 1/1440 GROUP BY sql_id, sql_opname ORDER BY cnt DESC FETCH FIRST 10 ROWS ONLY;
验证是不是真因未绑定变量导致反复硬解析
拿到 top sql_id 后,不能只看执行次数。真正要盯的是:它是否每次换值都重编译?Oracle 会明确标记无法复用游标的根因。
- 查
v$sql中该sql_id的EXECUTIONS = 1但LOADS > 5—— 基本坐实每次都是新硬解析 - 查
sql_text是否含字面量:WHERE sql_text LIKE '%''%''%'(注意是两个单引号连写,代表字符串值) - 查
v$sql_shared_cursor中对应sql_id的UNBOUND_CURSOR列是否为'Y'—— 这是 Oracle 明确写的“没法复用” - 别信
CURSOR_SHARING=FORCE:19c 已标记为desupported,它生成的系统绑定变量反而拉长 hash chain,加重 latch 争用
别误判为 shared_pool_size 不够或 flush 就行
shared pool latch 高 ≠ 共享池太小,也不等于该 flush。这两招不仅无效,还可能让问题更隐蔽。
-
ALTER SYSTEM FLUSH SHARED_POOL会清空所有 cursor,强制后续所有 SQL 全部硬解析,瞬间放大争用 - 盲目调大
shared_pool_size可能让碎片更分散:空闲块变多但单个不够大,request_misses升高才是碎片化的直接证据 - 观察
v$sgastat中free memory的最小值,再加 20% 作为 baseline;但如果library cache + sql area占比长期低于 60%,说明内存没被有效组织,优先调优而非扩容 -
RESULT_CACHE对缓解latch: shared pool基本无效:它缓存结果,不减少硬解析;其元数据本身也受 shared pool latch 保护
容易被忽略的细节:应用端 prepare() 调用方式
很多硬解析不是 SQL 写得差,而是客户端代码没复用 prepared statement。尤其 Python cx_Oracle 或 JDBC 场景下,一个循环里反复 cursor.prepare() 就等于反复硬解析。
- JDBC 应确认是否启用了
implicit caching或设置了statementCacheSize - Python
cx_Oracle要检查是否重复调用cursor.prepare(),而不是复用已prepare的对象 - Oracle 12c+ 默认启用
_kghdsidx_count(多子池),但若 SQL 模式高度集中(比如全走同一个 hash bucket),子池也救不了 latch 争用 -
v$latch中该 latch 的gets和misses比值持续低于 99.5%(即miss rate > 0.5%)才是真正的争用信号











