子游标过多导致大量mutex争用故障分析

来源:这里教程网 时间:2026-03-03 19:04:12 作者:

问题处理

背景:

2023 / 04/02 日中 11 30 左右 ,接到 jzdb3 节点 1 数据库 性能差, awr 中出现 严重 cursor: mutex S\ cursor: mutex X 争用

SR 3-14602160331 :   A Large number of child cursor caused the 'cursor: mutex S' and 'cursor: mutex X'

SSC 通过现场对 DB RAC OS SQLHC SQL version_rpt 等数据做了详细的原因分析。

救援 过程

1 AWR 报告分析

现场 抓取了 今天 一整天的AWR 日志, AWR 追溯到从凌晨 1 TOP Events 开始出现cursor: mutex S cursor: mutex X 争用:  

下一个半个小时 awr 开始 持续 处于 event 的前列:

分析 造成 event 冲高的 SQL 下面两条 SQL导致 的:

SQL ID:  au0v7hv343q45\

具体 SQL TEXT:

SQL ID : au0v7hv343q45: select  to_char(account_id),        acctbk_id,        to_char(last_balance),        to_char(current_balance),        status,        to_char(start_date, 'yyyymmddhh24miss' ),        to_char(end_date, 'yyyymmddhh24miss' ),        unit,        to_char(expire_date, 'yyyymmddhh24miss' ),        to_char(join_id),        to_char( version )    from  acctbk   where  account_id = to_number(:v_account_id)

    and  acctbk_id = :v_acctbk_id

 

SQL ID: select  to_char(account_id),        acctbk_id,        to_char(last_balance),        to_char(current_balance),        status,        to_char(start_date, 'yyyymmddhh24miss' ),        to_char(end_date, 'yyyymmddhh24miss' ),        unit,        to_char(expire_date, 'yyyymmddhh24miss' ),        to_char(join_id),        to_char( version )    from  acctbk   where  account_id = to_number(:v_account_id)     and  acctbk_id = :v_acctbk_id     for   update   nowait

 

  均为 acctbk 查询 操作

2 Version Count Report 分析

分析 SQL C ount

au0v7hv343q45

 

发现造成子游标不能共享的原因主要有 LANGUAGE_MISMATCH ROLL_INVALID_MISMATCH 子游标 数目比较高,而且一直没有age out 出去

于是 我们使用下面命令收集统计信息:

exec dbms_stats.gather_table_stats(ownname => 'accounting',tabname => 'acctbk',estimate_percent => 5,degree => 4,cascade => true, no_invalidate => false )

 

PL/SQL procedure successfully completed.

 

Elapsed: 00:07:47.44

该命令 执行了 7 多钟, no_invalidate 参数 设置为false 目的 让表 acctbk 对应 SQL 所有子游标 立即 失效,并age out share pool

执行 完该 命令 之后,再取awr 报告, c ursor: mutex S cursor: mutex X 消失 ,系统恢复正常。

分析过程

该故障分析 的焦点问题为:

1、  大量 LANGUAGE_MISMATCH ROLL_INVALID_MISMATCH BIND_MISMATCH 如何产生 的。

2、  为什么产生 了这么多的 mismatch 的子游标又不能及时 age out 出去

针对第一个 问题,我们逐个做了分析:

1、  产生 原因

LANGUAGE_MISMATCH

LANGUAGE_MISMATCH 环境设置 NLS_* 有关系 比如 客户端session 使用 了不同的NLS_SORT 等。  

TEST CASE:

a lter system flush shared_pool;

create table X as select * from dba_tables;

set time on pages 1000 lin 200

col FIRST_LOAD_TIME format a20

col last_load_time format a20

var b1 varchar2(128);

var b2 varchar2(128);

exec :b1 := '0';         

exec :b2 := '0';

nls_sort

alter session set = 'SCHINESE_RADICAL_M';

select count(*) from X where table_name=:b1 and OWNER=:b2;

--- 修改 nls_sort

alter session set nls_sort = 'SCHINESE_STROKE_M';

select count(*) from X where table_name=:b1 and OWNER=:b2;

-- 修改 nls_sort

alter session set nls_sort = 'SCHINESE_PINYIN_M';

select count(*) from X where table_name=:b1 and OWNER=:b2;

-- 查询 SQL id

09:12:41 SQL> select sql_id from v$sql where sql_text='select count(*) from X where table_name=:b1 and OWNER=:b2';

SQL_ID

-------------

1uk1h0wuzcuaf

 

09:14:53 SQL> select sql_id,child_address,language_mismatch,roll_invalid_mismatch,BIND_MISMATCH from v$sql_shared_cursor where sql_id='1uk1h0wuzcuaf';

SQL_ID        CHILD_ADDRESS    L R B

------------- ---------------- - - -

1uk1h0wuzcuaf 00000000750EDBB0 N N N

1uk1h0wuzcuaf 0000000079F0C748 Y N N

1uk1h0wuzcuaf 0000000075F5F6A0 Y N N

  ROLL_INVALID_MISMATCH

