本站文章除注明转载外,均为本站原创:转载自 love wife & love life —Roger 的Oracle技术博客
本文链接地址: _optimizer_null_aware_antijoin引发的SQL性能问题
前几天某客户联系我说之前我们进行存储迁移的系统,有个SQL跑的极慢,根本跑不出来结果。通过VPN登录看了下,SQL确认跑的很慢。开始我很难理解,我们仅仅是进行了存储迁移,数据库基本上没动,为什么会有SQL性能问题呢? 我们先来看看有问题的SQL:
SYS@rptdb1> set autot traceonly exp SYS@rptdb1> select a.*,b.rate TAX_RATE,round(a.charge*b.rate/(1+b.rate),0) tax,a.charge-round((a.charge*b.rate/(1+b.rate)),0) charge_flh,1 flag,b.tax_rule_id 2 from statrpt.rpt_offer_rate b,statrpt.tmp_item_aggr_ex_691 a 3 where a.acct_item_type_id = b.acct_item_type_id 4 and a.offer_cd =b.offer_ID 5 union all 6 SYS@rptdb1> select a.*,b.rate TAX_RATE,round(a.charge*b.rate/(1+b.rate),0) tax,a.charge-ROUND((a.charge*b.rate/(1+b.rate)),0) charge_flh,2,b.tax_rule_id 2 from statrpt.rpt_product_rate b,statrpt.tmp_item_aggr_ex_691 a 3 where a.acct_item_type_id = b.acct_item_type_id 4 and a.product_id=b.product_id 5 and (a.acct_item_type_id,a.offer_cd) not in(select acct_item_type_id,offer_id from statrpt.rpt_offer_rate) 6 union all 7 SYS@rptdb1> select a.*,b.rate TAX_RATE,round(a.charge*b.rate/(1+b.rate),0) tax,a.charge-round((a.charge*b.rate/(1+b.rate)),0) charge_flh,3,b.tax_rule_id 2 from statrpt.rpt_zm_rate b,statrpt.tmp_item_aggr_ex_691 a 3 where a.acct_item_type_id = b.acct_item_type_id 4 and (a.acct_item_type_id,a.offer_cd)not in(select acct_item_type_id,offer_id from statrpt.rpt_offer_rate ) 5 and (a.acct_item_type_id,a.product_id) not in(select acct_item_type_id,product_id from statrpt.rpt_product_rate ) 6 union all 7 select a.*,b.rate TAX_RATE,round(a.charge*b.rate/(1+b.rate),0) tax,a.charge-round((a.charge*b.rate/(1+b.rate)),0) charge_flh,4,b.tax_rule_id 8 from statrpt.tmp_zm_only_rate b,statrpt.tmp_item_aggr_ex_691 a 9 where a.acct_item_type_id = b.acct_item_type_id 10 and (a.acct_item_type_id,a.offer_cd)not in(select acct_item_type_id,offer_id from statrpt.rpt_offer_rate ) 11 and (a.acct_item_type_id,a.product_id) not in(select acct_item_type_id,product_id from statrpt.rpt_product_rate ) 12 and (a.acct_item_type_id) not in(select acct_item_type_id from statrpt.rpt_zm_rate ) 13 / Elapsed: 00:00:00.00 Execution Plan ---------------------------------------------------------- Plan hash value: 1624413711 ------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib | ------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 6983K| 765M| 563M (51)|999:59:59 | | | | | 1 | UNION-ALL | | | | | | | | | |* 2 | FILTER | | | | | | | | | | 3 | PX COORDINATOR | | | | | | | | | | 4 | PX SEND QC (RANDOM) | :TQ60001 | 3494K| 383M| 1050 (1)| 00:00:13 | Q6,01 | P->S | QC (RAND) | |* 5 | HASH JOIN | | 3494K| 383M| 1050 (1)| 00:00:13 | Q6,01 | PCWP | | | 6 | PX RECEIVE | | 1034 | 14476 | 3 (0)| 00:00:01 | Q6,01 | PCWP | | | 7 | PX SEND BROADCAST | :TQ60000 | 1034 | 14476 | 3 (0)| 00:00:01 | Q6,00 | P->P | BROADCAST | | 8 | PX BLOCK ITERATOR | | 1034 | 14476 | 3 (0)| 00:00:01 | Q6,00 | PCWC | | | 9 | TABLE ACCESS FULL| RPT_ZM_RATE | 1034 | 14476 | 3 (0)| 00:00:01 | Q6,00 | PCWP | | | 10 | PX BLOCK ITERATOR | | 3494K| 336M| 1046 (1)| 00:00:13 | Q6,01 | PCWC | | | 11 | TABLE ACCESS FULL | TMP_ITEM_AGGR_EX_691 | 3494K| 336M| 1046 (1)| 00:00:13 | Q6,01 | PCWP | | | 12 | PX COORDINATOR | | | | | | | | | | 13 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 9 | 8 (0)| 00:00:01 | Q1,00 | P->S | QC (RAND) | | 14 | PX BLOCK ITERATOR | | 1 | 9 | 8 (0)| 00:00:01 | Q1,00 | PCWC | | |* 15 | TABLE ACCESS FULL | RPT_OFFER_RATE | 1 | 9 | 8 (0)| 00:00:01 | Q1,00 | PCWP | | | 16 | PX COORDINATOR | | | | | | | | | | 17 | PX SEND QC (RANDOM) | :TQ20000 | 1 | 9 | 96 (2)| 00:00:02 | Q2,00 | P->S | QC (RAND) | | 18 | PX BLOCK ITERATOR | | 1 | 9 | 96 (2)| 00:00:02 | Q2,00 | PCWC | | |* 19 | TABLE ACCESS FULL | RPT_PRODUCT_RATE | 1 | 9 | 96 (2)| 00:00:02 | Q2,00 | PCWP | | |* 20 | FILTER | | | | | | | | | | 21 | PX COORDINATOR | | | | | | | | | | 22 | PX SEND QC (RANDOM) | :TQ70001 | 3494K| 383M| 1050 (1)| 00:00:13 | Q7,01 | P->S | QC (RAND) | |* 23 | HASH JOIN | | 3494K| 383M| 1050 (1)| 00:00:13 | Q7,01 | PCWP | | | 24 | PX RECEIVE | | 6053 | 84742 | 3 (0)| 00:00:01 | Q7,01 | PCWP | | | 25 | PX SEND BROADCAST | :TQ70000 | 6053 | 84742 | 3 (0)| 00:00:01 | Q7,00 | P->P | BROADCAST | | 26 | PX BLOCK ITERATOR | | 6053 | 84742 | 3 (0)| 00:00:01 | Q7,00 | PCWC | | | 27 | TABLE ACCESS FULL| TMP_ZM_ONLY_RATE | 6053 | 84742 | 3 (0)| 00:00:01 | Q7,00 | PCWP | | | 28 | PX BLOCK ITERATOR | | 3494K| 336M| 1046 (1)| 00:00:13 | Q7,01 | PCWC | | | 29 | TABLE ACCESS FULL | TMP_ITEM_AGGR_EX_691 | 3494K| 336M| 1046 (1)| 00:00:13 | Q7,01 | PCWP | | | 30 | PX COORDINATOR | | | | | | | | | | 31 | PX SEND QC (RANDOM) | :TQ30000 | 1 | 9 | 8 (0)| 00:00:01 | Q3,00 | P->S | QC (RAND) | | 32 | PX BLOCK ITERATOR | | 1 | 9 | 8 (0)| 00:00:01 | Q3,00 | PCWC | | |* 33 | TABLE ACCESS FULL | RPT_OFFER_RATE | 1 | 9 | 8 (0)| 00:00:01 | Q3,00 | PCWP | | | 34 | PX COORDINATOR | | | | | | | | | | 35 | PX SEND QC (RANDOM) | :TQ40000 | 1 | 9 | 96 (2)| 00:00:02 | Q4,00 | P->S | QC (RAND) | | 36 | PX BLOCK ITERATOR | | 1 | 9 | 96 (2)| 00:00:02 | Q4,00 | PCWC | | |* 37 | TABLE ACCESS FULL | RPT_PRODUCT_RATE | 1 | 9 | 96 (2)| 00:00:02 | Q4,00 | PCWP | | | 38 | PX COORDINATOR | | | | | | | | | | 39 | PX SEND QC (RANDOM) | :TQ50000 | 1 | 5 | 3 (0)| 00:00:01 | Q5,00 | P->S | QC (RAND) | | 40 | PX BLOCK ITERATOR | | 1 | 5 | 3 (0)| 00:00:01 | Q5,00 | PCWC | | |* 41 | TABLE ACCESS FULL | RPT_ZM_RATE | 1 | 5 | 3 (0)| 00:00:01 | Q5,00 | PCWP | | ------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter( NOT EXISTS (SELECT 0 FROM "STATRPT"."RPT_OFFER_RATE" "RPT_OFFER_RATE" WHERE LNNVL("ACCT_ITEM_TYPE_ID"<>:B1) AND LNNVL("OFFER_ID"<>:B2)) AND NOT EXISTS (SELECT 0 FROM "STATRPT"."RPT_PRODUCT_RATE" "RPT_PRODUCT_RATE" WHERE LNNVL("ACCT_ITEM_TYPE_ID"<>:B3) AND LNNVL("PRODUCT_ID"<>:B4))) 5 - access("A"."ACCT_ITEM_TYPE_ID"="B"."ACCT_ITEM_TYPE_ID") 15 - filter(LNNVL("ACCT_ITEM_TYPE_ID"<>:B1) AND LNNVL("OFFER_ID"<>:B2)) 19 - filter(LNNVL("ACCT_ITEM_TYPE_ID"<>:B1) AND LNNVL("PRODUCT_ID"<>:B2)) 20 - filter( NOT EXISTS (SELECT 0 FROM "STATRPT"."RPT_OFFER_RATE" "RPT_OFFER_RATE" WHERE LNNVL("ACCT_ITEM_TYPE_ID"<>:B1) AND LNNVL("OFFER_ID"<>:B2)) AND NOT EXISTS (SELECT 0 FROM "STATRPT"."RPT_PRODUCT_RATE" "RPT_PRODUCT_RATE" WHERE LNNVL("ACCT_ITEM_TYPE_ID"<>:B3) AND LNNVL("PRODUCT_ID"<>:B4)) AND NOT EXISTS (SELECT 0 FROM "STATRPT"."RPT_ZM_RATE" "RPT_ZM_RATE" WHERE LNNVL("ACCT_ITEM_TYPE_ID"<>:B5))) 23 - access("A"."ACCT_ITEM_TYPE_ID"="B"."ACCT_ITEM_TYPE_ID") 33 - filter(LNNVL("ACCT_ITEM_TYPE_ID"<>:B1) AND LNNVL("OFFER_ID"<>:B2)) 37 - filter(LNNVL("ACCT_ITEM_TYPE_ID"<>:B1) AND LNNVL("PRODUCT_ID"<>:B2)) 41 - filter(LNNVL("ACCT_ITEM_TYPE_ID"<>:B1))
大家看该SQL的执行计划就知道,COST巨大无比,很显然这个SQL基本上是跑不动的。本人SQL优化比较弱,因此直接从原库进行对比,因此在原库跑了下SQL:
Plan hash value: 2514835211 --------------------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib | --------------------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | 3493 (100)| | | | | | 1 | UNION-ALL | | | | | | | | | | | 2 | PX COORDINATOR | | | | | | | | | | | 3 | PX SEND QC (RANDOM) | :TQ10004 | 557 | 71853 | | 1745 (3)| 00:00:21 | Q1,04 | P->S | QC (RAND) | |* 4 | HASH JOIN BUFFERED | | 557 | 71853 | | 1745 (3)| 00:00:21 | Q1,04 | PCWP | | | 5 | PX RECEIVE | | 557 | 64055 | | 1742 (3)| 00:00:21 | Q1,04 | PCWP | | | 6 | PX SEND HASH | :TQ10002 | 557 | 64055 | | 1742 (3)| 00:00:21 | Q1,02 | P->P | HASH | | 7 | MERGE JOIN ANTI NA | | 557 | 64055 | | 1742 (3)| 00:00:21 | Q1,02 | PCWP | | | 8 | SORT JOIN | | 55738 | 5769K| 12M| 1733 (3)| 00:00:21 | Q1,02 | PCWP | | | 9 | MERGE JOIN ANTI NA | | 55738 | 5769K| | 1732 (3)| 00:00:21 | Q1,02 | PCWP | | | 10 | SORT JOIN | | 5573K| 515M| 1643M| 1631 (2)| 00:00:20 | Q1,02 | PCWP | | | 11 | PX BLOCK ITERATOR | | 5573K| 515M| | 1614 (1)| 00:00:20 | Q1,02 | PCWC | | |* 12 | TABLE ACCESS FULL | TMP_ITEM_AGGR_EX_691 | 5573K| 515M| | 1614 (1)| 00:00:20 | Q1,02 | PCWP | | |* 13 | SORT UNIQUE | | 421K| 3704K| 16M| 101 (5)| 00:00:02 | Q1,02 | PCWP | | | 14 | PX RECEIVE | | 421K| 3704K| | 96 (0)| 00:00:02 | Q1,02 | PCWP | | | 15 | PX SEND BROADCAST | :TQ10000 | 421K| 3704K| | 96 (0)| 00:00:02 | Q1,00 | P->P | BROADCAST | | 16 | PX BLOCK ITERATOR | | 421K| 3704K| | 96 (0)| 00:00:02 | Q1,00 | PCWC | | |* 17 | TABLE ACCESS FULL | RPT_PRODUCT_RATE | 421K| 3704K| | 96 (0)| 00:00:02 | Q1,00 | PCWP | | |* 18 | SORT UNIQUE | | 22695 | 199K| | 9 (12)| 00:00:01 | Q1,02 | PCWP | | | 19 | PX RECEIVE | | 22695 | 199K| | 8 (0)| 00:00:01 | Q1,02 | PCWP | | | 20 | PX SEND BROADCAST | :TQ10001 | 22695 | 199K| | 8 (0)| 00:00:01 | Q1,01 | P->P | BROADCAST | | 21 | PX BLOCK ITERATOR | | 22695 | 199K| | 8 (0)| 00:00:01 | Q1,01 | PCWC | | |* 22 | TABLE ACCESS FULL | RPT_OFFER_RATE | 22695 | 199K| | 8 (0)| 00:00:01 | Q1,01 | PCWP | | | 23 | PX RECEIVE | | 1059 | 14826 | | 3 (0)| 00:00:01 | Q1,04 | PCWP | | | 24 | PX SEND HASH | :TQ10003 | 1059 | 14826 | | 3 (0)| 00:00:01 | Q1,03 | P->P | HASH | | 25 | PX BLOCK ITERATOR | | 1059 | 14826 | | 3 (0)| 00:00:01 | Q1,03 | PCWC | | |* 26 | TABLE ACCESS FULL | RPT_ZM_RATE | 1059 | 14826 | | 3 (0)| 00:00:01 | Q1,03 | PCWP | | | 27 | PX COORDINATOR | | | | | | | | | | | 28 | PX SEND QC (RANDOM) | :TQ20004 | 6 | 804 | | 1748 (3)| 00:00:21 | Q2,04 | P->S | QC (RAND) | |* 29 | HASH JOIN | | 6 | 804 | | 1748 (3)| 00:00:21 | Q2,04 | PCWP | | | 30 | PX RECEIVE | | 6 | 720 | | 1745 (3)| 00:00:21 | Q2,04 | PCWP | | | 31 | PX SEND BROADCAST | :TQ20003 | 6 | 720 | | 1745 (3)| 00:00:21 | Q2,03 | P->P | BROADCAST | | 32 | MERGE JOIN ANTI NA | | 6 | 720 | | 1745 (3)| 00:00:21 | Q2,03 | PCWP | | | 33 | SORT JOIN | | 557 | 61827 | | 1736 (3)| 00:00:21 | Q2,03 | PCWP | | |* 34 | HASH JOIN RIGHT ANTI NA| | 557 | 61827 | | 1735 (3)| 00:00:21 | Q2,03 | PCWP | | | 35 | PX RECEIVE | | 1059 | 5295 | | 3 (0)| 00:00:01 | Q2,03 | PCWP | | | 36 | PX SEND BROADCAST | :TQ20000 | 1059 | 5295 | | 3 (0)| 00:00:01 | Q2,00 | P->P | BROADCAST | | 37 | PX BLOCK ITERATOR | | 1059 | 5295 | | 3 (0)| 00:00:01 | Q2,00 | PCWC | | |* 38 | TABLE ACCESS FULL | RPT_ZM_RATE | 1059 | 5295 | | 3 (0)| 00:00:01 | Q2,00 | PCWP | | | 39 | MERGE JOIN ANTI NA | | 55738 | 5769K| | 1732 (3)| 00:00:21 | Q2,03 | PCWP | | | 40 | SORT JOIN | | 5573K| 515M| 1643M| 1631 (2)| 00:00:20 | Q2,03 | PCWP | | | 41 | PX BLOCK ITERATOR | | 5573K| 515M| | 1614 (1)| 00:00:20 | Q2,03 | PCWC | | |* 42 | TABLE ACCESS FULL | TMP_ITEM_AGGR_EX_691 | 5573K| 515M| | 1614 (1)| 00:00:20 | Q2,03 | PCWP | | |* 43 | SORT UNIQUE | | 421K| 3704K| 16M| 101 (5)| 00:00:02 | Q2,03 | PCWP | | | 44 | PX RECEIVE | | 421K| 3704K| | 96 (0)| 00:00:02 | Q2,03 | PCWP | | | 45 | PX SEND BROADCAST | :TQ20001 | 421K| 3704K| | 96 (0)| 00:00:02 | Q2,01 | P->P | BROADCAST | | 46 | PX BLOCK ITERATOR | | 421K| 3704K| | 96 (0)| 00:00:02 | Q2,01 | PCWC | | |* 47 | TABLE ACCESS FULL| RPT_PRODUCT_RATE | 421K| 3704K| | 96 (0)| 00:00:02 | Q2,01 | PCWP | | |* 48 | SORT UNIQUE | | 22695 | 199K| | 9 (12)| 00:00:01 | Q2,03 | PCWP | | | 49 | PX RECEIVE | | 22695 | 199K| | 8 (0)| 00:00:01 | Q2,03 | PCWP | | | 50 | PX SEND BROADCAST | :TQ20002 | 22695 | 199K| | 8 (0)| 00:00:01 | Q2,02 | P->P | BROADCAST | | 51 | PX BLOCK ITERATOR | | 22695 | 199K| | 8 (0)| 00:00:01 | Q2,02 | PCWC | | |* 52 | TABLE ACCESS FULL | RPT_OFFER_RATE | 22695 | 199K| | 8 (0)| 00:00:01 | Q2,02 | PCWP | | | 53 | PX BLOCK ITERATOR | | 6083 | 85162 | | 3 (0)| 00:00:01 | Q2,04 | PCWC | | |* 54 | TABLE ACCESS FULL | TMP_ZM_ONLY_RATE | 6083 | 85162 | | 3 (0)| 00:00:01 | Q2,04 | PCWP | | --------------------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("A"."ACCT_ITEM_TYPE_ID"="B"."ACCT_ITEM_TYPE_ID") 12 - access(:Z>=:Z AND :Z<=:Z) 13 - access(INTERNAL_FUNCTION("A"."ACCT_ITEM_TYPE_ID")=INTERNAL_FUNCTION("ACCT_ITEM_TYPE_ID") AND INTERNAL_FUNCTION("A"."PRODUCT_ID")=INTERNAL_FUNCTION("PRODUCT_ID")) filter((INTERNAL_FUNCTION("A"."PRODUCT_ID")=INTERNAL_FUNCTION("PRODUCT_ID") AND INTERNAL_FUNCTION("A"."ACCT_ITEM_TYPE_ID")=INTERNAL_FUNCTION("ACCT_ITEM_TYPE_ID"))) 17 - access(:Z>=:Z AND :Z<=:Z) 18 - access(INTERNAL_FUNCTION("A"."ACCT_ITEM_TYPE_ID")=INTERNAL_FUNCTION("ACCT_ITEM_TYPE_ID") AND INTERNAL_FUNCTION("A"."OFFER_CD")=INTERNAL_FUNCTION("OFFER_ID")) filter((INTERNAL_FUNCTION("A"."OFFER_CD")=INTERNAL_FUNCTION("OFFER_ID") AND INTERNAL_FUNCTION("A"."ACCT_ITEM_TYPE_ID")=INTERNAL_FUNCTION("ACCT_ITEM_TYPE_ID"))) 22 - access(:Z>=:Z AND :Z<=:Z) 26 - access(:Z>=:Z AND :Z<=:Z) 29 - access("A"."ACCT_ITEM_TYPE_ID"="B"."ACCT_ITEM_TYPE_ID") 34 - access("A"."ACCT_ITEM_TYPE_ID"="ACCT_ITEM_TYPE_ID") 38 - access(:Z>=:Z AND :Z<=:Z) 42 - access(:Z>=:Z AND :Z<=:Z) 43 - access(INTERNAL_FUNCTION("A"."ACCT_ITEM_TYPE_ID")=INTERNAL_FUNCTION("ACCT_ITEM_TYPE_ID") AND INTERNAL_FUNCTION("A"."PRODUCT_ID")=INTERNAL_FUNCTION("PRODUCT_ID")) filter((INTERNAL_FUNCTION("A"."PRODUCT_ID")=INTERNAL_FUNCTION("PRODUCT_ID") AND INTERNAL_FUNCTION("A"."ACCT_ITEM_TYPE_ID")=INTERNAL_FUNCTION("ACCT_ITEM_TYPE_ID"))) 47 - access(:Z>=:Z AND :Z<=:Z) 48 - access(INTERNAL_FUNCTION("A"."ACCT_ITEM_TYPE_ID")=INTERNAL_FUNCTION("ACCT_ITEM_TYPE_ID") AND INTERNAL_FUNCTION("A"."OFFER_CD")=INTERNAL_FUNCTION("OFFER_ID")) filter((INTERNAL_FUNCTION("A"."OFFER_CD")=INTERNAL_FUNCTION("OFFER_ID") AND INTERNAL_FUNCTION("A"."ACCT_ITEM_TYPE_ID")=INTERNAL_FUNCTION("ACCT_ITEM_TYPE_ID"))) 52 - access(:Z>=:Z AND :Z<=:Z) 54 - access(:Z>=:Z AND :Z<=:Z)
很明显,原库的执行计划要好的,通过对比执行计划,我们发现:性能较差的SQL的执行计划中,not in 被改写成了not exits,进行了一些filter操作。而性能较高的SQL的执行计划,则是选择了ANTI Join。问题是原来为什么ok ?存储迁移之后就有问题了呢 ?第一感觉可能是调整了优化器参数,检查发现果然是:
SYS@rptdb1> show parameter optimizer NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ _optimizer_adaptive_cursor_sharing boolean FALSE _optimizer_extended_cursor_sharing string NONE _optimizer_extended_cursor_sharing_r string NONE el _optimizer_null_aware_antijoin boolean FALSE _optimizer_use_feedback boolean FALSE optimizer_capture_sql_plan_baselines boolean FALSE optimizer_dynamic_sampling integer 2 optimizer_features_enable string 11.2.0.2 optimizer_index_caching integer 0 optimizer_index_cost_adj integer 100 optimizer_mode string ALL_ROWS optimizer_secure_view_merging boolean TRUE optimizer_use_invisible_indexes boolean FALSE optimizer_use_pending_statistics boolean FALSE optimizer_use_sql_plan_baselines boolean TRUE SYS@rptdb1> SYS@rptdb1> alter session set "_optimizer_null_aware_antijoin"=true; Session altered.
通过将该参数改回默认值,测试一切正常。 这里我主要是通过SQLT来解决该SQL的性能问题,首先创建一个SQL profile,然后修改SQL profile的查询块信息即可,如下:
q'[OPT_PARAM('_optimizer_null_aware_antijoin' 'true')]', q'[OPT_PARAM('_optimizer_extended_cursor_sharing' 'none')]', q'[OPT_PARAM('_optimizer_extended_cursor_sharing_rel' 'none')]', q'[OPT_PARAM('_optimizer_adaptive_cursor_sharing' 'false')]', q'[OPT_PARAM('_optimizer_use_feedback' 'false')]',
通过调整之后,SQL性能恢复正常。 虽然这是一个很常见的问题,然而我却是第一次在生产中碰见,下面进行一个简单的测试。
说明:测试脚本来自google。
—For 10.2.0.5
www.killdb.com> create table t1 2 as select 3 cast(rownum as int) a, 4 cast(rownum+10 as int) b, 5 cast(dbms_random.string('i',10) as varchar2(10)) c 6 from dual connect by level<=10000; Table created. www.killdb.com> create table t2 2 as select 3 cast(rownum as int) a, 4 cast(rownum+10 as int) b, 5 cast(dbms_random.string('i',10) as varchar2(10)) c 6 from dual connect by level<=9980; Table created. www.killdb.com> www.killdb.com> set autot traceonly exp www.killdb.com> analyze table t1 compute statistics; Table analyzed. www.killdb.com> analyze table t2 compute statistics; Table analyzed. www.killdb.com> select /*SQL_1*/ c from t1 where a not in (select a from t2) ; Execution Plan ---------------------------------------------------------- Plan hash value: 895956251 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 9999 | 126K| 60407 (1)| 00:12:05 | |* 1 | FILTER | | | | | | | 2 | TABLE ACCESS FULL| T1 | 10000 | 126K| 12 (0)| 00:00:01 | |* 3 | TABLE ACCESS FULL| T2 | 1 | 3 | 12 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter( NOT EXISTS (SELECT 0 FROM "T2" "T2" WHERE LNNVL("A"<>:B1))) 3 - filter(LNNVL("A"<>:B1)) www.killdb.com> alter table t2 modify a not null ; Table altered. www.killdb.com> select /*SQL_2*/ c from t1 where a not in (select a from t2) ; Execution Plan ---------------------------------------------------------- Plan hash value: 895956251 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 9999 | 126K| 60407 (1)| 00:12:05 | |* 1 | FILTER | | | | | | | 2 | TABLE ACCESS FULL| T1 | 10000 | 126K| 12 (0)| 00:00:01 | |* 3 | TABLE ACCESS FULL| T2 | 1 | 3 | 12 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter( NOT EXISTS (SELECT 0 FROM "T2" "T2" WHERE LNNVL("A"<>:B1))) 3 - filter(LNNVL("A"<>:B1)) www.killdb.com> create index idx_t2_a on t2(a); Index created. www.killdb.com> create index idx_t1_a on t1(a); Index created. www.killdb.com> select /*SQL_3*/ c from t1 where a not in (select a from t2) ; Execution Plan ---------------------------------------------------------- Plan hash value: 377637984 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 9999 | 126K| 35333 (1)| 00:07:04 | |* 1 | FILTER | | | | | | | 2 | TABLE ACCESS FULL | T1 | 10000 | 126K| 12 (0)| 00:00:01 | |* 3 | INDEX FAST FULL SCAN| IDX_T2_A | 1 | 3 | 7 (0)| 00:00:01 | ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter( NOT EXISTS (SELECT 0 FROM "T2" "T2" WHERE LNNVL("A"<>:B1))) 3 - filter(LNNVL("A"<>:B1)) www.killdb.com>
我们可以看到,仍然没有走办连接,还是走filter了,这里的类似nest loop,很明显效率很低,其原因是需要用T1表的每条记录去和T2 返回的结果集进行匹配。那么有没有办法让SQL走半连接呢 ? 肯定是可以的,如下:
www.killdb.com> alter table t1 modify a not null ; Table altered. www.killdb.com> select /*SQL_4*/ c from t1 where a not in (select a from t2) ; Execution Plan ---------------------------------------------------------- Plan hash value: 1490751970 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 20 | 320 | 20 (5)| 00:00:01 | |* 1 | HASH JOIN RIGHT ANTI | | 20 | 320 | 20 (5)| 00:00:01 | | 2 | INDEX FAST FULL SCAN| IDX_T2_A | 9980 | 29940 | 7 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL | T1 | 10000 | 126K| 12 (0)| 00:00:01 | ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("A"="A")
我们可以看到,走半连接之后,效率明显要高的多。当然,这里不对t1表进行not null操作也可以进行优化。
—-for 11.2.0.2 test
[ora11g@localhost ~]$ sqlplus "/as sysdba" SQL*Plus: Release 11.2.0.2.0 Production on Sat Apr 18 22:59:32 2015 Copyright (c) 1982, 2010, Oracle. All rights reserved. Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - Production With the Partitioning, Automatic Storage Management, OLAP, Data Mining and Real Application Testing options www.killdb.com> conn roger/roger Connected. www.killdb.com> SQL> create table t1 2 as select 3 cast(rownum as int) a, 4 cast(rownum+10 as int) b, 5 cast(dbms_random.string('i',10) as varchar2(10)) c 6 from dual connect by level<=10000; Table created. SQL> create table t2 2 as select 3 cast(rownum as int) a, 4 cast(rownum+10 as int) b, 5 cast(dbms_random.string('i',10) as varchar2(10)) c 6 from dual connect by level<=9980; Table created. SQL> analyze table t1 compute statistics ; Table analyzed. SQL> analyze table t2 compute statistics; Table analyzed. SQL> set autot traceonly exp SQL> select /*SQL_1*/ c from t1 where a not in (select a from t2) ; Execution Plan ---------------------------------------------------------- Plan hash value: 2739594415 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 100 | 1600 | 23 (5)| 00:00:01 | |* 1 | HASH JOIN RIGHT ANTI NA| | 100 | 1600 | 23 (5)| 00:00:01 | | 2 | TABLE ACCESS FULL | T2 | 9980 | 29940 | 11 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL | T1 | 10000 | 126K| 11 (0)| 00:00:01 | -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("A"="A") SQL> alter session set "_optimizer_null_aware_antijoin"=false; Session altered. SQL> select /*SQL_2*/ c from t1 where a not in (select a from t2) ; Execution Plan ---------------------------------------------------------- Plan hash value: 895956251 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 9999 | 126K| 55478 (1)| 00:11:06 | |* 1 | FILTER | | | | | | | 2 | TABLE ACCESS FULL| T1 | 10000 | 126K| 11 (0)| 00:00:01 | |* 3 | TABLE ACCESS FULL| T2 | 1 | 3 | 11 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter( NOT EXISTS (SELECT 0 FROM "T2" "T2" WHERE LNNVL("A"<>:B1))) 3 - filter(LNNVL("A"<>:B1)) SQL> alter table t2 modify a not null; Table altered. SQL> create index idx_t2_a on t2(a); Index created. SQL> create index idx_t1_a on t1(a); Index created. SQL> SQL> alter session set "_optimizer_null_aware_antijoin"=true; Session altered. SQL> select /*SQL_3*/ c from t1 where a not in (select a from t2) ; Execution Plan ---------------------------------------------------------- Plan hash value: 2568882110 ------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 100 | 1600 | 19 (6)| 00:00:01 | |* 1 | HASH JOIN RIGHT ANTI SNA| | 100 | 1600 | 19 (6)| 00:00:01 | | 2 | INDEX FAST FULL SCAN | IDX_T2_A | 9980 | 29940 | 7 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL | T1 | 10000 | 126K| 11 (0)| 00:00:01 | ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("A"="A") SQL> alter session set "_optimizer_null_aware_antijoin"=false; Session altered. SQL> select /*SQL_3*/ c from t1 where a not in (select a from t2) ; Execution Plan ---------------------------------------------------------- Plan hash value: 377637984 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 9999 | 126K| 35396 (2)| 00:07:05 | |* 1 | FILTER | | | | | | | 2 | TABLE ACCESS FULL | T1 | 10000 | 126K| 11 (0)| 00:00:01 | |* 3 | INDEX FAST FULL SCAN| IDX_T2_A | 1 | 3 | 7 (0)| 00:00:01 | ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter( NOT EXISTS (SELECT 0 FROM "T2" "T2" WHERE LNNVL("A"<>:B1))) 3 - filter(LNNVL("A"<>:B1)) SQL> alter table t1 modify a not null ; Table altered. SQL> select /*SQL_3*/ c from t1 where a not in (select a from t2) ; Execution Plan ---------------------------------------------------------- Plan hash value: 1490751970 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 100 | 1600 | 19 (6)| 00:00:01 | |* 1 | HASH JOIN RIGHT ANTI | | 100 | 1600 | 19 (6)| 00:00:01 | | 2 | INDEX FAST FULL SCAN| IDX_T2_A | 9980 | 29940 | 7 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL | T1 | 10000 | 126K| 11 (0)| 00:00:01 | ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("A"="A")
实际上,通过我们测试可以发现,本质上应用SQL出问题,不是我们调整参数的问题,而是应用SQL写法不规范导致。或者说应用表结构设计存在缺陷导致。实际上该SQL,我们不需要调整隐含参数,通过对表的column 添加非空约束即可。