Cardinality Feedback基數反饋是版本11.2中引入的關於SQL 效能優化的新特性,該特性主要針對 統計資訊陳舊、無直方圖或雖然有直方圖但仍基數計算不準確的情況, Cardinality基數的計算直接影響到後續的JOIN COST等重要的成本計算評估,造成CBO選擇不當的執行計劃。以上是Cardinality Feedback特性引入的初衷。
但是每一個Oracle新版本引入的新特性 都被一些老外DBA稱之為buggy ,Cardinality Feedback基數反饋多少也造成了一些麻煩,典型的情況是測試語句效能時,第一次的效能最好,之後再執行其效能變差。
我們來看一下 Cardinality Feedback基數反饋是如何作用的:
注意使用普通使用者來測試Cardinality Feedback,sys使用者被預設禁用該特性
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production With the Partitioning, OLAP, Data Mining and Real Application Testing options SQL> conn maclean/oracle 已連線。 SQL> show parameter dynamic NAME TYPE VALUE ------------------------------------ ---------------------- ------------------------------ optimizer_dynamic_sampling integer 0 SQL> create table test as select * from dba_tables; 表已建立。 SQL> select /*+ gather_plan_statistics */ count(*) from test; COUNT(*) ---------- 2873 SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------ SQL_ID 0p4u1wqwg6t9z, child number 0 ------------------------------------- select /*+ gather_plan_statistics */ count(*) from test Plan hash value: 1950795681 ------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 104 | | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 104 | | 2 | TABLE ACCESS FULL| TEST | 1 | 8904 | 2873 |00:00:00.01 | 104 | ------------------------------------------------------------------------------------- 已選擇14行。 SQL> select /*+ gather_plan_statistics */ count(*) from test; COUNT(*) ---------- 2873 SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST')); PLAN_TABLE_OUTPUT --------------------------------------------------------------------------------------- SQL_ID 0p4u1wqwg6t9z, child number 1 ------------------------------------- select /*+ gather_plan_statistics */ count(*) from test Plan hash value: 1950795681 ------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 104 | | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 104 | | 2 | TABLE ACCESS FULL| TEST | 1 | 2873 | 2873 |00:00:00.01 | 104 | ------------------------------------------------------------------------------------- Note ----- - cardinality feedback used for this statement 已選擇18行。
上例中第一次執行時,由於未收集表上的統計資訊且optimizer_dynamic_sampling=0 關閉了動態取樣所以基數評估值(1)和實際值(2873)有著較大的差距。
cardinality feedback used for this statement這個資訊說明第二次執行時使用了Cardinality Feedback基數反饋,且其基數評估也十分精確了,這是因為第二次執行時考慮到第一次執行時的基數反饋,我們來看看Oracle到底是如何做到的:
SQL> alter system flush shared_pool; 系統已更改。 SQL> SQL> alter session set events '10053 trace name context forever, level 1'; 會話已更改。 SQL> select /*+ gather_plan_statistics */ count(*) from test; COUNT(*) ---------- 2873 SQL> select /*+ gather_plan_statistics */ count(*) from test; COUNT(*) ---------- 2873 10053 trace: 第一次執行: sql= select /*+ gather_plan_statistics */ count(*) from test ----- Explain Plan Dump ----- ----- Plan Table ----- ============ Plan Table ============ --------------------------------------+-----------------------------------+ | Id | Operation | Name | Rows | Bytes | Cost | Time | --------------------------------------+-----------------------------------+ | 0 | SELECT STATEMENT | | | | 31 | | | 1 | SORT AGGREGATE | | 1 | | | | | 2 | TABLE ACCESS FULL | TEST | 8904 | | 31 | 00:00:01 | --------------------------------------+-----------------------------------+ SELECT /*+ OPT_ESTIMATE (TABLE "TEST" ROWS=2873.000000 ) */ COUNT(*) "COUNT(*)" FROM "MACLEAN"."TEST" "TEST" SINGLE TABLE ACCESS PATH Single Table Cardinality Estimation for TEST[TEST] Table: TEST Alias: TEST Card: Original: 8904.000000 >> Single Tab Card adjusted from:8904.000000 to:2873.000000 Rounded: 2873 Computed: 2873.00 Non Adjusted: 8904.00 Access Path: TableScan Cost: 31.10 Resp: 31.10 Degree: 0 Cost_io: 31.00 Cost_cpu: 1991217 Resp_io: 31.00 Resp_cpu: 1991217 Best:: AccessPath: TableScan Cost: 31.10 Degree: 1 Resp: 31.10 Card: 2873.00 Bytes: 0 sql= select /*+ gather_plan_statistics */ count(*) from test ----- Explain Plan Dump ----- ----- Plan Table ----- ============ Plan Table ============ --------------------------------------+-----------------------------------+ | Id | Operation | Name | Rows | Bytes | Cost | Time | --------------------------------------+-----------------------------------+ | 0 | SELECT STATEMENT | | | | 31 | | | 1 | SORT AGGREGATE | | 1 | | | | | 2 | TABLE ACCESS FULL | TEST | 2873 | | 31 | 00:00:01 | --------------------------------------+-----------------------------------+
可以看到第二次執行時SQL最終轉換加入了 OPT_ESTIMATE (TABLE “TEST” ROWS=2873.000000 )的HINT ,OPT_ESTIMATE HINT一般由 kestsaFinalRound()核心函式生成。該HINT用以糾正各種型別的優化器評估,例如某表上的基數或某個列的最大、最小值。反應出優化的不足或者BUG。
可以通過V$SQL_SHARED_CURSOR和來找出現有系統shared pool中仍存在的 使用了Cardinality Feedback基數反饋的子游標:
SQL> select sql_ID,USE_FEEDBACK_STATS FROM V$SQL_SHARED_CURSOR where USE_FEEDBACK_STATS ='Y'; SQL_ID US -------------------------- -- 159sjt1f6khp2 Y
還可以使用cardinality HINT來強制使用Cardinality Feedback 。
select /*+ cardinality(test, 1) */ count(*) from test;
如何禁用Cardinality Feedback基數反饋
對於這些”惹火”特性,為了stable,往往考慮關閉該特性。
可以通過多種方法禁用該特性
1. 使用 _optimizer_use_feedback 隱藏引數
session 級別
SQL> alter session set “_optimizer_use_feedback”=false;
會話已更改。
system級別
SQL> alter system set “_optimizer_use_feedback”=false;
系統已更改。
2. 使用opt_param(‘_optimizer_use_feedback’ ‘false’) HINT
例如:
select /*+ opt_param(‘_optimizer_use_feedback’ ‘false’) cardinality(test,1) */ count(*) from test;