在统计信息收集的时候,默认情况下 dbms_stats no_invalidate 参数为 auto_invalidate ,意思 收集 完统计信息之后, 立即失效 统计 对象 相关 cursor ,而是 采用滚动 失效 的方式 。这种做法 是为了 避免 统计信息收集之后产生大量的硬解析 。数据库会随机 的取一个小于_optimizer_invalidation_period 值来判断子游标的 INVALIDATE 时间,默认情况下是 18000 秒,即 5 个小时。

18000s 并不是从统计信息收集开始(下图 中的 T0 ),而是从统计信息收集之后该 SQL 的第一次解析时间开始( T1 ), 然后随机的取一个时间,该时间小于_optimizer_invalidation_period 设定 的时间 ,我们 可以定义为下图的 T max

下次 执行的时候 T2 ), 如果 T2 小于 T max ,就会复用以前的子游标,如果 T2 大于 T max 会执行硬解析

 

TEST CASE:

alter system flush shared_pool;

create table X as select * from dba_tables;

set time on pages 1000 lin 200

col FIRST_LOAD_TIME format a20

col last_load_time format a20

alter system set "_optimizer_invalidation_period"=120;

09:38:56 SQL> select child_number,parse_calls,executions,first_load_time, last_load_time,last_active_time from v$sql where sql_id='95rckg79jgshh';

CHILD_NUMBER PARSE_CALLS EXECUTIONS FIRST_LOAD_TIME      LAST_LOAD_TIME       LAST_ACTIVE_TIME

------------ ----------- ---------- -------------------- -------------------- -----------------

0           3          3 2023-04-06/09:35:47  2023-04-06/09:35:47  06/04/2023 09:38:55

09:39:00 SQL>  exec dbms_stats.gather_table_stats(null,'X');

PL/SQL procedure successfully completed.

09:39:23 SQL> select last_analyzed from dba_tables where table_name='X';

LAST_ANALYZED

-------------------

06/04/2023 09:39:23

09:39:38 SQL> select count(*) from X;

COUNT(*)

----------

2341  

09:43:58 SQL> select child_number,parse_calls,executions,first_load_time, last_load_time,last_active_time from v$sql where sql_id='95rckg79jgshh';

CHILD_NUMBER PARSE_CALLS EXECUTIONS FIRST_LOAD_TIME      LAST_LOAD_TIME       LAST_ACTIVE_TIME

------------ ----------- ---------- -------------------- -------------------- -------------------

0           4          4 2023-04-06/09:35:47  2023-04-06/09:35:47  06/04/2023 09:39:46

1           1          1 2023-04-06/09:35:47  2023-04-06/09:43:57  06/04/2023 09:43:57  

09:44:01 SQL> select sql_id,child_address,language_mismatch,roll_invalid_mismatch,BIND_MISMATCH from v$sql_shared_cursor where sql_id='95rckg79jgshh';

SQL_ID        CHILD_ADDRESS    L R B

------------- ---------------- - - -

95rckg79jgshh 00000000748609A0 N N N

95rckg79jgshh 00000000751A7820 N Y N

BIND_MISMATCH

一般是由于bind value 的长度不同导致 bind buffer 无法重用,最终导致 cursor 无法重用。

2 为什么不被清理

经过分析 和多轮测试发现:

ROLL_INVALID_MISMATCH Y 的子 游标, 在统计信息 收集之后, 如果使用 默认的NO_INVALIDATE=   AUTO_INVALIDATE 一段时间 之后,会被置 invalidations 并有 机制保 它及时的age out 出去。 12 c ,未发现无法清理 bug 存在 ,假设 有,那么acctbk 表所 涉及到的所有 SQL 都应该会 出现该 问题 ,从 记账库 3 查询的情况来看,是可以排除该可能性的。

BIND_MISMATCH 客户端 编码 导致的, 一个 游标的LANGUAGE_MISMATCH/ BIND_MISMATCH 被标记成 Y 之后, object_status 一直为valid 状态 数据库 认为他还是有可能被重新 复用 的, 只有当该对象在 统计信息收集的时候 使用NO_INVALIDATE=>false 参数,该 状态才能变成INVALID_UNAUTH 在下次 SQL 执行之后 才能 age  out 出去

另外 目前记账库上 查询的情况来看 ,大部分子 cursor ROLL_INVALID_MISMATCH LANGUAGE_MISMATCH 栏位 同时为 Y V $SQL object_status valid 就能解释了 ROLL_INVALID_MISMATCH 不能 age out 原因

