- 數據庫高效優化:架構、規范與SQL技巧
- 馬立和 高振嬌 韓鋒
- 6566字
- 2020-08-03 16:49:20
2.1 優化器
優化器在整個SQL語句的執行過程中充當了非常重要的角色。圖2-1是一個SQL語句從提交到最終得到結果的示意圖,從中我們可以看到優化器充當的角色及其主要功能。
Oracle的優化器也是在不斷演變中的。在早期的版本中,Oracle使用一種基于規則的優化器。顧名思義,它是按照某種特定的規則來制定執行計劃的。這種方式比較簡單直觀,但對數據庫自身情況及SQL語句中對象本身的情況都沒有考慮。在后期的Oracle版本中,又推出了另外一種優化器——基于成本的優化器。下面將對兩種主要的優化器分別加以介紹,并對和優化器相關的數據庫參數和提示進行說明。

圖2-1 SQL語句執行過程
2.1.1 基于規則的優化器
基于規則的優化器(Rule Based Optimizer,RBO)內部采用了一種規則列表,其中每一種規則代表一種執行路徑并被賦予一個等級,不同的等級代表不同的優先級別。等級越高的規則越會被優先采用。Oracle會在代碼里事先給各種類型的執行路徑定一個等級,一共有15個等級,從等級1到等級15。Oracle會認為等級值低的執行路徑的執行效率比等級值高的執行效率高。在決定目標SQL的執行計劃時,如果可能的執行路徑不止一條,則RBO就會從該SQL多種可能的執行路徑中選擇一條等級最低的執行路徑來作為其執行計劃。
1.RBO的具體規則
下面我們就來看看RBO的具體規則,如表2-1所示。
表2-1 RBO規則

