【DB笔试面试580】在Oracle中,什么是High Version Count?

  • 2019 年 10 月 10 日
  • 笔记

题目部分

在Oracle中,什么是High Version Count?

答案部分

一个父游标下对应的子游标个数被称为Version Count,每个子游标对应一个执行计划。对于一个特定的游标有多少个版本(Version Count)就属于高版本游标是没有明确定义的。对于不同的系统有不同的数量界定。High Version Count不仅产生的原因是多种多样的,而且会导致各种令人头痛的问题,轻则导致数据库的性能急剧下降,CPU利用率剧增,重则导致数据库挂起,触发ORA-04031或者其它BUG导致宕机。

在AWR报告中,Version Count大于20就会被报告出来,如下图所示:

SQL执行计划的生成,是受到很多因素影响的。相同父游标只是表示输入SQL的字面值相同。子游标对应的因素,如优化器类型、优化器的模式(OPTIMIZER_MODE)、对应对象权限等的差异,都会影响到子游标的共享。

在Oracle 11g中,V$SQL_SHARED_CURSOR可以用来诊断子游标不共享问题的原因。该视图通过SQL_ID和CHILD_NUMBER就可以定义某个特定子游标的信息。该视图中大部分列都是以VARCHAR2(1)的Y/N取值,每列的含义都是一个不能共享的理由。需要注意的是,这个理由N表示的是不能与第一个子游标(CHILD_NUMBER=0)共享的理由。

下面举一个由于优化器模式不同导致游标不能共享的例子:

SYS@lhrdb> SELECT * FROM SCOTT.EMP WHERE ROWNUM<=0;  no rows selected  SYS@lhrdb> select sql_id, version_count from v$sqlarea where sql_text like 'SELECT * FROM SCOTT.EMP WHERE ROWNUM<=0%';  SQL_ID        VERSION_COUNT  ------------- -------------  7u75n20ktntsb             1  SYS@lhrdb>  show parameter optimizer_mode  NAME                                 TYPE        VALUE  ------------------------------------ ----------- ------------------------------  optimizer_mode                       string      ALL_ROWS  SYS@lhrdb>  alter session set optimizer_mode=first_rows;  Session altered.  SYS@lhrdb> SELECT * FROM SCOTT.EMP WHERE ROWNUM<=0;  no rows selected  SYS@lhrdb> select sql_id, version_count,SQL_TEXT from v$sqlarea where sql_text like 'SELECT * FROM SCOTT.EMP WHERE ROWNUM<=0%';  SQL_ID        VERSION_COUNT  ------------- -------------  7u75n20ktntsb             2  SYS@lhrdb>  select sql_id, child_number, OPTIMIZER_MODE from v$sql where sql_id='7u75n20ktntsb';  SQL_ID        CHILD_NUMBER OPTIMIZER_  ------------- ------------ ----------  7u75n20ktntsb            0 ALL_ROWS  7u75n20ktntsb            1 FIRST_ROWS  SYS@lhrdb> select sql_id, child_number, OPTIMIZER_MODE_MISMATCH from v$sql_shared_cursor where sql_id='7u75n20ktntsb';  SQL_ID        CHILD_NUMBER O  ------------- ------------ -  7u75n20ktntsb            0 N  7u75n20ktntsb            1 Y  

有时候会遇到某些SQL的V$SQL_SHARED_CURSOR所有的字段的结果都为N,但是其Version Count还是很高的情况。这种情况主要的原因是存在部分BUG,可能导致V$SQL_SHARED_CURSOR的信息不准确。例如:

Bug 12539487 – gv$sql_shared_cursor may not show all reasons to not share a cursor (Doc ID 12539487.8)    --所以在Oracle 10g以上版本中可以使用cursortrace来查找High Version Count的原因,打开cursortrace的方法如下所示:  alter system set events 'immediate trace name cursortrace level 577, address <hash_value>';    --如需关闭cursortrace,则可以使用以下方式进行关闭:  alter system set events 'immediate trace name cursortrace level 2147483648, address 1';    --或者使用以下方式关闭:  alter session set events 'immediate trace name cursortrace level 128 , address <address>';  

在MOS 438755.1中,Oracle提供了一个专门的脚本程序,用于协助诊断High Version Count问题。运行脚本version_rpt.sql可以创建函数VERSION_RPT。具体使用的方法有三个场景:

--① 列出Version Count大于某个阈值的报告,以SQL_ID方式显示  SELECT B.*    FROM V$SQLAREA A, TABLE(VERSION_RPT(A.SQL_ID)) B   WHERE LOADED_VERSIONS >= 4;    --② 列出Version Count大于某个阈值的报告,以SQL_HASH方式显示  SELECT B.*    FROM V$SQLAREA A, TABLE(VERSION_RPT(NULL, A.HASH_VALUE)) B   WHERE LOADED_VERSIONS >= 4;    --③ 列出某个特定SQL_ID的Version Count  SELECT * FROM TABLE(VERSION_RPT('7u75n20ktntsb'));  

对于版本过多的SQL,一次软解析甚至不如重新执行一次硬解析来的高效,所以Oracle引入了一系列的控制手段来处理这些特殊的游标。从Oracle 11.2.0.3开始,Oracle提供了一个隐含参数“_CURSOR_OBSOLETE_THRESHOLD”,其作用是当SQL版本超过这个参数设定后,直接舍弃这个游标,重新解析,从头开始,该隐含参数的默认值为100。如果子游标的数量超过了这个阈值,那么父游标就会被废弃,并且同时重新创建一个新的父游标。如果Oracle数据库的版本低于11.2.0.3,那么除了需要给系统打Patch(Enhancement Request Bug 10187168 : OBSOLETE PARENT CURSORS IF VERSION COUNT EXCEEDS A THRESHOLD)外,还同时需要设置以下参数:

--Oracle 11.2.0.1:  SQL> alter system set "_cursor_features_enabled"=34 scope=spfile;  SQL> alter system set event='106001 trace name context forever,level 1024' scope=spfile;    --Oracle 11.2.0.2:  SQL> alter system set "_cursor_features_enabled"=1026 scope=spfile;  SQL> alter system set event='106001 trace name context forever,level 1024' scope=spfile;  

& 说明:

有关Version Count的更多内容可以参考我的BLOG:http://blog.itpub.net/26736162/viewspace-2140136/

本文选自《Oracle程序员面试笔试宝典》,作者:李华荣。