執行計劃(CBO)

導致不使用CBO而使用RBO的三個原因:
1.optimizer_mode設置爲rule或optimizer_goal設置爲rule
2.查詢中包含有rule提示.
3.查詢的所有表都沒有被分析過,並且查詢不包含任何提示.

RBO的工作方式:
    RBO在決定SQL的執行計劃時,只使用少量信息:
        SQL文本.
        SQL的FROM後面的基本信息,如表,簇,視圖,參考的其他字段的數據類型.
        與SQL相關表上的索引.
        數據字典信息只對本地數據庫有用. 如參考遠程數據庫,數據字典信息對RBO無效.
    簇的缺點超過它的優點.
    很少在應用中使用簇.    
   
結論:
    RBO只考慮少量可能的訪問方式.
    對RBO來說,索引總是好的,即使有的時候索引不一定好.
    RBO經常使用沒有意義的竅門來解釋SQL,爲什麼FROM後面的順序會與次相關?!很多人借解釋了CBO的不穩定性,但是有多少次索引重建後RBO的執行計劃因此改變了呢.
    100%的命中率意味着沒有邏輯IO導致物理IO,這清楚地解釋了一點:緩存命中率對優化執行計劃沒有什麼意義. 減少執行,減少邏輯IO的數量纔是更好的目標.

    Oracle最後不得不拋棄IO. 自從介紹RBO到CBO出現並很好地應用於生產系統,RBO運行了7年之久.

   
CBO的工作原理:
    CBO是一個數學處理器,它使用公式去計算SQL的成本. 成本本質上被轉換爲物理IO,邏輯IO在Oracle的SGA的Buffer Cache中被完全處理.
    CBO使用更多的可能的訪問方式,它計算每個可能的訪問方式的邏輯IO的數量,然後簡單地選擇最低成本的一個執行方式.
    RBO是線性的設計,一步步地做決定,先最決定,然後下個決定以該決定爲基礎,初始決定對最終的結果影響很大.

  
什麼情況下可能出錯?
    CBO只是選擇執行計劃中成本最低的一種,至少兩種情況可能會出錯:
    1.可能接收到錯誤的數據,
    2.一個或多個公式沒有包括重要的因子或是錯誤的.
    這兩個問題貫穿Oracle7始終.

    Oracle7中,analyze命令存在很多BUG,到了Oracle8中,很多BUG被修復了. Oracle 8i中DBMS_stats包比analyze命令更好的統計信息. 剩下的就是對CBO使用的計算成本的公式進行細微的修正了.

Cost是什麼意思?
    CBO計算的成本主要由物理IO組成. 實際的公式:IO+CPU/1000+NetIO*1.5
    該公式中,IO表示物理IO請求, CPU描述邏輯IO請求, NetIO描述通過數據庫連接到遠程數據庫的邏輯IO請求. 這以爲着物理IO是最"昂貴"的組成部分. 從中也可以看出,分佈式數據庫應用會有更高的COST. 邏輯IO和物理IO的區別是什麼?  前者是對緩存在Oracle SGA的Buffer Cache中的數據塊的訪問.  後者是從Oracle系統中取得的不緩存在Buffer Cache的數據塊的請求數.  CBO計算所有可能的執行計劃的物理IO的請求數. 排除不是最低的執行計劃.
   
Oracle IO規則:
    Oracle不訪問行,而是訪問包含行的數據塊, 不是去訪問磁盤,而是首先去查Oracle SGA中的Buffer Cache內存中的塊(邏輯讀). 邏輯讀:consistent gets和db block gets(current gets).  一致讀是提取的特定版本或時間點的SQL數據塊, db block gets是當前的時間點提取的數據塊,一般是修改數據如插入、更新、刪除。
    v$sysstat或v$sesstat中不會有“logical read”,只有“consistent read”和"db block gets" ,將兩者相加就是了。
    如果在Buffer Cache中找到了數據塊,則系統就使用找到的數據塊,如果沒有找到,系統執行物理讀,將塊讀到Buffer Cache中。
    如果一個服務器進程正在執行一個物理讀,而另一個進程執行邏輯讀時,沒有發現緩存中有數據塊,那麼該進程也會去進行一個物理讀嗎?不,第一個進程在發現數據塊沒有在緩存中時,在實際執行物理讀之前會預先分配一個緩衝器給該塊,然後鎖定緩衝器併發送物理IO請求,第二個進程試圖進行邏輯讀,而緩衝器被鎖定,則會POST一個"buffer busy wait"的等待事件並等待該塊可用,減少沒有必要的IO。
   

