Oracle里的cursor分为两种:一种是shared cursor,一种是session cursor。 本测试主要说明shared cursour
shared cursor就是缓存在shared pool 里的一种 library cache object。 主要缓存sql和匿名的pl/sql。它们是Oracle缓存在library cache中的几十种library cache之一,它所属的namespace是 cursor(Oracle通过namespace来管理schema object的名字)。
Shared pool 主要组成由Library cache 和 Data Dictionary cache:
- Library Cache主要用于存储SQL语句、SQL语句相关的解析树、执行计划、PL/SQL程序块(包括匿名程序块、存储过程、包、函数等)以及它们转换后能够被Oracle执行的代码等,这部分信息可以通过v$librarycache视图查询;
- Data Dictionary Cache主要用于存放数据字典信息,包括表、视图等对象的结构信息,用户以及对象权限信息,这部分信息相对稳定,在Shared Pool中通过字典缓存单独存放,字典缓存的内容是按行(Row)存储的(其他数据通常按Buffer存储),所以又被称为Row Cache,其信息可以通过v$rowcache查询。
shared SQL, parent cursor和child cursor 概念:
SQL都是implicitly shareable。 当用户发出一条SQL后,Oracle会根据SQL文本内容生成hash value,以便能够快速找到 Shared pool已经存在的相同SQL。如果找不到,则Oracle会为这个SQL创建一个parent cursor和一个child cursor,这与SQL是否共享是没有关系的。
- parent cursor包含了SQL TEXT和相关的hash value,v$sqlarea中的每一行代表了一个parent cursor,根据address表示了其内存地址。
-
child cursor包含了SQL的metadata,即使得这个SQL可以执行的所有相关信息,如OBJECT和权限,优化器设置,执行计划等。v$sql中中 的每一行表示了一个child cursor,根据hash value和address与parent cursor 关联。child cursor有自己的address,即V$SQL.CHILD_ADDRESS。
第一个child cursor总是使用0来表示其创建顺序,V$SQL.CHILD_NUMBER = 0。因此,当从V$SQL_PLAN中查找某个SQL的执行计划时,要注意你写对了CHILD_NUMBER。
如果有多个child cursor,则表示parent cursor有多个版本,v$sqlarea中的version_count字段就会纪录下来。
我们通过下面进行测试:
--不使用绑定变量,执行以下几个sql,之后的结果
SQL> col sql_text for a60
SQL> set lines 200
SQL> select sql_text, hash_value,address from v$sqlarea where sql_text like 'select count(*) from emp%';
SQL_TEXT HASH_VALUE ADDRESS
------------------------------------------------------------ ---------- ----------------
select count(*) from emp where empno=7499 3945401509 000000009F88D7A8
select count(*) from emp where empno=7989 3561150173 000000009F3D1CE0
SQL>
6 rows selected.
通过上述结果可以看到,如果不适用绑定变量,没执行一次语句(条件值不同),都会生成父游标和子游标,这就是硬解析。
通过使用绑定变量方式:
--制定7499,7989
var x number;
exec :x := 7989;
select count(*) from emp2 where empno=:x;
SQL> select sql_text, hash_value,address from v$sqlarea where sql_text like 'select count(*) from emp%';
SQL_TEXT HASH_VALUE ADDRESS
------------------------------------------------------------ ---------- ----------------
select count(*) from emp where empno=7499 3945401509 000000009F88D7A8
select count(*) from emp where empno=7989 3561150173 000000009F3D1CE0
select count(*) from emp2 where empno=:x 3447257031 000000007CAFCFA0
--查看子游标,一个
SQL> select sql_id,address,CHILD_ADDRESS,CHILD_NUMBER from v$sql_shared_cursor where address='000000007CAFCFA0';
SQL_ID ADDRESS CHILD_ADDRESS CHILD_NUMBER
------------- ---------------- ---------------- ------------
cv0ar6b6rjxy7 000000007CAFCFA0 00000000A97BB8B0 0
--加大数值,执行后再次查看
SQL> var x number;
exec :x := 798900;
select count(*) from emp2 where empno=:x; SQL>
PL/SQL procedure successfully completed.
SQL>
COUNT(*)
----------
0
SQL> select sql_id,address,CHILD_ADDRESS,CHILD_NUMBER from v$sql_shared_cursor where address='000000007CAFCFA0';
SQL_ID ADDRESS CHILD_ADDRESS CHILD_NUMBER
------------- ---------------- ---------------- ------------
cv0ar6b6rjxy7 000000007CAFCFA0 00000000A97BB8B0 0
cv0ar6b6rjxy7 000000007CAFCFA0 000000007CF93560 1
--查看不共享原因,字段中 是Y的对应相关列进行检查。 如下所示,本例 是BIND_EQUIV_FAILURE ( 绑定值的选择性与用于优化现有子游标的选择性不匹配)导致
SQL> select * from v$sql_shared_cursor where address='000000007CAFCFA0';
SQL_ID ADDRESS CHILD_ADDRESS CHILD_NUMBER U S O O S L F E B P I S T A B D L T B I I R L I O E M U T N F A P T D L D B P C S C P T M B M R O P M F L P L A F L R L H P B U
------------- ---------------- ---------------- ------------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
REASON
--------------------------------------------------------------------------------
cv0ar6b6rjxy7 000000007CAFCFA0 00000000A97BB8B0 0 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N Y N N N N N N N N N
0 40 Bind mismatch(25)
子游标不共享原因有很多,我们可以通过上述视图 v$sql_shared_cursor 进行查看,做进行sql处理时,尽量规范操作。
参考: