Oracle 11gR2 新技术 Cardinality Feedback

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,由于优化器对于联合谓词评估不准,导致优化器认为经过2个联合谓词的过滤,order_items表只返回一条记录,进而优化器选择了nest loop的执行计划。

Oracle 11gR2 新技术 Cardinality Feedback

如果开启Cardinality Feedback,在SQL第一次执行结束后,会把实际的Cardinality与评估的Cardinality做比较。如果差异较大,实际的Cardinality会被存储以期待SQL再次被执行时这些Cardinality被优化器所使用。

我们看看第二次执行的情况:

Oracle 11gR2 新技术 Cardinality Feedback

优化器依据第一次执行所记录的Cardinality,重新评估执行计划,第二次执行时,已经选择了HASH JOIN的执行计划,在执行计划的NOTE部分也看到了cardinality feedback used for this statement字样。

针对此特性,也专门在V$SQL_SHARED_CURSOR中增加了 USE_FEEDBACK_STATS列来记录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级别进行设置。

alter session set "_optimizer_use_feedback"=false;
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

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

cardinality feedback技术给优化器提供了更加精准的cardinality来帮助优化器更容易产生优秀的执行计划,ORACLE里也有其他的一些技术来提升评估cardinality的精准性,如dynamic sampling、多列联合的统计信息收集等,如果采用了这些技术,cardinality feedback技术将不再被采用。

已知的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


推荐阅读:

http://www.askmaclean.com/archives/11g-new-feature-cardinality-feedback.html

https://blogs.oracle.com/optimizer/entry/cardinality_feedback

http://www.dba-oracle.com/t_cardinality_feedback.htm

http://kerryosborne.oracle-guy.com/2011/07/cardinality-feedback/

http://oradbastuff.blogspot.com/2012/07/cardinality-feedback-nasty-feature-in.html

上一篇:python--django 实现 建议反馈管理页面 源码分享


下一篇:Deep learning-based CSI Feedback for Beamforming 1