Cardinality Feedback是11GR2出现的一个新特性,它的出现是为了帮助ORACLE优化器依据更精准的Cardinality 产生出更加优秀的执行计划。Cardinality基数的评估准确与否,对于优化器异常重要,直接影响到后续的JOIN COST等重要的成本计算评估,如果评估不当会造成CBO选择不当的执行计划。此技术对于仅执行一次SQL无效,在SQL第一次执行时,记录存储实际的Cardinality 和评估的Cardinality之间的差异,如果差异较大,在第二次执行时,优化器会依据实际的Cardinality 重新决策生成执行计划,但是需要注意的是,当使用更准确的Cardinality重新生成执行计划时,不一定生成的执行计划与第一次时不一样,完全有可能是相同的。 这个技术的出现是由于优化器在一些情况下不能很好的去计算Cardinality的数值,比如:统计信息缺失或陈旧、多谓词、直方图缺失或者缺少直方图等等,在这些情况下,Cardinality Feedback可能会帮上忙。
我们来看下Cardinality Feedback是如何发挥作用的。注意使用普通用户来测试Cardinality Feedback,sys用户被默认禁用该特性
select product_name from order_items o, product_information p where o.unit_price = 15 and quantity > 1 and p.product_id = o.product_id;在order_items表上有2个谓词,o.unit_price = 15 and quantity > 1,由于优化器对于联合谓词评估不准,导致ORACLE优化器认为经过2个联合谓词的过滤, order_items表只返回一条记录,进而优化器选择了nest loop的执行计划。
![](http://img.blog.itpub.net/blog/attachment/201407/10/22034023_1404984748V5Sl.jpg?x-oss-process=style/bb)
如果 Cardinality Feedback被开启,在SQL第一次执行结束后,ORACLE会把实际的Cardinality与评估的Cardinality做比较,如果差异较大,这些实际的Cardinality会被存储以期待SQL再次被执行时这些Cardinality被优化器所使用。我们看看第二次执行的情况:
![](http://img.blog.itpub.net/blog/attachment/201407/10/22034023_14049848820m7z.jpg?x-oss-process=style/bb)
发现ORACLE 优化器依据第一次执行所记录的 Cardinality,重新评估执行计划,在第二次执行时,已经选择了HASH JOIN的执行计划,在执行计划的NOTE部分也看到了cardinality feedback used for this statement字样。11GR2针对此特性,也专门在V$SQL_SHARED_CURSOR中增加了 USE_FEEDBACK_STATS列来记录SQL是否使用了Cardinality Feedback。
如目前我所负责的一个数据库中,有521个SQL都使用到了Cardinality Feedback。
select count(*) FROM V$SQL_SHARED_CURSOR where USE_FEEDBACK_STATS ='Y';
COUNT(*)
----------
521
通过10053 trace两次执行过程,可以很容易发现Cardinality Feedback是如何发挥作用的。在第一次执行时,存储实际的Cardinality 信息,在第二次执行时,通过hint OPT_ESTIMATE (TABLE “ORDER_ITEMS” ROWS=13.000000 )的方式来告诉优化器真实的基数信息。优化器根据真实的基数信息重新评估产生执行计划。
Cardinality Feedback动能的开启和关闭通过一个隐含参数_optimizer_use_feedback来控制。此参数可以在session 和 system级别进行设置。
SQL> alter session set “_optimizer_use_feedback”=false;
会话已更改。
system级别
SQL> alter system set “_optimizer_use_feedback”=false;
系统已更改。
还可以在SQL语句级进行开启和关闭。select /*+ opt_param(‘_optimizer_use_feedback’ ‘false’) */ count(*) from test;
select /*+ opt_param(‘_optimizer_use_feedback’ ‘true’) */ count(*) from test;
Cardinality feedback的信息将存放在cursor中,当Cursor一旦被aged out则会丢失。如果已经采用了 Cardinality Feedback的SQL被刷出共享池,那么在SQL下一次执行的时候,还是会依据表的原始的统计信息来生成执行计划,在第二次执行的时候参考第一次执行时的 Cardinality 重新生成执行计划,如此反复。 因此,如果发现一个SQL的执行性能经常反复,这个SQL可能是使用了 Cardinality Feedback,有必要搞清楚是哪里出现了问题,比如是不是由于统计信息陈旧导致优化器评估了错误的基数进而导致选择了错误的驱动表。
如下一个例子,a表上status='ccc'的值有15355个,由于统计信息陈旧导致了优化器评估只有1个符合条件。
select count(*) from a a1,a a2 where a1.object_id=a2.object_id and
a1.object_name='c' and a1.object_type>'O' and a2.status='ccc'
Plan hash value: 370690737
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 21965 (100)| |
| 1 | SORT AGGREGATE | | 1 | 43 | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 1 | 43 | 21965 (1)| 00:04:24 |
|* 4 | TABLE ACCESS FULL | A | 1 | 12 | 21962 (1)| 00:04:24 |
|* 5 | INDEX RANGE SCAN | TT | 65 | | 1 (0)| 00:00:01 |
|* 6 | TABLE ACCESS BY INDEX ROWID| A | 1 | 31 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("A2"."STATUS"='ccc')
5 - access("A1"."OBJECT_ID"="A2"."OBJECT_ID")
6 - filter(("A1"."OBJECT_NAME"='c' AND "A1"."OBJECT_TYPE">'O'))
第二次执行时,优化器已经使用了 Cardinality Feedback,已经选用了正确的驱动表:
Plan hash value: 370690737
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 22045 (100)| |
| 1 | SORT AGGREGATE | | 1 | 43 | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 47 | 2021 | 22045 (1)| 00:04:25 |
|* 4 | TABLE ACCESS FULL | A | 30 | 930 | 21955 (1)| 00:04:24 |
|* 5 | INDEX RANGE SCAN | TT | 65 | | 1 (0)| 00:00:01 |
|* 6 | TABLE ACCESS BY INDEX ROWID| A | 2 | 24 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter(("A1"."OBJECT_NAME"='c' AND "A1"."OBJECT_TYPE">'O'))
5 - access("A1"."OBJECT_ID"="A2"."OBJECT_ID")
6 - filter("A2"."STATUS"='ccc')
Note
-----
- cardinality feedback used for this statement
把SQL刷出共享池,看看再一次执行,执行计划会是什么:
select address,hash_value,executions,parse_calls from v$sqlarea where sql_id='7s9av17u9k6f5';
ADDRESS HASH_VALUE EXECUTIONS PARSE_CALLS
---------------- ---------- ---------- -----------
00000004A34485C8 4103674309 2 2
1 row selected.
exec dbms_shared_pool.purge('00000004A34485C8,4103674309','C');
select count(*) from a a1,a a2 where a1.object_id=a2.object_id and
a1.object_name='c' and a1.object_type>'O' and a2.status='ccc'
Plan hash value: 370690737
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 21965 (100)| |
| 1 | SORT AGGREGATE | | 1 | 43 | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 1 | 43 | 21965 (1)| 00:04:24 |
|* 4 | TABLE ACCESS FULL | A | 1 | 12 | 21962 (1)| 00:04:24 |
|* 5 | INDEX RANGE SCAN | TT | 65 | | 1 (0)| 00:00:01 |
|* 6 | TABLE ACCESS BY INDEX ROWID| A | 1 | 31 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("A2"."STATUS"='ccc')
5 - access("A1"."OBJECT_ID"="A2"."OBJECT_ID")
6 - filter(("A1"."OBJECT_NAME"='c' AND "A1"."OBJECT_TYPE">'O'))
我们看到执行计划又返回到了第一次执行时的状态,选用了错误的驱动表,再次执行,发现又使用了 cardinality feedback:
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 22045 (100)| |
| 1 | SORT AGGREGATE | | 1 | 43 | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 47 | 2021 | 22045 (1)| 00:04:25 |
|* 4 | TABLE ACCESS FULL | A | 30 | 930 | 21955 (1)| 00:04:24 |
|* 5 | INDEX RANGE SCAN | TT | 65 | | 1 (0)| 00:00:01 |
|* 6 | TABLE ACCESS BY INDEX ROWID| A | 2 | 24 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter(("A1"."OBJECT_NAME"='c' AND "A1"."OBJECT_TYPE">'O'))
5 - access("A1"."OBJECT_ID"="A2"."OBJECT_ID")
6 - filter("A2"."STATUS"='ccc')
Note
-----
- cardinality feedback used for this statement
已知的 cardinatilty feedback 问题
Fixed in 11.2.0.2
Note 8608703.8 Bug 8608703 - SubOptimal Execution Plan created by Cardinality Feedback
Note 9465425.8 Bug 9465425 - New cursors generated after cardinality feedback
Note 9342979.8 Bug 9342979 Suboptimal plan change with cardinatilty feedback
Fixed in 12g
Note 8521689.8 Bug 8521689 - SubOptimal execution plan on second execution of GROUP BY query
Note 8729064.8 Bug 8729064 Adaptive cursor sharing fails to share / USE_FEEDBACK_STATS not set
推荐阅读:
【11g新特性】Cardinality Feedback基数反馈
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/22034023/viewspace-1216002/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/22034023/viewspace-1216002/