1. 程式人生 > >Oracle 優化器

Oracle 優化器

什麼是優化器

  優化器是Oracle中的一個核心模組,它的作用是為使用者輸入的SQL選擇一個它計算出來的最高效的執行計劃。SQL語句在Oracle中的執行過程如下圖所示:

  

 

基於規則優化器RBO

  基於規則的優化器現在基本上已經不怎麼用了,這裡只做簡單介紹。

  基於規則的優化器是將一系列規則固定在系統中,給 每個執行路徑定一個等級。最低是1最高是15 。如:等級1對應的是:single row by rowid (通過rowid訪問資料),等級15則對應的是:full table scan (全表掃描) 。當SQL執行的時候,有不同的執行路徑可以選擇,那麼就從該SQL的執行計劃中選擇一條等級值最低的路徑作為其執行計劃。

RBO的缺點

  由於其選擇執行計劃的方式比較死板,所以會有很多缺陷(相對於CBO)。

  1.在使用RBO的時候,一旦執行計劃出了問題,很難對其做調整。

  2.使用RBO時,SQL的寫法,甚至是SQL中涉及物件在SQL文字出現的先後次序都可能會影響RBO對執行計劃的選擇。

  3.Oracle 資料庫很多好的特性、功能RBO都無法相容。如:目標SQL中涉及的物件有ITO(index organized table);使用了雜湊連結、星型連結、函式索引等。

  由於諸多原因,RBO選擇出來的執行計劃不一定是最優的執行計劃(例如:建立索引的欄位都是同樣的值,那麼使用索引效率不如全表掃描效率高。但是依然會通過索引去取值)。所以,Oracle 10g 開始,就改為使用CBO了。10g 以後的版本,如果想要使用RBO。需要通過修改優化器模式,或使用RULE Hint來繼續使用RBO。

  在當前session中使用RBO:

ALTER SESSION SET OPTIMIZER_MODE='RULE'

基於成本的優化器CBO

什麼是CBO

  為了解決RBO由於硬編碼導致執行計劃不準確的問題,從Oracle7開始,Oracle就引入了CBO。CBO在選擇執行計劃的時候,所用的判斷原則為成本,CBO會從諸多的執行計劃中選擇一條成本最小的執行路徑作為其執行計劃。各條執行路徑成本是根據目標的SQL語句所涉及的表、索引、列等相關物件的統計資訊計算得出的。這些統計資訊儲存在Oracle的資料字典裡,並且從多個角度描述了Oracle資料庫中相關物件的資料量、資料分佈情況等資訊。

  Oracle在選擇執行計劃的時候,會根據這些統計資訊算出相關執行步驟對應的IO、CPU和網路資源消耗的值,然後根據這些消耗的成本選擇一條執行路徑。

CBO相關概念_Cardinality

  Cardinality 是CBO特有概念,指的是指定結果集的行數。與SQL執行計劃的某個執行步驟相對應。用於對目標SQL的某個執行步驟的執行結果包含的記錄數進行估算。對於整個SQL,真是最終執行結果包含的記錄數。這個值越大,標識著結果集所消耗的IO越多。對應的成本一般也會越大。這個執行路徑的總的成本也會越大。

CBO相關概念_可選擇率

  可選擇率,也是CBO特有的概念。指的是加上WHERE條件後返回的結果集的數量與不加條件返回的原始結果集的記錄數比值。這個值的取值範圍是0~1 計算公式如下:

                   施加謂語條件得到的記錄數
      可選擇率   =      --------------------------------------------------
                    未施加謂語條件返回的原始記錄數

  可選擇率和成本的估算關係是:可選擇率越大,對應的結果集行數也會越大。可選擇率越大, 執行步驟估算的成本值也就越大,這個執行路徑對應的總成本也會越大。

CBO相關概念_可傳遞性

  可傳遞性,依然是CBO特有概念。CBO在拿到SQL語句後的第一件事,就是對SQL進行等價改寫。即在SQL上新增根據現有條件推匯出來的新的謂語條件,,我們SQL在進行執行路徑選擇的時候,就會將推匯出來的謂詞條件對應的執行步驟也進行計算。可能會得到一個成本比原來的謂詞條件成本更低的執行路徑。從而選擇更優的執行計劃。關於可傳遞性,分為三種情況:

1.簡單謂詞傳遞

  例如原SQL為:select * from t1,t2 where t1.c1= t2.c1 and t1.c1 = 10 這條SQL,CBO就會新增一個 t2.c1=10的條件,將SQL語句變為:select * from t1,t2 where t1.c1= t2.c1 and t1.c1 = 10 and t2.c1=10,改寫前後的SQL語句謂詞條件是等價的。我們通過 t1.c1=t2.c1 ,t1.c1= 10 很容易推導得出 t2.c1 = 10 

2.連線謂詞傳遞

  例如:原SQL為: select * from t1,t2,t3 where t1.c1= t2.c1 and t2.c1 = t3.c1 ,CBO 在優化的時候,會在謂詞條件中新增一個 t1.c1= t3.c1 的條件,將SQL語句變為:select * from t1,t2,t3 where t1.c1= t2.c1 and t2.c1 = t3.c1 and t1.c1=t3.c1,同理,這樣的修改也是等價的。

