Oracle qb_name导致SPM绑定的执行计划错乱
前言
最近生产在为SQL使用SPM绑定执行计划的时候,出现执行计划错乱的问题。
以前都是可以成功绑定的,此次出现问题后查看对应SQL_HANDLE和PLAN_NAME的执行计划发现,该执行计划并非我想要的执行计划。
以前绑过好几条都是成功的,这个唯一不同的是使用了qb_name之后导致了这个问题。
怀疑是BUG,但是并不能再mos上搜索到匹配的问题场景。
接下来模拟下该场景。
模拟
10:28:23 ZKM@test1(1068)> create table t1 as select * from dba_objects; Table created. Elapsed: 00:00:01.01 10:28:53 ZKM@test1(1068)> create table t2 as select * from dba_objects; Table created. Elapsed: 00:00:00.67
目标SQL为:
select count(*) from t1 where not exists ( select 1 from t2 where t1.owner=t2.owner);
运行一遍查找该SQL的SQL ID号:
11:13:08 ZKM@test1(1068)> select count(*) from t1 where not exists ( select 1 from t2 where t1.owner=t2.owner); COUNT(*) ---------- 0 Elapsed: 00:00:00.06 11:13:13 ZKM@test1(1068)> select sql_id from v$sql where sql_text='select count(*) from t1 where not exists ( select 1 from t2 where t1.owner=t2.owner)' and rownum=1; SQL_ID --------------- c9dku2pajc2t1 Elapsed: 00:00:00.00
默认情况下,该SQL执行计划为“hash join anti”。
11:15:35 ZKM@test1(1068)> set pagesize 9999 long 9999 line 500 timing on 11:15:35 ZKM@test1(1068)> alter session set statistics_level=all; Session altered. Elapsed: 00:00:00.00 11:15:35 ZKM@test1(1068)> select count(*) from t1 where not exists ( select 1 from t2 where t1.owner=t2.owner); COUNT(*) ---------- 0 Elapsed: 00:00:00.06 11:15:37 ZKM@test1(1068)> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID c9dku2pajc2t1, child number 2 ------------------------------------- select count(*) from t1 where not exists ( select 1 from t2 where t1.owner=t2.owner) Plan hash value: 1513027705 ----------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem | ----------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.05 | 874 | | | | | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.05 | 874 | | | | |* 2 | HASH JOIN ANTI | | 1 | 22186 | 0 |00:00:00.05 | 874 | 2952K| 2624K| 3184K (0)| | 3 | TABLE ACCESS FULL| T1 | 1 | 22186 | 31002 |00:00:00.01 | 437 | | | | | 4 | TABLE ACCESS FULL| T2 | 1 | 30838 | 31003 |00:00:00.01 | 437 | | | | ----------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("T1"."OWNER"="T2"."OWNER") Note ----- - dynamic sampling used for this statement (level=2) 26 rows selected. Elapsed: 00:00:00.03
接下来使用hint,让cbo走出NESTED LOOP ANTI,如下SQL为:
select /*+ nl_aj(@b1) */ count(*) from t1 where not exists ( select /*+ qb_name(b1) */ 1 from t2 where t1.owner=t2.owner);
详细的执行信息:
11:18:07 ZKM@test1(1068)> select /*+ nl_aj(@b1) */ count(*) from t1 where not exists ( select /*+ qb_name(b1) */ 1 from t2 where t1.owner=t2.owner); COUNT(*) ---------- 0 Elapsed: 00:00:00.20 11:18:08 ZKM@test1(1068)> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 7a9z1syzrc9nw, child number 0 ------------------------------------- select /*+ nl_aj(@b1) */ count(*) from t1 where not exists ( select /*+ qb_name(b1) */ 1 from t2 where t1.owner=t2.owner) Plan hash value: 4091582738 -------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | -------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.20 | 29593 | | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.20 | 29593 | | 2 | NESTED LOOPS ANTI | | 1 | 22186 | 0 |00:00:00.20 | 29593 | | 3 | TABLE ACCESS FULL| T1 | 1 | 22186 | 31002 |00:00:00.02 | 437 | |* 4 | TABLE ACCESS FULL| T2 | 110 | 1 | 110 |00:00:00.17 | 29156 | -------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - filter("T1"."OWNER"="T2"."OWNER") Note ----- - dynamic sampling used for this statement (level=2) 26 rows selected. Elapsed: 00:00:00.03
接下来使用SPM添加基线并查看该基线的执行计划,可以看出默认的执行计划为HASH JOIN ANTI的:
11:19:12 ZKM@test1(1068)> declare 11:19:13 2 l_plans_loaded PLS_INTEGER; 11:19:13 3 begin 11:19:13 4 l_plans_loaded := DBMS_SPM.load_plans_from_cursor_cache(sql_id => 'c9dku2pajc2t1'); 11:19:13 5 DBMS_OUTPUT.put_line('Plans Loaded: ' || l_plans_loaded); 11:19:13 6 END; 11:19:13 7 / PL/SQL procedure successfully completed. Elapsed: 00:00:00.09 11:19:13 ZKM@test1(1068)> col SQL_HANDLE for a25 11:19:18 ZKM@test1(1068)> col plan_name for a35 11:19:18 ZKM@test1(1068)> select signature,sql_handle,plan_name,origin,enabled,accepted,autopurge from dba_sql_plan_baselines where sql_text like 'select count(*) from t1 where not exists ( select 1 from t2 where t1.owner=t2.owner)'; SIGNATURE SQL_HANDLE PLAN_NAME ORIGIN ENABLED ACCEPTED AUTOPURGE ---------- ------------------------- ----------------------------------- ------------------------------------------ --------- --------- --------- 1.1694E+19 SQL_a24a103b54bc3969 SQL_PLAN_a4khh7dabsfb9679d9149 MANUAL-LOAD YES YES YES Elapsed: 00:00:00.00 11:19:19 ZKM@test1(1068)> select * from table(dbms_xplan.display_sql_plan_baseline(sql_handle=>'SQL_a24a103b54bc3969',plan_name=>'SQL_PLAN_a4khh7dabsfb9679d9149')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- SQL handle: SQL_a24a103b54bc3969 SQL text: select count(*) from t1 where not exists ( select 1 from t2 where t1.owner=t2.owner) -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- Plan name: SQL_PLAN_a4khh7dabsfb9679d9149 Plan id: 1738379593 Enabled: YES Fixed: NO Accepted: YES Origin: MANUAL-LOAD -------------------------------------------------------------------------------- Plan hash value: 1513027705 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 34 | 248 (1)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 34 | | | |* 2 | HASH JOIN ANTI | | 36675 | 1217K| 248 (1)| 00:00:01 | | 3 | TABLE ACCESS FULL| T1 | 36675 | 608K| 124 (1)| 00:00:01 | | 4 | TABLE ACCESS FULL| T2 | 36675 | 608K| 124 (1)| 00:00:01 | ---------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("T1"."OWNER"="T2"."OWNER") 28 rows selected. Elapsed: 00:00:00.15
然后把我们想要的执行计划添加进去,并查看新添加的plan_name对应的执行计划:
11:20:56 ZKM@test1(1068)> DECLARE 11:20:57 2 k1 pls_integer; 11:20:57 3 begin 11:20:57 4 k1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE ( 11:20:57 5 sql_id=>'7a9z1syzrc9nw', 11:20:57 6 plan_hash_value=>4091582738,sql_handle=>'SQL_a24a103b54bc3969' 11:20:57 7 ); 11:20:57 8 end; 11:20:57 9 / PL/SQL procedure successfully completed. Elapsed: 00:00:00.04 11:20:57 ZKM@test1(1068)> select signature,sql_handle,plan_name,origin,enabled,accepted,autopurge from dba_sql_plan_baselines where sql_text like 'select count(*) from t1 where not exists ( select 1 from t2 where t1.owner=t2.owner)'; SIGNATURE SQL_HANDLE PLAN_NAME ORIGIN ENABLED ACCEPTED AUTOPURGE ---------- ------------------------- ----------------------------------- ------------------------------------------ --------- --------- --------- 1.1694E+19 SQL_a24a103b54bc3969 SQL_PLAN_a4khh7dabsfb9679d9149 MANUAL-LOAD YES YES YES 1.1694E+19 SQL_a24a103b54bc3969 SQL_PLAN_a4khh7dabsfb96a2152fd MANUAL-LOAD YES YES YES Elapsed: 00:00:00.01 11:21:10 ZKM@test1(1068)> select * from table(dbms_xplan.display_sql_plan_baseline(sql_handle=>'SQL_a24a103b54bc3969',plan_name=>'SQL_PLAN_a4khh7dabsfb96a2152fd')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- SQL handle: SQL_a24a103b54bc3969 SQL text: select count(*) from t1 where not exists ( select 1 from t2 where t1.owner=t2.owner) -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- Plan name: SQL_PLAN_a4khh7dabsfb96a2152fd Plan id: 1780568829 Enabled: YES Fixed: NO Accepted: YES Origin: MANUAL-LOAD -------------------------------------------------------------------------------- Plan hash value: 59119136 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 17 | 3566 (1)| 00:00:03 | | 1 | SORT AGGREGATE | | 1 | 17 | | | |* 2 | FILTER | | | | | | | 3 | TABLE ACCESS FULL| T1 | 36675 | 608K| 124 (1)| 00:00:01 | |* 4 | TABLE ACCESS FULL| T2 | 308 | 5236 | 3 (0)| 00:00:01 | ---------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter( NOT EXISTS (SELECT 0 FROM "T2" "T2" WHERE "T2"."OWNER"=:B1)) 4 - filter("T2"."OWNER"=:B1) Note ----- - dynamic sampling used for this statement (level=2) 34 rows selected. Elapsed: 00:00:00.07
问题来了,可以看出该执行计划和我们添加的plan hash value:4091582738完全不同,而且该计划的id2步骤显示为filter。
操作完全没问题,估计就是个BUG了。
接下来,不使用qb_name这个hint,直接在子查询里边使用nl_aj,看看是否能够正常出现想要的执行计划。
对应SQL为:
select count(*) from t1 where not exists ( select /*+ nl_aj */ 1 from t2 where t1.owner=t2.owner);
详细的执行信息如下:
11:25:36 ZKM@test1(1068)> select count(*) from t1 where not exists ( select /*+ nl_aj */ 1 from t2 where t1.owner=t2.owner); COUNT(*) ---------- 0 Elapsed: 00:00:00.20 11:25:36 ZKM@test1(1068)> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 1wkqm7d8319gw, child number 0 ------------------------------------- select count(*) from t1 where not exists ( select /*+ nl_aj */ 1 from t2 where t1.owner=t2.owner) Plan hash value: 4091582738 -------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | -------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.19 | 29593 | | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.19 | 29593 | | 2 | NESTED LOOPS ANTI | | 1 | 22186 | 0 |00:00:00.19 | 29593 | | 3 | TABLE ACCESS FULL| T1 | 1 | 22186 | 31002 |00:00:00.02 | 437 | |* 4 | TABLE ACCESS FULL| T2 | 110 | 1 | 110 |00:00:00.16 | 29156 | -------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - filter("T1"."OWNER"="T2"."OWNER") Note ----- - dynamic sampling used for this statement (level=2) 26 rows selected. Elapsed: 00:00:00.05 11:25:38 ZKM@test1(1068)> DECLARE 11:25:59 2 k1 pls_integer; 11:25:59 3 begin 11:25:59 4 k1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE ( 11:25:59 5 sql_id=>'1wkqm7d8319gw', 11:25:59 6 plan_hash_value=>4091582738,sql_handle=>'SQL_a24a103b54bc3969' 11:25:59 7 ); 11:25:59 8 end; 11:25:59 9 / PL/SQL procedure successfully completed. Elapsed: 00:00:00.06 11:25:59 ZKM@test1(1068)> select signature,sql_handle,plan_name,origin,enabled,accepted,autopurge from dba_sql_plan_baselines where sql_text like 'select count(*) from t1 where not exists ( select 1 from t2 where t1.owner=t2.owner)'; SIGNATURE SQL_HANDLE PLAN_NAME ORIGIN ENABLED ACCEPTED AUTOPURGE ---------- ------------------------- ----------------------------------- ------------------------------------------ --------- --------- --------- 1.1694E+19 SQL_a24a103b54bc3969 SQL_PLAN_a4khh7dabsfb9679d9149 MANUAL-LOAD YES YES YES 1.1694E+19 SQL_a24a103b54bc3969 SQL_PLAN_a4khh7dabsfb96a2152fd MANUAL-LOAD YES YES YES Elapsed: 00:00:00.00 11:26:06 ZKM@test1(1068)> select * from table(dbms_xplan.display_sql_plan_baseline(sql_handle=>'SQL_a24a103b54bc3969',plan_name=>'SQL_PLAN_a4khh7dabsfb96a2152fd')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- SQL handle: SQL_a24a103b54bc3969 SQL text: select count(*) from t1 where not exists ( select 1 from t2 where t1.owner=t2.owner) -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- Plan name: SQL_PLAN_a4khh7dabsfb96a2152fd Plan id: 1780568829 Enabled: YES Fixed: NO Accepted: YES Origin: MANUAL-LOAD -------------------------------------------------------------------------------- Plan hash value: 4091582738 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 34 | 4480K (1)| 00:53:03 | | 1 | SORT AGGREGATE | | 1 | 34 | | | | 2 | NESTED LOOPS ANTI | | 36675 | 1217K| 4480K (1)| 00:53:03 | | 3 | TABLE ACCESS FULL| T1 | 36675 | 608K| 124 (1)| 00:00:01 | |* 4 | TABLE ACCESS FULL| T2 | 1 | 17 | 122 (0)| 00:00:01 | ---------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - filter("T1"."OWNER"="T2"."OWNER") 28 rows selected. Elapsed: 00:00:00.06
可以看出,已经正常出现想要的执行计划了。。
这估计是BUG吧??