9i以后引入了bind peeking绑定变量窥视特性,但该特性常有帮当忙之嫌,所以有了11g的自适应游标特性。排除因绑定变量窥视造成的因素外,统计信息讹误也会造成执行计划偏差,这时我们就可能需要清除指定游标的缓存信息,从而达到重新解析的目的。
下面我们列举几种可以达到清除游标缓存的方法,权作抛砖引玉:
1. alter system flush shared_pool; /* 最简单最粗暴的方法,清除所有游标缓存,可能造成短期内大量解析,不推荐*/
2. dbms_shared_pool 包很早就有了,但该包名下的purge过程却要到10.2.0.4才出现,Bug 5614566最早在2006年描述了需要清除游标缓存接口的要求:
Hdr: 5614566 10.2.0.2 RDBMS 10.2.0.2 DICTIONARY PRODID-5 PORTID-176
Abstract: WE NEED A FLUSH CURSOR INTERFACE
*** 10/20/06 07:48 am ***
而且该过程在10.2.0.4中默认是无法正常使用的,需要通过设置event 5614566或者打上5614566补丁来启用;具体设置方法如下:
alter system set events ‘5614566 trace name context forever’;
该存储过程的具体argument如下:
PROCEDURE PURGE 参数名称 类型 输入/输出默认值? ------------------------------ ----------------------- ------ -------- NAME VARCHAR2 IN FLAG CHAR IN DEFAULT HEAPS NUMBER IN DEFAULT
其中NAME指定了需要清除的对象名,这里分成2种。PL/SQL对象,触发器,序列,类型和JAVA对象以其命名指定;SQL游标对象通过该SQL的address与hash_value组合指定。FLAG指定了对象的类型,若没有指定该参数,Oracle将认为之前代入的NAME参数对应到包/存储过程/函数的命名空间, 需要注意的是该参数是大小写敏感的,包括了以下各类型:
FLAG值 | 对应对象类型 |
P | 包/存储过程/函数 |
Q | 序列 |
R | 触发器 |
T | 类型 |
JS | Java源程序 |
JC | Java类程序 |
JR | Java资源 |
JD | Java共享数据 |
C | cursor |
HEAP参数指定了清除对象的哪些堆信息,以SQL游标为例,其最主要的信息包括在HEAP 0和HEAP 6中,HEAP 0包括了游标自身的大多数信息,而HEAP 6则存放了游标相关的执行计划。如果我们想要清除HEAP 0和HEAP 6中的信息,则2的0次方+2的6次方=1+64=65,那么我们在代入HEAP参数为65 即可;如果我们只想清除游标的执行计划则清除HEAP 6即可,代入HEAP参数为2的6次方即64。该参数的默认值为1,清除HEAP 0将会导致整个对象的缓存信息被清除掉。
下面我们来演示如何利用该存储过程来清除SQL缓存:
SQL> alter system flush shared_pool; 系统已更改。 SQL> select /* cache_me */ count(*) from youyus; COUNT(*) ---------- 9 SQL> select sql_id, 2 address, 3 hash_value, 4 executions, 5 loads, 6 version_count, 7 invalidations, 8 parse_calls 9 from v$sqlarea 10 where sql_text like '%cache_me%' 11 and sql_text not like '%v$sqlarea%'; SQL_ID ADDRESS HASH_VALUE EXECUTIONS LOADS VERSION_COUNT INVALIDATIONS PARSE_CALLS ------------- -------- ---------- ---------- ---------- ------------- ------------- ----------- 25asu5a76nqmn 2F51508C 2389334644 3 1 1 0 3 SQL> select address, plan_hash_value 2 from v$sql_plan 3 where sql_id = '25asu5a76nqmn'; ADDRESS PLAN_HASH_VALUE -------- --------------- 2F51508C 2542806819 2F51508C 2542806819 2F51508C 2542806819 SQL> exec dbms_shared_pool.purge('2F51508C,2389334644','C',64); PL/SQL 过程已成功完成。 SQL> select sql_id, 2 address, 3 hash_value, 4 executions, 5 loads, 6 version_count, 7 invalidations, 8 parse_calls, 9 plan_hash_value 10 from v$sqlarea 11 where sql_text like '%cache_me%' 12 and sql_text not like '%v$sqlarea%'; SQL_ID ADDRESS HASH_VALUE EXECUTIONS LOADS VERSION_COUNT INVALIDATIONS PARSE_CALLS PLAN_HASH_VALUE ------------- -------- ---------- ---------- ---------- ------------- ------------- ----------- --------------- 25asu5a76nqmn 2F51508C 2389334644 4 1 1 0 4 2542806819 SQL> select * from v$sql_plan where plan_hash_value= 2542806819; 未选定行 /*执行计划消失了,而游标主体信息仍在*/ SQL> select /* cache_me */ count(*) from youyus; COUNT(*) ---------- 9 SQL> select sql_id, 2 address, 3 hash_value, 4 executions, 5 loads, 6 version_count, 7 invalidations, 8 parse_calls, 9 plan_hash_value 10 from v$sqlarea 11 where sql_text like '%cache_me%' 12 and sql_text not like '%v$sqlarea%'; SQL_ID ADDRESS HASH_VALUE EXECUTIONS LOADS VERSION_COUNT INVALIDATIONS PARSE_CALLS PLAN_HASH_VALUE ------------- -------- ---------- ---------- ---------- ------------- ------------- ----------- --------------- 25asu5a76nqmn 2F51508C 2389334644 5 1 1 0 5 2542806819 /*这里新增的一次parse call是硬解析*/ SQL> select address,operation from v$sql_plan where plan_hash_value= 2542806819; ADDRESS OPERATION -------- ------------------------------------------------------------ 2F51508C SELECT STATEMENT 2F51508C SORT 2F51508C TABLE ACCESS SQL> exec dbms_shared_pool.purge('2F51508C,2389334644','C',1); PL/SQL 过程已成功完成。 SQL> select sql_id, 2 address, 3 hash_value, 4 executions, 5 loads, 6 version_count, 7 invalidations, 8 parse_calls, 9 plan_hash_value 10 from v$sqlarea 11 where sql_text like '%cache_me%' 12 and sql_text not like '%v$sqlarea%'; 未选定行 SQL> select address,operation from v$sql_plan where plan_hash_value= 2542806819; 未选定行 SQL> select /* cache_me */ count(*) from youyus; COUNT(*) ---------- 9 SQL> select sql_id, 2 address, 3 hash_value, 4 executions, 5 loads, 6 version_count, 7 invalidations, 8 parse_calls, 9 plan_hash_value 10 from v$sqlarea 11 where sql_text like '%cache_me%' 12 and sql_text not like '%v$sqlarea%'; SQL_ID ADDRESS HASH_VALUE EXECUTIONS LOADS VERSION_COUNT INVALIDATIONS PARSE_CALLS PLAN_HASH_VALUE ------------- -------- ---------- ---------- ---------- ------------- ------------- ----------- --------------- 25asu5a76nqmn 2F51508C 2389334644 1 2 1 1 1 2542806819 /*清除游标heap 0后,包括执行计划的所有信息都被清除了,甚至于simulator中的信息*/
3.如果您的环境中恰好无法利用dbms_shared_pool.purge存储过程,我们也可以采用一些折中的方法来清除游标缓存;譬如通过一个无关紧要的grant/revoke操作,但这样也会造成所有该授权/撤职对象相关SQL的执行计划失效:
SQL> alter system flush shared_pool; 系统已更改。 SQL> select /* cache_me */ count(*) from youyus; COUNT(*) ---------- 9 SQL> select sql_id, 2 address, 3 hash_value, 4 executions, 5 loads, 6 version_count, 7 invalidations, 8 parse_calls, 9 plan_hash_value 10 from v$sqlarea 11 where sql_text like '%cache_me%' 12 and sql_text not like '%v$sqlarea%'; SQL_ID ADDRESS HASH_VALUE EXECUTIONS LOADS VERSION_COUNT INVALIDATIONS PARSE_CALLS PLAN_HASH_VALUE ------------- -------- ---------- ---------- ---------- ------------- ------------- ----------- --------------- 25asu5a76nqmn 2F540EA0 2389334644 1 1 1 0 1 2542806819 SQL> select address,operation,to_char(timestamp,'HH24:MI:SS') from v$sql_plan where plan_hash_value= 2542806819; ADDRESS OPERATION TO_CHAR( -------- ------------------------------------------------------------ -------- 2F540EA0 SELECT STATEMENT 13:39:28 2F540EA0 SORT 13:39:28 2F540EA0 TABLE ACCESS 13:39:28 SQL> revoke select on youyus from scott; 撤销成功。 SQL> select sql_id, 2 address, 3 hash_value, 4 executions, 5 loads, 6 version_count, 7 invalidations, 8 parse_calls, 9 plan_hash_value 10 from v$sqlarea 11 where sql_text like '%cache_me%' 12 and sql_text not like '%v$sqlarea%'; SQL_ID ADDRESS HASH_VALUE EXECUTIONS LOADS VERSION_COUNT INVALIDATIONS PARSE_CALLS PLAN_HASH_VALUE ------------- -------- ---------- ---------- ---------- ------------- ------------- ----------- --------------- 25asu5a76nqmn 2F540EA0 2389334644 1 1 1 1 1 2542806819 /*授权/撤销会造成执行计划invalid,此处 INVALIDATIONS上升到1*/ SQL> select /* cache_me */ count(*) from youyus; COUNT(*) ---------- 9 /*重新执行SQL,将引发一次硬解析*/ SQL> select address,operation,to_char(timestamp,'HH24:MI:SS') from v$sql_plan where plan_hash_value= 2542806819; ADDRESS OPERATION TO_CHAR( -------- ------------------------------------------------------------ -------- 2F540EA0 SELECT STATEMENT 13:40:23 2F540EA0 SORT 13:40:23 2F540EA0 TABLE ACCESS 13:40:23 /*执行计划的时间戳发生了变化,达到了重新解析游标的目的*/
4.或许你不是一个位高权重的DBA,无法执行授权/撤职命令,但如果你能分析游标所涉及对象的统计信息或者执行其他一些ddl操作,那么也可以达到同样的目的:
SQL> alter system flush shared_pool; 系统已更改。 SQL> SQL> select /* cache_me */ count(*) from youyus; COUNT(*) ---------- 9 SQL> select sql_id, 2 address, 3 hash_value, 4 executions, 5 loads, 6 version_count, 7 invalidations, 8 parse_calls, 9 plan_hash_value 10 from v$sqlarea 11 where sql_text like '%cache_me%' 12 and sql_text not like '%v$sqlarea%'; SQL_ID ADDRESS HASH_VALUE EXECUTIONS LOADS VERSION_COUNT INVALIDATIONS PARSE_CALLS PLAN_HASH_VALUE ------------- -------- ---------- ---------- ---------- ------------- ------------- ----------- --------------- 25asu5a76nqmn 2F540EA0 2389334644 1 1 1 0 1 2542806819 SQL> analyze table youyus compute statistics; 表已分析。 SQL> select sql_id, 2 address, 3 hash_value, 4 executions, 5 loads, 6 version_count, 7 invalidations, 8 parse_calls, 9 plan_hash_value 10 from v$sqlarea 11 where sql_text like '%cache_me%' 12 and sql_text not like '%v$sqlarea%'; SQL_ID ADDRESS HASH_VALUE EXECUTIONS LOADS VERSION_COUNT INVALIDATIONS PARSE_CALLS PLAN_HASH_VALUE ------------- -------- ---------- ---------- ---------- ------------- ------------- ----------- --------------- 25asu5a76nqmn 2F540EA0 2389334644 1 1 1 1 1 2542806819 /*统计信息更新,造成了invalid*/ SQL> create index ind_youyus on youyus(t1); 索引已创建。 SQL> alter system flush shared_pool; 系统已更改。 SQL> select /* cache_me */ count(*) from youyus; COUNT(*) ---------- 9 SQL> select sql_id, 2 address, 3 hash_value, 4 executions, 5 loads, 6 version_count, 7 invalidations, 8 parse_calls, 9 plan_hash_value 10 from v$sqlarea 11 where sql_text like '%cache_me%' 12 and sql_text not like '%v$sqlarea%'; SQL_ID ADDRESS HASH_VALUE EXECUTIONS LOADS VERSION_COUNT INVALIDATIONS PARSE_CALLS PLAN_HASH_VALUE ------------- -------- ---------- ---------- ---------- ------------- ------------- ----------- --------------- 25asu5a76nqmn 2F464EA0 2389334644 1 1 1 0 1 2542806819 SQL> alter index ind_youyus rebuild online; 索引已更改。 SQL> select sql_id, 2 address, 3 hash_value, 4 executions, 5 loads, 6 version_count, 7 invalidations, 8 parse_calls, 9 plan_hash_value 10 from v$sqlarea 11 where sql_text like '%cache_me%' 12 and sql_text not like '%v$sqlarea%'; SQL_ID ADDRESS HASH_VALUE EXECUTIONS LOADS VERSION_COUNT INVALIDATIONS PARSE_CALLS PLAN_HASH_VALUE ------------- -------- ---------- ---------- ---------- ------------- ------------- ----------- --------------- 25asu5a76nqmn 2F464EA0 2389334644 1 1 1 1 1 2542806819 /*在线重建索引也可以达到同样的目的*/ That's Great!