Oracle優化器之基數反饋(CardinalityFeedback)功能
概述
在Oracle 11gR2的版本上推出了基數反饋(Cardinality Feedback 以後簡稱CFB)功能,通過這個特性,對於某些查詢在第一次執行時,如果CBO發現根據統計資訊估算出的基數(Computed cardinality)和SQL執行時的實際值差距很大的情況發生時,在SQL下次執行時,會根據實際值調整基數,重新生成執行計劃。
另外,基數反饋 (CFB)在12c版本上得到更進一步的擴充套件改稱為統計反饋(Statistics Feedback),成為12c自動重新優化(Automatic Reoptimization)的一部分。
關於這統計反饋(Statistics Feedback)中擴充套件的內容和12c自動重新優化(Automatic Reoptimization)的內容,將在以後的章節中進行介紹。
下面我們將通過幾個例子來了解一下CFB功能。
例子1(CFB無效)
首先我們在10.2.0.5的環境中也就是CFB無效的情況下,看看執行的情況:
(我們使用了Oracle資料庫提供的樣例Schema OE 及其表PRODUCT_INFORMATION和ORDER_ITEMS進行測試。)
1.首先確認相關表的統計資訊和表的資料量。(基於10.2.0.5版本測試)
--統計資訊能夠反映出表中的資料量。 SQL> select TABLE_NAME,NUM_ROWS,BLOCKS from user_tables where TABLE_NAME in ('PRODUCT_INFORMATION','ORDER_ITEMS'); TABLE_NAME NUM_ROWS BLOCKS -------------------- ---------- ---------- ORDER_ITEMS 665 5 PRODUCT_INFORMATION 288 13 SQL> select count(*) from ORDER_ITEMS; COUNT(*) ---------- 665 SQL> select count(*) from PRODUCT_INFORMATION; COUNT(*) ---------- 288
2.設定環境引數statistics_level為ALL,以便能夠通過dbms_xplan.display_cursor函式檢視SQL文根據統計資訊估算出的訪問資料行數和SQL執行時的實際值。
SQL> alter session set statistics_level=all;
Session altered.
3.第一次執行SQL
SQL> SELECT o.order_id, v.product_name 2 FROM orders o, 3 ( SELECT order_id, product_name 4 FROM order_items o, product_information p 5 WHERE p.product_id = o.product_id 6 AND list_price < 50 7 AND min_price < 40 ) v 8 WHERE o.order_id = v.order_id 9 ; ORDER_ID PRODUCT_NAME ---------- -------------------- 2354 Sound Card STD ... 2457 Graphics - DIK+ 269 rows selected.
4.檢視第一次執行後的執行計劃
SQL> select * from table(dbms_xplan.display_cursor(NULL, NULL,'typical iostats last -cost -bytes'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------
SQL_ID bmh5hb8331u33, child number 0
-------------------------------------
SELECT o.order_id, v.product_name FROM orders o, ( SELECT order_id, product_name FROM
order_items o, product_information p WHERE p.product_id = o.product_id AND list_price < 50
AND min_price < 40 ) v WHERE o.order_id = v.order_id
Plan hash value: 1906736282
---------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | E-Time | A-Rows | A-Time | Buffers | Reads |
---------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 269 |00:00:00.44 | 9189 | 20 |
| 1 | NESTED LOOPS | | 1 | 1 | 00:00:01 | 269 |00:00:00.44 | 9189 | 20 |
| 2 | MERGE JOIN CARTESIAN| | 1 | 4 | 00:00:01 | 9135 |00:00:00.17 | 35 | 15 |
|* 3 | TABLE ACCESS FULL | PRODUCT_INFORMATION | 1 | 1 ★| 00:00:01 | 87 ★|00:00:00.01 | 34 | 14 |
| 4 | BUFFER SORT | | 87 | 105 | 00:00:01 | 9135 |00:00:00.07 | 1 | 1 |
| 5 | INDEX FULL SCAN | ORDER_PK | 1 | 105 | 00:00:01 | 105 |00:00:00.01 | 1 | 1 |
|* 6 | INDEX UNIQUE SCAN | ORDER_ITEMS_UK | 9135 | 1 | | 269 |00:00:00.18 | 9154 | 5 |
---------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("MIN_PRICE"<40 AND "LIST_PRICE"<50))
6 - access("O"."ORDER_ID"="ORDER_ID" AND "P"."PRODUCT_ID"="O"."PRODUCT_ID")
26 rows selected.
SQL> ---檢視V$SQL的統計資訊。
SQL> select sql_id,child_number, executions, buffer_gets,plan_hash_value
2 from v$sql
3 where sql_id = 'bmh5hb8331u33';
SQL_ID CHILD_NUMBER EXECUTIONS BUFFER_GETS PLAN_HASH_VALUE
------------- ------------ ---------- ----------- ---------------
bmh5hb8331u33 0 1 9701 1906736282
我們發現由於訪問條件(“MIN_PRICE”<40 AND “LIST_PRICE”<50)的影響,優化器認為PRODUCT_INFORMATION表的預估行數(E-Rows)為1,優化器基於預估基數在選擇表PRODUCT_INFORMATION和ORDER_ITEMS結合的最優執行計劃時,選擇了MERGE JOIN CARTESIAN的結合方式。
但實際實際訪問行數(A-Time:87),因此由於預估基數不準,很有可能導致選擇的執行計劃不是最優的。
5.我們再多次執行相同的SQL文
---第二次執行
SQL> SELECT o.order_id, v.product_name
2 FROM orders o,
3 ( SELECT order_id, product_name
4 FROM order_items o, product_information p
5 WHERE p.product_id = o.product_id
6 AND list_price < 50
7 AND min_price < 40 ) v
8 WHERE o.order_id = v.order_id
9 ;
ORDER_ID PRODUCT_NAME
---------- --------------------
2354 Sound Card STD
...
2457 Graphics - DIK+
269 rows selected.
SQL>
---第二次執行的執行計劃
SQL> select * from table(dbms_xplan.display_cursor(NULL, NULL,'typical iostats last -cost -bytes'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------
SQL_ID bmh5hb8331u33, child number 0
-------------------------------------
SELECT o.order_id, v.product_name FROM orders o, ( SELECT order_id, product_name FROM
order_items o, product_information p WHERE p.product_id = o.product_id AND
list_price < 50 AND min_price < 40 ) v WHERE o.order_id = v.order_id
Plan hash value: 1906736282
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | E-Time | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 269 |00:00:00.45 | 9189 |
| 1 | NESTED LOOPS | | 1 | 1 | 00:00:01 | 269 |00:00:00.45 | 9189 |
| 2 | MERGE JOIN CARTESIAN| | 1 | 4 | 00:00:01 | 9135 |00:00:00.17 | 35 |
|* 3 | TABLE ACCESS FULL | PRODUCT_INFORMATION | 1 | 1 | 00:00:01 | 87 |00:00:00.01 | 34 |
| 4 | BUFFER SORT | | 87 | 105 | 00:00:01 | 9135 |00:00:00.06 | 1 |
| 5 | INDEX FULL SCAN | ORDER_PK | 1 | 105 | 00:00:01 | 105 |00:00:00.01 | 1 |
|* 6 | INDEX UNIQUE SCAN | ORDER_ITEMS_UK | 9135 | 1 | | 269 |00:00:00.18 | 9154 |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("MIN_PRICE"<40 AND "LIST_PRICE"<50))
6 - access("O"."ORDER_ID"="ORDER_ID" AND "P"."PRODUCT_ID"="O"."PRODUCT_ID")
26 rows selected.
---第三次執行的執行計劃
SQL> SELECT o.order_id, v.product_name
2 FROM orders o,
3 ( SELECT order_id, product_name
4 FROM order_items o, product_information p
5 WHERE p.product_id = o.product_id
6 AND list_price < 50
7 AND min_price < 40 ) v
8 WHERE o.order_id = v.order_id
9 ;
ORDER_ID PRODUCT_NAME
---------- --------------------
2354 Sound Card STD
...
2457 Graphics - DIK+
269 rows selected.
--第三次執行的執行計劃
SQL> select * from table(dbms_xplan.display_cursor(NULL, NULL,'typical iostats last -cost -bytes'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------
SQL_ID bmh5hb8331u33, child number 0
-------------------------------------
SELECT o.order_id, v.product_name FROM orders o, ( SELECT order_id, product_name FROM
order_items o, product_information p WHERE p.product_id = o.product_id AND
list_price < 50 AND min_price < 40 ) v WHERE o.order_id = v.order_id
Plan hash value: 1906736282
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | E-Time | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 269 |00:00:00.45 | 9189 |
| 1 | NESTED LOOPS | | 1 | 1 | 00:00:01 | 269 |00:00:00.45 | 9189 |
| 2 | MERGE JOIN CARTESIAN| | 1 | 4 | 00:00:01 | 9135 |00:00:00.16 | 35 |
|* 3 | TABLE ACCESS FULL | PRODUCT_INFORMATION | 1 | 1 | 00:00:01 | 87 |00:00:00.01 | 34 |
| 4 | BUFFER SORT | | 87 | 105 | 00:00:01 | 9135 |00:00:00.07 | 1 |
| 5 | INDEX FULL SCAN | ORDER_PK | 1 | 105 | 00:00:01 | 105 |00:00:00.01 | 1 |
|* 6 | INDEX UNIQUE SCAN | ORDER_ITEMS_UK | 9135 | 1 | | 269 |00:00:00.19 | 9154 |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("MIN_PRICE"<40 AND "LIST_PRICE"<50))
6 - access("O"."ORDER_ID"="ORDER_ID" AND "P"."PRODUCT_ID"="O"."PRODUCT_ID")
26 rows selected.
我們發現,雖然根據統計資訊估算出的基數(Computed cardinality)和SQL執行時的實際值不同,但是以後的執行過程中,SQL文依然會利用以前的執行計劃(軟解析)。
在這個情況下,很有可能由於最初優化器沒有選擇最優的執行計劃,在以後的重複執行中也得不到改進而導致效率問題。
例子2(CFB有效)
下面我們在11.2.0.4的環境中也就是CFB有效的情況下,看看執行的情況:
(我們依然使用Oracle資料庫提供的樣例Schema OE 及其表PRODUCT_INFORMATION和ORDER_ITEMS進行測試。)
1.首先確認相關表的統計資訊和表的資料量。(基於11.2.0.4版本測試)
--統計資訊能夠反映出表中的資料量。
SQL> select TABLE_NAME,NUM_ROWS,BLOCKS from user_tables where TABLE_NAME in ('PRODUCT_INFORMATION','ORDER_ITEMS');
TABLE_NAME NUM_ROWS BLOCKS
-------------------- ---------- ----------
ORDER_ITEMS 665 5
PRODUCT_INFORMATION 288 13
Elapsed: 00:00:00.04
SQL> select count(*) from ORDER_ITEMS;
COUNT(*)
----------
665
Elapsed: 00:00:00.02
SQL> select count(*) from PRODUCT_INFORMATION;
COUNT(*)
----------
288
Elapsed: 00:00:00.01
SQL>
2.設定環境引數statistics_level為ALL,以便能夠通過dbms_xplan.display_cursor函式檢視SQL文根據統計資訊估算出的訪問資料行數和SQL執行時的實際值。
SQL> alter session set statistics_level=all;
Session altered.
Elapsed: 00:00:00.01
3.第一次執行SQL
SQL>
SQL> SELECT o.order_id, v.product_name
2 FROM orders o,
3 ( SELECT order_id, product_name
4 FROM order_items o, product_information p
5 WHERE p.product_id = o.product_id
6 AND list_price < 50
7 AND min_price < 40 ) v
8 WHERE o.order_id = v.order_id
9 ;
ORDER_ID PRODUCT_NAME
---------- --------------------
2403 Battery - EL
...
2450 Plastic Stock - W/HD
269 rows selected.
Elapsed: 00:00:00.22
SQL>
4.檢視第一次執行後的執行計劃
SQL> select * from table(dbms_xplan.display_cursor(format=>'typical iostats last -cost -bytes'));
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------
SQL_ID bmh5hb8331u33, child number 0
-------------------------------------
SELECT o.order_id, v.product_name FROM orders o, ( SELECT
order_id, product_name FROM order_items o,
product_information p WHERE p.product_id = o.product_id
AND list_price < 50 AND min_price < 40 ) v WHERE
o.order_id = v.order_id
Plan hash value: 1906736282
---------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | E-Time | A-Rows | A-Time | Buffers | Reads |
---------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 269 |00:00:00.17 | 1337 | 20 |
| 1 | NESTED LOOPS | | 1 | 1 | 00:00:01 | 269 |00:00:00.17 | 1337 | 20 |
| 2 | MERGE JOIN CARTESIAN| | 1 | 4 | 00:00:01 | 9135 |00:00:00.06 | 33 | 15 |
|* 3 | TABLE ACCESS FULL | PRODUCT_INFORMATION | 1 | ★ 1 | 00:00:01 | ★ 87 |00:00:00.01 | 32 | 14 |
| 4 | BUFFER SORT | | 87 | 105 | 00:00:01 | 9135 |00:00:00.02 | 1 | 1 |
| 5 | INDEX FULL SCAN | ORDER_PK | 1 | 105 | 00:00:01 | 105 |00:00:00.01 | 1 | 1 |
|* 6 | INDEX UNIQUE SCAN | ORDER_ITEMS_UK | 9135 | 1 | | 269 |00:00:00.05 | 1304 | 5 |
---------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(("MIN_PRICE"<40 AND "LIST_PRICE"<50))
6 - access("O"."ORDER_ID"="ORDER_ID" AND "P"."PRODUCT_ID"="O"."PRODUCT_ID")
28 rows selected.
Elapsed: 00:00:00.19
我們發現和10.2.0.5環境一樣,由於訪問條件(“MIN_PRICE”<40 AND “LIST_PRICE”<50)的影響,優化器認為PRODUCT_INFORMATION表的預估行數(E-Rows)為1,優化器基於預估基數在選擇表PRODUCT_INFORMATION和ORDER_ITEMS結合的最優執行計劃時,選擇了MERGE JOIN CARTESIAN的結合方式。
5.檢視動態檢視VSQL和VSQL_SHARED_CURSOR
SQL> ---sql_id:bmh5hb8331u33
SQL> select sql_id,child_number, executions, buffer_gets,plan_hash_value
2 from v$sql
3 where sql_id = 'bmh5hb8331u33';
SQL_ID CHILD_NUMBER EXECUTIONS BUFFER_GETS PLAN_HASH_VALUE
------------- ------------ ---------- ----------- ---------------
bmh5hb8331u33 0 1 1604 1906736282
Elapsed: 00:00:00.01
SQL>
SQL> select sql_id, child_number, USE_FEEDBACK_STATS
2 from V$SQL_SHARED_CURSOR
3 where sql_id = 'bmh5hb8331u33';
SQL_ID CHILD_NUMBER U
------------- ------------ -
bmh5hb8331u33 0 Y
Elapsed: 00:00:00.04
SQL>
我們發現V$SQL_SHARED_CURSOR的USE_FEEDBACK_STATS列標記為Y。
(USE_FEEDBACK_STATS列是在11.2.0.4 的版本上新追加的列,用於標示當根據統計資訊估算出的基數(Computed cardinality)和SQL執行時的實際值差距很大時,下次執行時重新生成執行計劃)
6.我們再次次執行相同的SQL文
---第二次執行
SQL> SELECT o.order_id, v.product_name
2 FROM orders o,
3 ( SELECT order_id, product_name
4 FROM order_items o, product_information p
5 WHERE p.product_id = o.product_id
6 AND list_price < 50
7 AND min_price < 40 ) v
8 WHERE o.order_id = v.order_id
9 ;
ORDER_ID PRODUCT_NAME
---------- --------------------
2403 Battery - EL
...
2401 SPNIX3.3 AU
269 rows selected.
Elapsed: 00:00:00.03★
SQL>
我們發現執行時間變短了。
7.再次檢視執行計劃
SQL> select * from table(dbms_xplan.display_cursor(format=>'typical iostats last -cost -bytes'));
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------
SQL_ID bmh5hb8331u33, child number 1
-------------------------------------
SELECT o.order_id, v.product_name FROM orders o, ( SELECT
order_id, product_name FROM order_items o,
product_information p WHERE p.product_id = o.product_id
AND list_price < 50 AND min_price < 40 ) v WHERE
o.order_id = v.order_id
Plan hash value: 35479787
----------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | E-Time | A-Rows | A-Time | Buffers | Reads |
----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 269 |00:00:00.01 | 61 | 1 |
| 1 | NESTED LOOPS | | 1 | 313 | 00:00:01 | 269 |00:00:00.01 | 61 | 1 |
|* 2 | HASH JOIN | | 1 | 313 | 00:00:01 | 269 |00:00:00.01 | 40 | 1 |
|* 3 | TABLE ACCESS FULL | PRODUCT_INFORMATION | 1 | ★87 | 00:00:01 | ★87 |00:00:00.01 | 15 | 0 |
| 4 | INDEX FAST FULL SCAN| ORDER_ITEMS_UK | 1 | 665 | 00:00:01 | 665 |00:00:00.01 | 25 | 1 |
|* 5 | INDEX UNIQUE SCAN | ORDER_PK | 269 | 1 | | 269 |00:00:00.01 | 21 | 0 |
----------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("P"."PRODUCT_ID"="O"."PRODUCT_ID")
3 - filter(("MIN_PRICE"<40 AND "LIST_PRICE"<50))
5 - access("O"."ORDER_ID"="ORDER_ID")
Note
-----
- cardinality feedback used for this statement ★
32 rows selected.
Elapsed: 00:00:00.03
我們發現SQL文進行了硬解析,並且表PRODUCT_INFORMATION的預估資訊(E-Rows)調整為第一次執行時收集的實際值(87),用於優化器選擇執行計劃。因此,優化器基於調整後預估基數在選擇表PRODUCT_INFORMATION和ORDER_ITEMS結合的最優執行計劃時,選擇了HASH JOIN的結合方式,從而更有效的執行了SQL文。
8.再次檢視動態檢視VSQL和VSQL_SHARED_CURSOR
SQL> ---sql_id:bmh5hb8331u33
SQL> select sql_id,child_number, executions, buffer_gets,plan_hash_value,is_shareable
2 from v$sql
3 where sql_id = 'bmh5hb8331u33';
SQL_ID CHILD_NUMBER EXECUTIONS BUFFER_GETS PLAN_HASH_VALUE I
------------- ------------ ---------- ----------- --------------- -
bmh5hb8331u33 0 1 1604 1906736282 N★
bmh5hb8331u33 1 1 61 35479787 Y★
Elapsed: 00:00:00.02
SQL>
SQL> select sql_id, child_number, USE_FEEDBACK_STATS
2 from V$SQL_SHARED_CURSOR
3 where sql_id = 'bmh5hb8331u33';
SQL_ID CHILD_NUMBER U
------------- ------------ -
bmh5hb8331u33 0 Y
bmh5hb8331u33 1 N
Elapsed: 00:00:00.00
通過檢視V$SQL我們發現,新生成的遊標CHILD#1比以前的遊標CHILD#1會使用更少的BUFFER_GETS,效率更高。並且以前遊標CHILD#0的is_shareable列標記為N,不在被共享。
新生成的遊標CHILD#1的is_shareable列標記為Y,供以後的執行重用。
9.再多次執行SQL文
--第三次執行
SQL> SELECT o.order_id, v.product_name
2 FROM orders o,
3 ( SELECT order_id, product_name
4 FROM order_items o, product_information p
5 WHERE p.product_id = o.product_id
6 AND list_price < 50
7 AND min_price < 40 ) v
8 WHERE o.order_id = v.order_id
9 ;
ORDER_ID PRODUCT_NAME
---------- --------------------
2403 Battery - EL
...
2401 SPNIX3.3 AU
269 rows selected.
Elapsed: 00:00:00.06
SQL>
--檢視執行計劃
SQL> set line 200
SQL> set pagesize 9999
SQL>
SQL> select * from table(dbms_xplan.display_cursor(format=>'typical iostats last -cost -bytes'));
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------
SQL_ID bmh5hb8331u33, child number 1
-------------------------------------
SELECT o.order_id, v.product_name FROM orders o, ( SELECT
order_id, product_name FROM order_items o,
product_information p WHERE p.product_id = o.product_id
AND list_price < 50 AND min_price < 40 ) v WHERE
o.order_id = v.order_id
Plan hash value: 35479787
-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | E-Time | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 269 |00:00:00.03 | 61 |
| 1 | NESTED LOOPS | | 1 | 313 | 00:00:01 | 269 |00:00:00.03 | 61 |
|* 2 | HASH JOIN | | 1 | 313 | 00:00:01 | 269 |00:00:00.02 | 40 |
|* 3 | TABLE ACCESS FULL | PRODUCT_INFORMATION | 1 | 87 | 00:00:01 | 87 |00:00:00.01 | 15 |
| 4 | INDEX FAST FULL SCAN| ORDER_ITEMS_UK | 1 | 665 | 00:00:01 | 665 |00:00:00.01 | 25 |
|* 5 | INDEX UNIQUE SCAN | ORDER_PK | 269 | 1 | | 269 |00:00:00.01 | 21 |
-------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("P"."PRODUCT_ID"="O"."PRODUCT_ID")
3 - filter(("MIN_PRICE"<40 AND "LIST_PRICE"<50))
5 - access("O"."ORDER_ID"="ORDER_ID")
Note
-----
- cardinality feedback used for this statement
32 rows selected.
Elapsed: 00:00:00.07
--檢視動態檢視
SQL> ---sql_id:bmh5hb8331u33
SQL> select sql_id,child_number, executions, buffer_gets,plan_hash_value,is_shareable
2 from v$sql
3 where sql_id = 'bmh5hb8331u33';
SQL_ID CHILD_NUMBER EXECUTIONS BUFFER_GETS PLAN_HASH_VALUE I
------------- ------------ ---------- ----------- --------------- -
bmh5hb8331u33 0 1 1604 1906736282 N
bmh5hb8331u33 1 2 122 35479787 Y
Elapsed: 00:00:00.00
SQL>
SQL> select sql_id, child_number, USE_FEEDBACK_STATS
2 from V$SQL_SHARED_CURSOR
3 where sql_id = 'bmh5hb8331u33';
SQL_ID CHILD_NUMBER U
------------- ------------ -
bmh5hb8331u33 0 Y
bmh5hb8331u33 1 N
Elapsed: 00:00:00.00
--第四次執行
SQL> SELECT o.order_id, v.product_name
2 FROM orders o,
3 ( SELECT order_id, product_name
4 FROM order_items o, product_information p
5 WHERE p.product_id = o.product_id
6 AND list_price < 50
7 AND min_price < 40 ) v
8 WHERE o.order_id = v.order_id
9 ;
ORDER_ID PRODUCT_NAME
---------- --------------------
2403 Battery - EL
...
2401 SPNIX3.3 AU
269 rows selected.
Elapsed: 00:00:00.05
SQL>
--檢視執行計劃
SQL> set line 200
SQL> set pagesize 9999
SQL>
SQL> select * from table(dbms_xplan.display_cursor(format=>'typical iostats last -cost -bytes'));
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------
SQL_ID bmh5hb8331u33, child number 1
-------------------------------------
SELECT o.order_id, v.product_name FROM orders o, ( SELECT
order_id, product_name FROM order_items o,
product_information p WHERE p.product_id = o.product_id
AND list_price < 50 AND min_price < 40 ) v WHERE
o.order_id = v.order_id
Plan hash value: 35479787
-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | E-Time | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 269 |00:00:00.02 | 61 |
| 1 | NESTED LOOPS | | 1 | 313 | 00:00:01 | 269 |00:00:00.02 | 61 |
|* 2 | HASH JOIN | | 1 | 313 | 00:00:01 | 269 |00:00:00.01 | 40 |
|* 3 | TABLE ACCESS FULL | PRODUCT_INFORMATION | 1 | 87 | 00:00:01 | 87 |00:00:00.01 | 15 |
| 4 | INDEX FAST FULL SCAN| ORDER_ITEMS_UK | 1 | 665 | 00:00:01 | 665 |00:00:00.01 | 25 |
|* 5 | INDEX UNIQUE SCAN | ORDER_PK | 269 | 1 | | 269 |00:00:00.01 | 21 |
-------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("P"."PRODUCT_ID"="O"."PRODUCT_ID")
3 - filter(("MIN_PRICE"<40 AND "LIST_PRICE"<50))
5 - access("O"."ORDER_ID"="ORDER_ID")
Note
-----
- cardinality feedback used for this statement
32 rows selected.
Elapsed: 00:00:00.02
SQL>
--檢視動態檢視
SQL>
SQL> ---sql_id:bmh5hb8331u33
SQL> select sql_id,child_number, executions, buffer_gets,plan_hash_value,is_shareable
2 from v$sql
3 where sql_id = 'bmh5hb8331u33';
SQL_ID CHILD_NUMBER EXECUTIONS BUFFER_GETS PLAN_HASH_VALUE I
------------- ------------ ---------- ----------- --------------- -
bmh5hb8331u33 0 1 1604 1906736282 N
bmh5hb8331u33 1 3 183 35479787 Y
Elapsed: 00:00:00.00
SQL>
SQL> select sql_id, child_number, USE_FEEDBACK_STATS
2 from V$SQL_SHARED_CURSOR
3 where sql_id = 'bmh5hb8331u33';
SQL_ID CHILD_NUMBER U
------------- ------------ -
bmh5hb8331u33 0 Y
bmh5hb8331u33 1 N
Elapsed: 00:00:00.00
我們發現以後的執行都會變成軟解析,使用第二次產生的執行計劃。
通過CFB功能使優化器能夠在以後的執行中選擇更優的執行計劃,從得到更好的執行效率。
CFB的處理流程
下面通過以下流程圖來總體的回顧一下CFB的處理過程。
在下列情況CBO可能無法估算出準確的Cardinality,Oracle會啟用CFB功能:
1 2 3 |
|
針對上述情況,Oracle會採取如下的CFB流程處理:
1 2 3 4 |
|