下面針對表2-1中所示的每一種規則的含義及其用法進行說明。
·Single Row by ROWID:根據ROWID,返回一條記錄。這種規則發生在SQL語句的WHERE部分,指定了記錄的ROWID或者使用了CURRENT OF CURSOR形式的SQL。
·Single Row by Cluster Join:根據聚簇連接,返回一條記錄。這種規則發生在SQL語句中WHERE部分,包含了兩表關聯,且關聯字段為一個聚簇,同時還存在一個過濾條件為一個表的唯一索引或主鍵。
·Single Row by Hash Cluster Key with Unique or Primary Key:根據哈希聚簇鍵,返回一條記錄。這種規則發生在SQL語句的WHERE部分所包含的過濾條件中,字段是一個哈希聚簇鍵且這個字段為唯一或主鍵索引字段。
·Single Row by Unique or Primary Key:根據主鍵或唯一索引鍵值,返回一條記錄。這種規則發生在SQL語句中WHERE部分,為唯一或主鍵所有字段的等值連接條件。
·Clustered Join:根據聚簇連接,返回一組記錄。這種規則跟Path 2類似,只不過過濾條件中沒有唯一限制,可以返回多條記錄。
·Hash Cluster Key:根據哈希聚簇鍵值,返回一條記錄。這種規則跟表2-1所示Path 3類似,只不過過濾條件中沒有唯一限制,可以返回多條記錄。
·Indexed Cluster Key:根據一個索引的聚簇鍵字段,返回一組記錄。
·Composite Index:根據一個組合索引字段,返回一組記錄。這種規則中WHERE部分需要指定組合索引字段且通過邏輯“與”運算符進行連接。
·Single-Column Indexes:根據單一索引字段,返回一組記錄。
·Bounded Range Search on Indexed Columns:根據索引字段的有限范圍搜索,返回一組記錄。這里所說的有限范圍搜索,包括字段的等值比較、大于等于和小于等于、BETWEEN...AND、LIKE等過濾條件。
·Unbounded Range Search on Indexed Columns:根據索引字段的無限范圍搜索,返回一組記錄。這里所說的無限范圍搜索,包括字段的大于等于、小于等于過濾條件。
·Sort Merge Join:根據排序合并關聯,返回一組記錄。
·MAX or MIN of Indexed Column:獲取一個索引字段的最大、最小值。這種規則需要遍歷整個索引。
·ORDER BY on Indexed Column:根據一個索引字段,進行排序操作。
·Full Table Scan:通過全表掃描方式,獲取一個結果集。
2.RBO在實際工作中的應用
在一般的工作場景中,很少會涉及使用RBO的情況。隨著Oracle自身技術的發展,CBO優化器成為首選。只有在極個別的情況下,需要手工調整執行計劃時,可采取指定優化器參數或引用相關的提示(參見后面的介紹)。需要注意的是,因為RBO技術出現比較早,很多新的技術不支持,所以在很多情況下即使手工指定使用RBO優化器,也可能會失效,Oracle仍然會使用CBO優化器。下面介紹一下失效的情況。
只要出現如下的情形之一(包括但不限于這些情形),那么即便修改了優化器模式或者使用了RULE Hint,Oracle依然不會使用RBO(而是強制使用CBO)。
·目標SQL中涉及的對象有IOT。
·目標SQL中涉及的對象有分區表。
·使用了并行查詢或者并行DML。
·使用了星型連接。
·使用了哈希連接。
·使用了索引快速全掃描。
·使用了函數索引。
2.1.2 基于成本的優化器
基于成本的優化器(Cost Based Optimizer,CBO)在堅持實事求是的基礎上,通過對具有現實意義的諸多要素的分析和計算來完成最優路徑的選擇工作。這里的關鍵點在于對成本的理解,后面會有對成本的專門介紹。這里簡單交代一句,成本可以理解為SQL執行的代價。成本越低,SQL執行的代價越小,CBO也就認為這是一個更優異的執行路徑。
隨著Oracle版本的不斷演變,CBO優化器變得越來越智能,但需要注意的是,CBO仍然存在一些特殊情況,導致其可能產生較差的執行計劃。這也是以后CBO發展需要彌補的弱點。CBO存在的問題主要有以下幾個方面。
·多列關聯關系:在默認情況下,CBO認為WHERE條件中的各個字段之間是獨立的,并據此計算其選擇率,進而估計成本來選擇執行計劃。但如果各列之間有某種關系,則估算的結果與實際結果之間往往存在較大誤差??梢酝ㄟ^動態采樣或者多列統計信息的方法解決部分問題,但都不是完美的解決方案。
·SQL無關性:CBO認為SQL語句運行都是相對獨立的,之間沒有任何關系;但在實際運行中可能是有關聯的。例如前一條語句訪問某個索引,則相關數據塊會被緩存到Data Buffer中,后續SQL如果也需要訪問這個索引,則可以從Cache獲得,這將大大減少讀取成本,但這一點CBO是無法感知的。
·直方圖統計信息:一方面在12c之前,基于頻率的直方圖的桶的個數不能超過254,這可能導致一些精度的丟失。另一方面,對于文本型字段的直方圖收集,Oracle只會提取前32字節(對于多字節字符集來說更加嚴重),這樣獲得的數據會失真,可能會導致優化器獲得錯誤的執行計劃。
·復雜多表關聯:對于復雜的多表關聯,其可能的表間關聯順序組合隨著表的數量增加呈幾何級數增長。假設多表關聯的目標SQL包含表的數量為n,則該SQL各表之間可能的連接順序的總數就是n!。CBO在處理這個問題時,是有所取舍的。在11gR2的版本中,CBO在解析這種多表關聯的目標SQL時,所考慮的各個表連接順序的總和會受到隱含參數_OPTIMIZER_MAX_PERMUTATIONS的限制。這意味著不管目標SQL在理論上有多少種可能的連接順序,CBO至多只會考慮其中根據_OPTIMIZER_MAX_PERMUTATIONS計算出來的有限種可能。這同時也意味著只要該目標SQL正確的執行計劃不在上述有限種可能之中,則CBO一定會漏選最優的執行計劃。
2.1.3 對比兩種優化器
RBO和CBO的優缺點對比如表2-2所示。
表2-2 RBO和CBO的優缺點對比

在通常情況下,已經沒有理由不選用CBO優化器了,這也是Oracle強大之所在。在極個別的情況下,也存在對CBO優化器不適合使用的情況,原因可能是BUG或者CBO設計問題。此時可以考慮使用RBO優化器,但即使是這種情況,也要嚴格限制特定范圍,一般只在語句級使用RBO優化器。
2.1.4 優化器相關參數
本小節重點介紹幾個與優化器密切相關的參數。想真正了解優化器,這些參數是必須掌握的。
1.optimizer_mode
數據庫使用哪種優化器主要是由optimizer_mode初始化參數決定的。
(1)取值說明
·RULE:使用RBO優化器。需要注意的是即使指定數據庫使用RBO優化器,但有時Oracle數據庫還是會采用CBO優化器,這并不是Oracle的BUG,主要是由于從Oracle 8i后引入的許多新特性都必須在CBO下才能使用,而你的SQL語句可能正好使用了這些新特性,此時數據庫會自動轉為CBO優化器執行這些語句。
·CHOOSE:根據實際情況,如果數據字典中包含被引用的表的統計數據,即引用的對象已經被分析,那么使用CBO優化器,否則為RBO優化器。CHOOSE是Oracle 9i的默認值。
·ALL_ROWS:為CBO優化器使用的第一種具體的優化方法,以數據的吞吐量為主要目標,以便可以使用最少的資源完成語句;是10g以及后續版本中optimizer_mode的默認值數。
·FIRST_ROWS:為優化器使用的第二種具體的優化方法,以數據的響應時間為主要目標,以便快速查詢出開始的幾行數據。
·FIRST_ROWS_[1 | 10 | 100 | 1000]:為優化器使用的第三種具體的優化方法,讓優化器選擇一個能夠把響應時間減到最小的查詢執行計劃,以迅速產生查詢結果的前n行。該值為Oracle 9i新引入的。注意以前的FIRST_ROWS已經不再使用,僅僅是為了向后兼容的需要。
(2)默認值變化
·在8i、9i等版本中,CHOOSE為默認值;在10g及以后不再支持基于RULE的優化器中,新的默認值為ALL_ROWS。因此,參數值CHOOSE和RULE都不再被支持。
·雖然從Oracle 10g開始,RBO優化器已不再被Oracle支持,但RBO優化器的相關實現代碼并沒有從Oracle數據庫的代碼中移除,這意味著即使是在11gR2中,依然可以通過修改優化器模式或使用RULE Hint來繼續使用RBO。
(3)相關操作
初始化參數optimizer_mode是動態的,可以在實例級或會話級改變。此外使用提示(hint),也可以在SQL級別設置優化器。
//查看實例級優化器設置 select name,value,isdefault,ismodified,description from v$system_parameter where name like '%optimizer_mode%'; //修改會話級優化器設置 alter session set optimizer_mode=..; //查看當前會話設置 select name,value,isdefault,ismodified,description fromv$parameter where name like '%optimizer_mode%'; //相關提示 /*+ all_rows ... */ /*+ first_rows(n) ... */ 2. optimizer_features_enable
optimizer_features_enable參數控制使用的優化器特征的版本,比如從Oracle 8i升級到了Oracle 9i,默認情況下參數為9.2.0,如果將它設置為8.1.6,那么將使用Oracle 8i的優化器特征。Oracle不推薦顯式設置該參數,而是更改應用程序中的相關SQL。參數optimizer_features_enable不僅能禁用特性,而且能禁用BUG修復。
(1)相關操作
1)查看可用的版本號,代碼如下:
select value fromv$parameter_valid_values where name='optimizer_features_enable'; VALUE --------- 8.0.0 8.0.3 … 10.2.0.4 10.2.0.4.1
10gR1以前的版本不存在這個視圖。要獲得可用版本號,可以執行一個錯誤的設置,由系統提供可選項。類似下面的做法:
alter session set optimizer_features_enable='1.0.0'; ERROR: ORA-00096: invalid value 1.0.0 for parameter optimizer_features_enable, must be from among 10.2.0.4.1, 10.2.0.4, 10.2.0.3, 10.2.0.2, 10.2.0.1, 10.1.0.5, 10.,9.0.1, 9.0.0, 8.1.7, 8.1.6, 8.1.5, 8.1.4, 8.1.3, 8.1.0, 8.0.7, 8.0.6, 8.0.5, 8.0.4, 8.0.3, 8.0.0
2)設置版本號:可以在實例、會話、SQL級別設定optimizer_features_enable參數。設置示例分別如下。
alter system set optimizer_features_enable='9.2.0'; //實例級別 alter session set optimizer_features_enable='9.2.0'; //會話級別 /*+ optimizer_features_enable('9.2.0') */ //SQL級別(Hint)
(2)測試案例
下面通過一個簡單的案例說明optimizer_features_enable參數的作用。案例通過設置optimizer_features_enable參數,模擬了不同版本數據庫中不同的表間關聯處理方式。具體關于表間關聯的細節,可參見本書相應章節,這里只是說明參數的使用方法。案例中數據庫版本為11gR2。
1)準備工作,具體如下:
create table anti_test1 as select * from dba_objects; create table anti_test2 as select * from dba_objects; desc anti_test1; Name Null? Type ------------------- -------- --------------------- OWNER VARCHAR2(30) OBJECT_NAME VARCHAR2(128) SUBOBJECT_NAME VARCHAR2(30) OBJECT_ID NUMBER DATA_OBJECT_ID NUMBER OBJECT_TYPE VARCHAR2(19) CREATED DATE LAST_DDL_TIME DATE TIMESTAMP VARCHAR2(19) STATUS VARCHAR2(7) TEMPORARY VARCHAR2(1) GENERATED VARCHAR2(1) SECONDARY VARCHAR2(1) NAMESPACE NUMBER EDITION_NAME VARCHAR2(30) //注意測試表中的OBJECT_ID字段是可以為空的。
2)測試11g的情況,具體如下:
select * from anti_test1 a where a.object_id not in (select b.object_id from anti_test2 b); ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 65273 | 1384 (1)| 00:00:17 | |* 1 | HASH JOIN RIGHT ANTI NA| | 65273 | 1384 (1)| 00:00:17 | | 2 | TABLE ACCESS FULL | ANTI_TEST2 | 102K| 292 (1)| 00:00:04 | | 3 | TABLE ACCESS FULL | ANTI_TEST1 | 65273 | 292 (1)| 00:00:04 | ------------------------------------------------------------------------------ /* 在11gR2的版本中,對于上述表間關聯,優化器采用了哈希連接的處理方式。這是一個在11g版本中新增的特性,稱為NULL AWARE,可以支持空字段的反連接操作使用哈希連接處理。在老的版本中,不支持這樣處理,因此只能使用較原始的嵌套循環方式處理。這個在后面的表連接的章節中會詳細講解。 */
3)測試10g的情況。
select /*+ optimizer_features_enable('10.2.0.5') */ * from anti_test1 a wherea.object_id not in (selectb.object_id from anti_test2 b); ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 65241 | 4372 (1)| 00:00:53 | |* 1 | FILTER | | | | | | 2 | TABLE ACCESS FULL| ANTI_TEST1 | 65273 | 292 (1)| 00:00:04 | |* 3 | TABLE ACCESS FULL| ANTI_TEST2 | 97664 | 2 (0)| 00:00:01 | ------------------------------------------------------------------------- /* 與上面的執行計劃不同,這里是因為通過提示的方式修改了optimizer_features_enable參數,指定優化器使用較老的版本。指定的10.2.0.5版本中,只能使用這種原始的嵌套循環方式處理表間關聯。 */
2.1.5 優化器相關Hint
在SQL優化中,除了可以通過修改參數的方式干預優化器工作外,還可以使用提示的方式進行干預,而且這種方式更加精準、不影響其他SQL,故使用場景更加廣泛。關于提示——Hint,將在后面的章節中詳細介紹。
1.ALL_ROWS
說明:
·ALL_ROWS是針對整個目標SQL的Hint,它的含義是讓優化器啟用CBO,而且在得到目標SQL的執行計劃時會選擇那些吞吐量最佳的執行路徑。這里的“吞吐量最佳”是指資源消耗量(即對I/O、CPU等硬件資源的消耗量)最小,也就是說在ALL_ROWS Hint生效的情況下,優化器會啟用CBO,而且會依據各個執行路徑的資源消耗量來計算它們各自的成本。
·ALL_ROWS Hint其實就相當于對目標SQL啟用CBO,其優化器為ALL_ROWS。從Oracle 10g開始,ALL_ROWS就是默認的優化器模式。這也意味著自Oracle 10g以來,默認情況下優化器啟用的就是CBO,而且會依據各條執行路徑的資源消耗量來計算它們各自的成本。
·如果在目標SQL中除了ALL_ROWS之外還使用了其他與執行路徑、表連接相關的Hint,則優化器會優先考慮ALL_ROWS。
格式:
/*+ ALL_ROWS */
范例:
select /*+ all_rows */ empno,ename,sal,job from emp where empno=7369;
2.FIRST_ROWS(n)
說明:FIRST_ROWS(n)是針對整個目標SQL的Hint,它的含義是讓優化器啟用CBO模式,而且在得到目標SQL的執行計劃時會選擇那些以最快響應并返回頭n條記錄的執行路徑,也就是說在FIRST_ROWS(n)Hint生效的情況下,優化器會啟用CBO,而且會依據返回頭n條記錄的響應時間來決定目標SQL的執行計劃。
格式:
/*+ FIRST_ROWS(n) */
范例:
select /*+ first_rows(10) */ empno,ename,sal,job from emp where empno=7369;
優化器模式-FIRST_ROWS_n:FIRST_ROWS(n)Hint和優化器模式FIRST_ROWS_n不是一一對應的。優化器模式FIRST_ROWS_n中只能是1、10、100和1000,但FIRST_ROWS(n)Hint中的n可以是除1、10、100和1000之外的所有值。
alter session set optimizer_mode=first_rows_10;
忽略情況:如果在UPDATE、DELETE或者含如下內容的查詢語句中使用了FIRST_ROWS(n)Hint,則該FIRST_ROWS(n)Hint會被Oracle忽略。
·集合運算(如UNION、INTERSECT、MINUS、UNION ALL等)
·GROUP BY
·FOR UPDATE
·聚合函數(比如SUM等)
·DISTINCT
·ORDER BY(對應的排序列上沒有索引)
這里優化器會忽略FIRST_ROWS(n)Hint是因為對于上述類型的SQL語言而言,Oracle必須訪問所有的行記錄后才能返回滿足條件的頭n行記錄,即在上述情形下,使用FIRST_ROWS(n)Hint是沒有意義的。
3.RULE
說明:RULE是針對整個SQL的Hint,它表示對目標SQL啟用RBO。
格式:
/*+ RULE */
范例:
select /*+ rule */ empno,ename,sal,job from emp where empno=7369;
RULE與其他Hint:RULE通常不能與除DRIVING_SITE以外的Hint聯用,當RULE與除DRIVING_SITE以外的Hint聯用時,其他的Hint可能會失效。但是,當RULE和DRIVING_SITE聯用時,它自身可能會失效,所以RULE Hint最好是單獨使用。
最佳實踐:不推薦使用RULE Hint。一是因為Oracle早就不支持RBO了,二是因為啟用RBO后優化器在執行目標SQL時選擇的執行路徑將大大減少,很多執行路徑RBO根本就不支持(比如哈希連接),這也就意味著啟用RBO后目標SQL跑出正確執行計劃的概率將大大降低。
忽略情況:因為很多執行路徑RBO根本就不支持,所以即使在目標SQL中使用了RULE Hint,如果出現了如下情況(包括但不限于),RULE Hint依然會被Oracle忽略。
·目標SQL除RULE之外還聯合使用了其他Hint(比如DRIVING_SITE)。
·目標SQL使用了并行執行。
·目標SQL所涉及的對象有IOT。
·目標SQL所涉及的對象有分區表。
4.測試案例
下面通過一個完整的案例,介紹混合使用各種不同的提示并觀察其效果。
準備工作,代碼如下:
create table t1 as select * from dba_objects; insert into t1 select * from t1; insert into t1 select * from t1; commit; select count(*) from t1; => 292280 //構造了一張測試表,數據規模接近30萬 create index idx_t1 on t1(object_id); //對OBJECT_ID字段創建了索引 update t1 set object_id=1 where rownum<288280; commit; select count(*) from t1 where object_id=1; => 288279 //手動修改了OBJECT_ID的值,將表中絕大多數記錄的OBJECT_ID設置為1 exec dbms_stats.gather_table_stats( ownname=>'HF', tabname=>'T1', estimate_percent=>100, method_opt=>'for columns size auto object_id', cascade=>true); //收集表的統計信息,注意此時也收集了相關對象—索引的統計信息 select clustering_factor from dba_indexes where index_name='IDX_T1'; => 4213 /* 查看當前索引的聚簇因子為4213。關于聚簇因子,后面章節有詳細說明。這里簡單說明一下,聚簇因子反映了索引字段的順序和表中數據存儲的有序關系。聚簇因子越小,說明索引字段順序與表中數據存儲順序一致性越高;反之,則一致性越低,即越無序 */ exec dbms_stats.set_index_stats( ownname=>'HF', indname=>'IDX_T1', clstfct=>10000, no_invalidate=>false); select clustering_factor from dba_indexes where index_name='IDX_T1'; => 10000 /* 這里手動修改了聚簇因子,將其設置為10000。手動修改統計信息是一種常用的優化手段,可以 便于我們分析問題。后面的統計信息的章節會有詳細說明 */
測試SQL-默認情況,具體如下:
select object_name,object_id from t1 where object_id=1; -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 287K| 19M| 1170 (1)| 00:00:15 | |* 1 | TABLE ACCESS FULL| T1 | 287K| 19M| 1170 (1)| 00:00:15 | -------------------------------------------------------------------------- /* 在默認情況下,上面的SQL應該是采用的索引掃描。因為上面手工修改了索引的聚簇因子,大大增加了索引掃描的成本。所以,這里選擇使用了全表掃描。注意此時是使用了CBO,且優化器模式為默認值—ALL_ROWS */
測試SQL-first_rows(10),具體如下:
select /*+ first_rows(10) */ object_name,object_id from t1 where object_id=1; ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 12 | 4 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| T1 | 12 | 4 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_T1 | | 3 (0)| 00:00:01 | ------------------------------------------------------------------------------ /* 這里使用了一個提示first_rows(10),其作用是優先返回10條記錄。在使用提示后,Oracle認為此時掃描索引IDX_T1能夠以最短的響應時間返回滿足上述SQL的where條件object_id=1的頭10條記錄,因此這里使用了索引范圍掃描 */
測試SQL-first_rows(9),具體如下:
select /*+ first_rows(9) */ object_name,object_id from t1 where object_id=1; ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 11 | 4 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| T1 | 11 | 4 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_T1 | | 3 (0)| 00:00:01 | ------------------------------------------------------------------------------ /* 使用提示first_rows(9),帶來的變化就是優化器對基數的估算不同。注意觀察執行計劃中的Rows部分。從first_rows(10)的12變成了11 */
測試SQL-all_rows,具體如下:
select /*+ all_rows */ object_name,object_id from t1 where object_id=1; -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 287K| 19M| 1170 (1)| 00:00:15 | |* 1 | TABLE ACCESS FULL| T1 | 287K| 19M| 1170 (1)| 00:00:15 | -------------------------------------------------------------------------- /* ALL_ROWS Hint其實就相當于對目標SQL啟用CBO且優化器模式為ALL_ROWS,而ALL_ROWS本身就是自10g以來優化器模式的默認設置,即在默認情況下單獨使用ALL_ROWS Hint和不使用任何Hint的效果是一樣的 */
測試SQL-rule,具體如下:
select /*+ rule */ object_name,object_id from t1 where object_id=1; ---------------------------------------------- | Id | Operation | Name | ---------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | TABLE ACCESS BY INDEX ROWID| T1 | |* 2 | INDEX RANGE SCAN | IDX_T1 | ---------------------------------------------- Note ----- - rule based optimizer used (consider using cbo) /* 注意執行計劃中的關鍵字rule based...,并且顯示的具體執行步驟中并沒有Cost列,這說明RULE起作用了(現在用的是RBO) */
測試SQL-rule + parallel,具體如下:
alter table t1 parallel; select /*+ rule */ object_name,object_id from t1 where object_id=1; ----------------------------------------------------- | Id | Operation | Name |Cost (%CPU)| ----------------------------------------------------- | 0 | SELECT STATEMENT | | 81 (0)| | 1 | PX COORDINATOR | | | | 2 | PX SEND QC (RANDOM)| :TQ10000 | 81 (0)| | 3 | PX BLOCK ITERATOR | | 81 (0)| |* 4 | TABLE ACCESS FULL| T1 | 81 (0)| ----------------------------------------------------- /* 輸出中包含了Cost列,這表示上述SQL在解析時使用的是CBO,這也驗證了之前的觀點:如果目標SQL使用了并行執行,就意味著其中的RULE Hint會失效,此時Oracle會自動啟用CBO */