shared pool latch争用高说明多个会话高频竞争同一把锁以访问shared pool内存结构,主因是硬解析暴增或未绑定变量导致hash冲突加剧、链表过长。
shared pool latch 争用高,说明什么
shared pool latch 争用高,不是“共享池太小”这么简单——它本质是多个会话在高频抢同一把锁,去访问 shared pool 中的内存结构(比如 hash bucket 链表头)。常见现象是 latch: shared pool 等待事件在 awr 或 ash 里排前几,v$latch 中该 latch 的 gets 和 misses 比值持续低于 99.5%(即 miss rate > 0.5%)。
真正触发点往往是硬解析暴增,或大量相似但未绑定变量的 SQL 反复入池,导致 shared pool 内部 hash 冲突加剧、链表过长,每次查找/插入都要竞争 latch。
- 不是所有硬解析都等价:
SELECT * FROM t WHERE id = 1和SELECT * FROM t WHERE id = 2在未绑定变量时,会被当作两条不同语句硬解析,各自生成独立 cursor,挤占 shared pool 空间并拉长 hash 链 - Oracle 12c+ 默认启用
_kghdsidx_count(多子池),但若 SQL 模式高度集中(比如全走同一个 hash bucket),子池也救不了 latch 争用 - 注意
v$sgastat中free memory是否持续低于 5MB —— 不是看总量,而是看“碎片化空闲块是否足够容纳新 cursor”,request_misses升高才是碎片化的直接证据
怎么快速定位硬解析来源
别先翻 AWR 报告总硬解析数,那只是结果。要盯住「谁在反复硬解析」和「为什么不能重用」:
- 查
v$sql中EXECUTIONS = 1且PARSE_CALLS > 1的语句(说明反复软解析失败,退化为硬解析) - 重点关注
SQL_TEXT高度相似、仅字面量不同的语句组,用DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE或正则提取谓词模板(如把数字/字符串替换成?)做聚类 - 检查
v$sql_shared_cursor,对疑似语句查UNBOUND_CURSOR、EXPLAIN_PLAN_CURSOR、OPTIMIZER_MISMATCH等列为Y的行——这是硬解析无法共享的根因,比如 optimizer_features_enable 设置不一致、NLS 参数漂移 - 应用端若用 JDBC,确认是否启用了
implicit caching或设置了statementCacheSize;Python cx_Oracle 则需检查是否重复调用cursor.prepare()而非复用已 prepare 的对象
shared_pool_size 设多大才不碎片化
没有固定公式。设大了可能让碎片更隐蔽(空闲块分散、单个不够用),设小了又频繁触发 LRU 清理和重解析。关键看实际使用模式:
- 观察
v$sgastat中free memory的最小值,再加 20% 作为 baseline;但若发现library cache+sql area占比长期低于 60%,说明 pool 内存没被有效组织,优先调优而非扩容 - 避免盲目开大
shared_pool_size后忽略cursor_sharing设置——cursor_sharing = FORCE在 OLTP 场景可能引发隐式类型转换,反而增加 parse 失败率 - Oracle 19c+ 推荐搭配
MEMORY_TARGET或SGA_TARGET自动管理,但必须关掉lock_sga,否则 shared pool 子区无法动态收缩,碎片会长期滞留 - 紧急缓解可用
ALTER SYSTEM FLUSH SHARED_POOL,但这是止痛药:它清空所有 cursor,下一次请求全变硬解析,可能瞬间压垮 latch —— 只在确认是某批异常 SQL 污染后才用,且配合DBMS_SHARED_POOL.PURGE精准清理
绑定变量没写对,照样硬解析
写了绑定变量,但没写对,等于白写。常见失效场景比想象中多:
- 字符串拼接中混入字面量:
'WHERE status = ''' || v_status || ''''→ 即使v_status是变量,拼出来仍是字面量 SQL,Oracle 看不到绑定点 - PL/SQL 中用
EXECUTE IMMEDIATE 'SELECT ... WHERE id = ' || id→ 必须改成EXECUTE IMMEDIATE 'SELECT ... WHERE id = :id' USING id - JDBC PreparedStatement 的参数索引错位、或执行前未 setXXX(比如
setString(1, null)但字段不允许 null,驱动可能 fallback 到字面量) - ORM 如 MyBatis 的
${}是字符串替换,#{}才是绑定;Hibernate 的@Query(nativeQuery = true)若含拼接,同样绕过绑定
最稳的验证方式:查 v$sql 的 SQL_TEXT,如果看到具体数值(如 WHERE id = 123),而不是 :B1 或 :1,那就还没真正绑定。