3.外連線謂詞傳遞

  例如:原SQL為:select * from t1,t2 where t1.c1= t2.c1(+) and t1.c1 = 10 ,對於該SQL語句CBO在優化的時候會在謂詞條件中加上t2.c1(+)=10 這個條件。將SQL改為:select * from t1,t2 where t1.c1= t2.c1(10)  and t1.c1 = 10  and t2.c1(+) =10 這同樣是等價的改寫。

 簡單例項

   這裡通過一個簡單的例項對CBO可傳遞性做個測試:

  建立t1表和t2表:

create table t1(c1 number,c2 varchar2(10));
CREATE table t2(c1 number,c2 VARCHAR2(10));

  t2中建立索引:

CREATE index idx_t2 on t2 (c1);

  往表中隨便插入一些資料,然後執行SQL:

SELECT t1.C1,t2.c2 FROM T1,t2 WHERE t1.C1 = t2.C1 and t1.C1 =10;

  檢視執行計劃

Plan hash value: 794674300
 
------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |        |     1 |    33 |     2   (0)| 00:00:01 |
|   1 |  MERGE JOIN CARTESIAN                 |        |     1 |    33 |     2   (0)| 00:00:01 |
|*  2 |   TABLE ACCESS FULL                   | T1     |     1 |    13 |     2   (0)| 00:00:01 |
|   3 |   BUFFER SORT                         |        |     1 |    20 |     0   (0)| 00:00:01 |
|   4 |    TABLE ACCESS BY INDEX ROWID BATCHED| T2     |     1 |    20 |     0   (0)| 00:00:01 |
|*  5 |     INDEX RANGE SCAN                  | IDX_T2 |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - filter("T1"."C1"=10)
   5 - access("T2"."C1"=10)
 
Note
-----
   - 'PLAN_TABLE' is old version
   - dynamic statistics used: dynamic sampling (level=2)

  在執行計劃id為5的執行計劃中可以看到Oracle執行計劃通過索引進行了範圍掃描。而這個步驟所對應的查詢條件是 access("T2"."C1"=10)  這個條件。說明執行計劃生成的謂詞條件中有  t2.c1=10 這個謂詞條件,但是原SQL中並不包含該條件。說明CBO確實對SQL語句進行了改寫。而改寫後的SQL條件可以使用t2表的索引,這有利於提高SQL語句的執行效率。改寫後的SQL:

SELECT t1.C1,t2.c2 FROM T1,t2 WHERE t1.C1 = t2.C1 and t1.C1 =10 and t2.c1 = 10;

CBO的侷限性

  CBO解決了RBO的先天缺陷,並且越來越智慧,但是仍然有很多可以優化的地方,如:

  1.CBO認為SQL的where條件出現的各個列相互獨立,沒有關聯。CBO對於每個列單獨進行成本計算,然後通過執行成本來選擇執行計劃,但是很多列有時候是相關聯的,單獨計算可能會導致執行計劃出現偏差,選錯執行計劃。

  2.CBO選擇執行計劃的時候,只考慮當前SQL。 CBO假設所有SQL都是單獨執行,互不干擾的,但是,很多時候,執行目標SQL需要的資料塊、索引葉子塊等資料可能已經被快取到了Buffer Cache中了,單獨計算可能也會導致選擇不到最優的執行計劃。

  3.CBO在直方圖統計方面有諸多限制。

  4.CBO在處理多表關聯的SQL時,可能會漏選執行計劃。在關聯的表越多的情況下,執行路徑的總數量也會成倍數增長。例如:假如一個表只有1個執行分支可供選擇,那麼兩個表(t1,t2)就是有 t1-t2(先執行t1條件,然後篩選t2條件) 和 t2-t1(先執行t2條件,然後篩選t1條件)  兩個執行路徑。如果是三個表(t1,t2,t3)那麼就會有6種組合,如果是四個表那麼就有24種組合,其計算方式為:表格數量的階乘。那麼如果有十幾個二十個表的時候甚至會有百億級別的執行路徑,如果把所有執行路徑全部執行一遍,那麼,選擇器就會耗費相當巨大的時間,所以,Oracle在執行CBO的時候,會有個最大選擇路徑的隱含引數(_optimizer_max_permutations)。不管有多少執行計劃,Oracle只會考慮_optimizer_max_permutations限制的可能。那麼,如果最優的執行計劃,不再該範圍內,就會漏選。

oracle 優化器模式的切換

  通過如下SQL可以切換當前session的優化器模式:

ALTER SESSION SET OPTIMIZER_MODE='RULE'

  其中,OPTIMIZER_MODE的各個可能值如下:

  RULE:使用RBO來解析目標SQL。

  CHOOSE:9i 的預設值,表示在執行SQL語句時,選擇那個RBO還是CBO取決於物件是否具有統計資訊。

  FIRST_ROWS_n(n= 1,10,100,1000) :使用CBO進行執行,並且在執行時,以最快的響應速度,返回前n條記錄。

  FIRS_ROWS 9i 中過時的引數。表示同時使用RBO和CBO 。

  ALL_ROWS: 10g 及以後的預設值,表示使用CBO來解析目標SQL,且挑選執行計劃時,側重計算執行路徑成本值。

&n