SQL>select sql_id,child_address,language_mismatch,roll_invalid_mismatch,BIND_MISMATCH from v$sql_shared_cursor where sql_id='6uvmkxmcvbc6t';

    SQL_ID            CHILD_ADDRESS        LANGUAGE_MISMATCH ROLL_INVALID_MISMATCH BIND_MISMATCH    

    6uvmkxmcvbc6t     C000002357553CE8            Y            N                     Y    

    6uvmkxmcvbc6t     C00000235749AC10            Y            N                     Y    

    6uvmkxmcvbc6t     C000002353906DA0            Y            N                     Y    

    6uvmkxmcvbc6t     C000002354557798            Y            N                     Y    

    6uvmkxmcvbc6t     C0000021BFEDCC50            Y            Y                     N    

    6uvmkxmcvbc6t     C0000021BF661238            Y            Y                     N    

    6uvmkxmcvbc6t     C0000021BE4973D8            Y            Y                     Y    

    6uvmkxmcvbc6t     C0000021BDF0F488            Y            Y                     N    

    6uvmkxmcvbc6t     C0000020DBE75628            Y            Y                     N    

    6uvmkxmcvbc6t     C0000020DB9FA1A8            Y            Y                     N    

    6uvmkxmcvbc6t     C0000020DB9F2FC8            Y            Y                     N    

    6uvmkxmcvbc6t     C0000020DB7E7C60            Y            Y                     N    

    6uvmkxmcvbc6t     C00000226B498B18            Y            Y                     N    

    6uvmkxmcvbc6t     C000001FEC10BE90            Y            Y                     N    

    6uvmkxmcvbc6t     C000002269971508            Y            Y                     N    

    6uvmkxmcvbc6t     C000002265912D38            Y            Y                     N    

    6uvmkxmcvbc6t     C0000020D32857D8            Y            Y                     N    

    6uvmkxmcvbc6t     C0000020D3C625B8            Y            Y                     Y    

    6uvmkxmcvbc6t     C0000020D01DF1D0            Y            Y                     N    

    6uvmkxmcvbc6t     C0000020CFE39790            Y            Y                     N    

    6uvmkxmcvbc6t     C00000226A5A8D80            Y            Y                     N    

    6uvmkxmcvbc6t     C00000226B529B90            Y            Y                     N    

    6uvmkxmcvbc6t     C00000226B53B440            Y            Y                     N    

    6uvmkxmcvbc6t     C000002268732578            Y            Y                     N    

    6uvmkxmcvbc6t     C0000020CD03B6C8            Y            Y                     Y    

    6uvmkxmcvbc6t     C0000020C6D79578            Y            Y                     N    

    6uvmkxmcvbc6t     C000002351AFBB78            Y            Y                     Y    

    6uvmkxmcvbc6t     C0000020C4E37F08            Y            Y                     N

 

SQL>select child_number,LAST_ACTIVE_TIME,LAST_LOAD_TIME,OBJECT_STATUS from v$sql where sql_id='6uvmkxmcvbc6t' order by LAST_ACTIVE_TIME;

      CHILD_NUMBER    LAST_ACTIVE_TIME         LAST_LOAD_TIME            OBJECT_STATUS      

      1               2023/4/1 17:04:57        2023-04-01/17:04:57            VALID      

      2               2023/4/2 9:01:35         2023-04-01/17:06:01            VALID      

      3               2023/4/2 9:02:27         2023-04-01/17:06:01            VALID      

      0               2023/4/2 11:19:38        2023-04-01/17:04:57            VALID      

      6               2023/4/3 1:53:15         2023-04-02/11:20:01            VALID      

      5               2023/4/3 1:54:21         2023-04-02/09:55:21            VALID      

      7               2023/4/3 3:48:58         2023-04-02/12:39:23            VALID      

      4               2023/4/3 6:00:42         2023-04-02/09:02:28            VALID      

      11              2023/4/3 23:16:10        2023-04-03/06:17:07            VALID      

 

故障 分析 总结

基于 上述分析, 客户端环境 变量 游标使用不规范导致了mismatch 的子游标过多,进而导致了 cursor: mutex S cursor: mutex X 争用, 性能出现问题, 因为 客户端环境比较复杂,修改起来需要一个过程,目前 数据库 端能做的workaround 有如下

1、  定期 监控游标过多的 SQL ,子游标 超过 200 的, 收集统计信息的时候 使用 no_invalidate=false 参数。

或者为有 上述 问题 特别的指 统计信息收集属性, 设置 no_invalidate => false 方法 如下

SQL> SELECT DBMS_STATS.GET_PREFS('NO_INVALIDATE','SYS','X') FROM DUAL;   DBMS_STATS.GET_PREFS('NO_INVALIDATE','SYS','X') -------------------------------------------------------------------------------- DBMS_STATS.AUTO_INVALIDATE   SQL>   PL/SQL procedure successfully completed.   SQL> SELECT DBMS_STATS.GET_PREFS('NO_INVALIDATE','SYS','X') FROM DUAL;   DBMS_STATS.GET_PREFS('NO_INVALIDATE','SYS','X') --------------------------------------------------------------------------------

FALSE

2、  出现子游标过多的时候,执行如下命令将该 SQL 的所有 子游标清除出去

SQL> alter session set events '5614566 trace name context forever'  --oracle10g 需要执行     SQL>select address,hash_value,executions,parse_calls from v$sql where sql_id='3yr4fwqux2buz';   SQL> . e('00000000749AF8B8,3050385247','C');   - -   参数 1 SQL address ;参数 2  hash_value ;参数 3 C 表示 PURGE 的对象是 CURSOR  

3、  修改每个 sql 允许生成子游标 的总个数 12 c 版本默认值为 1024

SQL>alter system set " _cursor_obsolete_threshold  "=200;

 

相关推荐