cursor: pin s wait on x 是oracle中因游标共享失败导致的并发等待事件,本质是多个会话争抢同一sql游标的共享pin,而该游标正被另一会话以排他模式pin住,常由硬解析、执行计划刷新或共享池元数据修改引发。
cursor: pin s wait on x 是什么锁等待
这是 Oracle 数据库中一种典型的游标(SQL 语句)并发争用现象,本质不是“锁表”或“锁行”,而是多个会话在争抢同一个 SQL 游标对象的共享(S)pin,而该游标正被另一个会话以排他(X)模式 pin 着——通常是因为它正在做硬解析、刷新执行计划、或修改共享池中的游标元数据。
常见错误现象包括:应用响应明显变慢,AWR 或 ASH 报告里 cursor: pin S wait on X 排在 Top 5 等待事件前列,且对应 SQL 的 version_count 偏高(查 v$sql),loaded_versions 和 open_versions 也异常增长。
- 这类等待几乎总和软解析失败有关,不是 SQL 写得差,而是 Oracle 没能复用已有游标
- 不是所有“相同文本”的 SQL 都能共享游标;绑定变量类型、优化器环境、NLS 设置、甚至客户端字符集微小差异都会导致游标分裂
-
cursor: pin S wait on X高发时,往往伴随大量library cache lock或library cache mutex X等待,说明共享池争用已成瓶颈
怎么快速定位是哪个 SQL 或会话在制造游标分裂
核心思路:从等待事件反查持有 X pin 的会话,再看它正在解析/刷新哪条 SQL,同时检查该 SQL 在 v$sql 中有多少个子游标(version_count)。
- 查当前阻塞链:
SELECT sid, event, p1text, p1, p2text, p2, blocking_session FROM v$session WHERE event = 'cursor: pin S wait on X';
- 对每个
blocking_session,查它正在执行的 SQL:SELECT sql_id, sql_text FROM v$session s JOIN v$sqlarea a ON s.sql_id = a.sql_id WHERE s.sid = &blocking_sid; - 查该
sql_id的游标碎片化程度:SELECT version_count, loaded_versions, open_versions, users_opening, users_executing FROM v$sqlarea WHERE sql_id = '&sql_id';—— 若version_count > 5就值得怀疑 - 注意:
v$sql_shared_cursor才是真相所在,对高version_count的 SQL,执行SELECT * FROM v$sql_shared_cursor WHERE sql_id = '&sql_id' AND reason IS NOT NULL;,逐行看哪些列是Y(比如OPTIMIZER_MISMATCH、BIND_MISMATCH、TRANSLATION_MISMATCH)
BIND_MISMATCH 导致软解析失败的典型场景
绑定变量未显式声明类型或长度,让 Oracle 在不同调用中推断出不同数据类型(如 VARCHAR2(1) vs VARCHAR2(32)),就会强制生成新子游标,进而引发后续的 pin S/X 争用。
- Java PreparedStatement 中用
setString(1, "a")没问题,但若混用setObject(1, "a", Types.VARCHAR)和setObject(1, "a", Types.CHAR),Oracle 可能视为两种类型 - PL/SQL 匿名块里定义
v_name VARCHAR2(10),却传入长度超限的值,触发隐式截断+类型重推导 - 绑定变量在 SQL 中参与函数运算(如
UPPER(:name)),且不同调用传入不同字符集(AL32UTF8 vs ZHS16GBK),也会触发TRANSLATION_MISMATCH
解决方案不是“改 SQL”,而是统一绑定方式:
- 在 JDBC 连接串加
useBindVariables=true&bindStringAsChar=false(具体依驱动版本) - PL/SQL 中显式使用
DBMS_SQL.BIND_VARIABLE并指定datatype和max_length - 应用层避免对同一占位符混用不同 setXXX 方法
shared_pool 大小与 cursor_sharing 设置的真实影响
shared_pool_size 过小会加剧游标老化、频繁 reload,间接抬高 pin 争用概率;但盲目调大未必解决问题。更关键的是 cursor_sharing 的取值是否匹配业务模式。
-
cursor_sharing = EXACT(默认):最安全,但绝不自动改写 SQL,完全依赖应用写法 -
cursor_sharing = FORCE:把字面量全替换成绑定变量,可能破坏谓词选择性,导致执行计划劣化(尤其范围查询、IN 列表) -
cursor_sharing = SIMILAR(11g 已废弃):有严重 Bug,极易引发游标爆炸,必须禁用
性能影响很直接:设为 FORCE 后,原本 1 条 SQL 变成 100 条不同文本,每条都尝试软解析 → 每次都查 shared pool → 每次都可能遇到 X pin → 等待雪崩。
- 检查当前设置:
SHOW PARAMETER cursor_sharing - 生产环境强烈建议保持
EXACT,靠应用规范绑定变量,而不是靠数据库“兜底” - 如果 shared_pool 确实频繁报
ORA-04031,优先考虑调整_kghdsidx_count(增加 shared pool hash bucket 数)或启用automatic_shared_memory_management=TRUE,而非只加内存
游标共享失败从来不是单点问题,它把 SQL 写法、绑定习惯、驱动行为、实例参数、甚至字符集配置全串在了一起。最容易被忽略的,是开发在本地测试时用的 NLS_LANG 和生产不一致,或者 JDBC 驱动版本升级后默认绑定策略变了——这些细节不会报错,但会让 v$sql_shared_cursor 里多出十几行 Y。