在cache buffers chain上工作
    Buffer Cache是一個填滿緩衝塊的高速緩衝存儲器,由LRU(least recently used)運
算法則管理,使用該法則,最經常被訪問的緩衝塊被放在鏈表的MRU(most recently used)端, 新讀到緩衝存儲器中的塊被放置在MRU端,邏輯讀時,他們被移到MRU列表一端,如果塊沒有被訪問,它就會逐漸地向LRU端移動。當它達到LRU端末的時候,新讀到的塊就會覆蓋該緩衝塊,該塊也就掉離列表的LRU.  Oracle8i中使用了中點插入算法來減少因塊移出列表特別是“熱”塊移動到列表的MRU端的latch處理。

兩類IO:
    Oracle讀數據文件有兩中類型的IO:
    1.單塊,隨機訪問讀取,通常是與索引訪問有關的(該IO請求發生時,產生等待事件db file sequential read).
    2.多塊,順序訪問讀取,通常與全表掃描訪問有關(該IO請求發生時,將產生db file scattered read等待事件),但是可能與排序和並行查詢(等待事件:direct path read).
    這兩種IO類型規則十分相似,進程需要數據塊時,首先檢查Buffer Cache,如果塊存在,就使用它,並且把它移動到LRU鏈的MRU端。如果塊不存在,就從磁盤上的數據文件讀取放到緩衝區中,並將它防止到LRU鏈的MRU端。
    不可避免地,每個新讀到的塊都將被放置到LRU鏈的MRU端,當前面的塊被LRU鏈的LRU端時,最後被推出LRU鏈或被覆蓋。全表掃描時,需要掃描的塊大於Buffer Cache時,將完全刷新Buffer Cache的內容。

Oracle中的緩存IO:
    Oracle7中一個改變來防止這種緩衝區被刷新的情況(cache flushing effect), 首先,多塊順序訪問的全表掃描的處理和LRU的算法是不同的,當全表掃描時,讀到的塊將被防止到LRU鏈的LRU端. 索引掃描的單塊讀沒有改變,繼續被放置在LRU鏈的MRU端. 全表掃描的數據塊計劃會立刻被覆蓋,除非這些塊很快被再次訪問(將被移動到MRU端).
    全表掃描的邏輯讀一般很在Buffer Cache中找到需要的塊,因此,全表掃描總是表現爲大量的物理讀.  但是被全表掃描重新訪問的塊有多塊的頻率? 大多數情況下很少. 所以Oracle7的設計者公認地允許塊很快被覆蓋,同時保留下Buffer Cache的原來真實內容.
    但是小表沒有必要每次物理讀,於是他們設計Oracle7時將小表的全表掃描塊防止在LRU鏈的MRU端,作爲特例,加速對小表全表掃描的訪問.  參數SMALL_TABLE_THESHOLD存儲了可以使用的數據塊數, 默認db_block_buffers的2%. Oracle7前沒有該參數.  有人將該參數設置過大,已存儲更多的小表, 但是這樣將導致buffer cache可用變小,反問容易出現不希望出現的cache flush.

Oracle7 + 版本中的IO:
    small_table_threshold已經被設置爲隱含參數_small_table_threshold.考慮到開發者可能會將某表駐留在Buffer Cache中, 所以在建表或修改表的語句中設置了cache和nocache關鍵字, 設置爲cache的表塊將被防止在LRU鏈的MRU端.
    考慮到可能有大量的表緩存,所以Oracle設置一個參數:CACHE_SIZE_THRESHOLD,超過該參數設置的數量的數據塊數的表,將不能被cache到buffer中. 該參數默認Buffer Cache的大小的10%,可以根據需要進行更改.

    索引掃描的塊總是放在LRU鏈的MRU端. 標記爲cache的表並且塊要比cache_size_threshold參數設置的小時,該表會處理LRU鏈的MRU端. 全表掃描的大表塊將被在LRU鏈的LRU端.

    全表掃描的幾乎每次邏輯讀都將導致物理讀,這是正常的,也是有利的. 全表掃描通常Buffer Cache的命中率只有20%設置更少,常是1-5%. 意味着除了少數邏輯讀外,都需要物理讀. 索引掃描通常表現膸很高的Buffer Cache命中率95%或以上.


OPTIMIZER_INDEX_COST_ADJ的調整
    系統運行一段時間後(需要將參數timed_statistics設置爲TRUE),查詢v$system_event,db file sequential read和db file scattered read事件的average_wait字段分別顯示的是索引掃描和全表掃描的平均等待時間,單位爲1/100秒.
可以將OPTIMIZER_INDEX_COST_ADJ 參數設置爲(前者/後者)*100
   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章