Oracle SQL Tuning知識

Oracle SQL Tuning 目標:
   是以最小的數據庫訪問次數提取更多地數據行來生成最佳的執行計劃(儘可能最小化物理讀(PIO)與邏輯讀(LIO)。

優化器模式
ORACLE的優化器共有3種:
a. RULE (基於規則) b. COST (基於成本) c. CHOOSE (選擇性)
       爲了使用基於成本的優化器(CBO, Cost-Based Optimizer) , 你必須定期更新統計信息,以保證數據庫中的對象統計信息(object statistics)的準確性.
       如果數據庫的優化器模式設置爲選擇性(CHOOSE),那麼實際的優化器模式將和是否運行過analyze命令有關. 如果table已經被analyze過, 優化器模式將自動成爲CBO , 反之,數據庫將採用RULE形式的優化器。

訪問Table的方式
ORACLE 採用兩種訪問表中記錄的方式:
a. 全表掃描 (TABLE ACCESS FULL
 全表掃描就是順序地訪問表中每條記錄. ORACLE採用一次讀入多個數據塊(database block)的方式優化全表掃描。使用FTS的前提條件:在較大的表上不建議使用全表掃描,除非取出數據的比較多,超過總量的5%-10%,或你想使用並行查詢功能時。

b. 索引掃描
 你可以採用基於ROWID的訪問方式情況,提高訪問表的效率, ROWID包含了表中記錄的物理位置信息.ORACLE採用索引(INDEX)實現了數據和存放數據的物理位置(ROWID)之間的聯繫. 通常索引提供了快速訪問ROWID的方法,因此那些基於索引列的查詢就可以得到性能上的提高.

其中ORACLE對索引又有兩種訪問模式.
a)索引唯一掃描 ( INDEX UNIQUE SCAN)
       大多數情況下, 優化器通過WHERE子句訪問INDEX.

b)索引範圍查詢(INDEX RANGE SCAN)
       謂詞(where限制條件)中使用了範圍操作符(如>、<、<>、>=、<=、between)。在非唯一索引上,謂詞"="也可能返回多行數據,所以在非唯一索引上都使用索引範圍掃描。
       使用index rang scan的3種情況:
1. 在唯一索引列上使用了range操作符(> < <> >= <= between)
2. 在組合索引上,只使用部分列進行查詢,導致查詢出多行
3. 對非唯一索引列上進行的任何查詢。

指導原則:
 移除不必要的大型全表掃描
     大型表的全表掃描將產生龐大的系統I/O且使得整個數據庫性能下降。優化專家首先會評估當前SQL查詢所返回的行數。最常見的辦法是爲走全表掃描的大表增加索引。B樹索引,位圖索引,以及基於函數的索引等能夠避免全表掃描。有時候,對一些不必要的全表掃描通過添加提示的方法來避免全表掃描。

 緩存小表全表掃描
    有時候全表掃描是最快的訪問方式,管理員應當確保專用的數據緩衝區(keep buffer cache,nk buffer cache)對這些表可用。在Oracle 8 以後小表可以被強制緩存到 keep 池。

 使用最佳索引
    Oracle 訪問對象有時候會有一個以上的索引選擇。因此應當檢查當前查詢對象上的每一個索引以確保Oracle使用了最佳索引。

 物化聚合運算以靜態化表統計
    Oracle 10g的特性之一SQL Access advisor 會給出索引建議以及物化視圖的建議。物化視圖可以預連接表和預摘要表數據。(譯者按,即Oracle可以根據特定的更新方式來提前更新物化視圖中的數據,而在查詢時僅僅查詢物化視圖即可得到最終所需的統計數據結果。物化視圖實際上是一張實體表)

調整步驟
    很多人問SQL tuning從哪裏着手。首先應當是從Library cache去根據他們的活動狀況捕獲SQL語句。

1、尋找影響較大的SQL語句
    我們可以根據SQL語句執行次數的多少進行排序來獲得執行次數較多的SQL語句。在v$sqlarea視圖中executions 列以及表stats$sql_summary或 dba_hist_sql_summary 能夠去定位當前最頻繁使用的SQL語句。注:也可以按照下列方式列出SQL語句。
        Rows processed  :處理的行數越多,則相應會有很高的I/O,也有可能耗用大量的臨時表空間
        Buffer gets  :Buffer gets過高可能表明資源被過度集中化查詢,存在熱塊現象
        Disk reads  :高的磁盤讀將引起過度的I/O
        Memory KB  :內存的分配大小可以鑑別該SQL語句是否在內存中使用了大量的表連接
        CPU secs  :CPU的開銷表明哪些SQL語句耗用了大量的CPU資源
        Sorts  :排序越多,則SQL性能越差,而且會佔用大量的臨時表空間
        Executions  :執行次數表明了當前SQL語句的頻繁度,應當被首先考慮調整,因爲這些語句影響了數據庫的整體性能

2、收集SQL的執行計劃
Oracle提供一個utlxplan.sql腳本來創建該表。執行該腳本並且爲該表創建一個公共同義詞。

    SQL  > @utlxplan
    Table created.
    SQL > create public synonym plan_table for sys.plan_table;
    Synonym created.

3、調整SQL語句
    對於那些存在可優化的子執行計劃,SQL應當按照下面的方式進行調整。

    通過添加提示(hint)來修改SQL的執行計劃;  一個添加提示的SQL語句,使得SQL查詢按指定路徑訪問。 
    使用全局臨時表來重寫SQL;
    使用PL/SQL來重寫SQL; 對於一些特定查詢該方法能夠有20倍左右的提升,將這些SQL封裝到包含存儲過程的包中去完成查詢。
    使用提示來調整SQL。

    爲便於測試,我們能夠隨時使用alter session命令來修改一個優化參數的值來觀察調整前後的結果比較。使用新的 opt_param 提示能獲得
    同樣的效果。
    
    select /*+ opt_param('optimizer_mode','first_rows_10') */ col1, col2 . . .
    select /*+ opt_param('optimizer_index_cost_adj',20) */ col1, col2 . .

    Oracle 發佈了大量的SQL提示,而且提示隨着Oracle版本的不同不斷的增強和複雜化。

    注意:提示通常用於調試SQL,最佳的辦法是調整優化器的統計信息使的CBO模式自動獲取最佳執行路徑,等同於使用提示的功能。
    
    表連接順序 :當表連接的順序可優化時,我們可以使用 ORDERED提示來強制表按照from子句中出現的先後順序來進行連接
     
    first_rows_n提示 :Oracle 有兩個基於成本優化的提示,一個是first_rows_n,一個是all_rows。first_rows模式將儘可能在一查詢到數據時就返回個客戶端。而 all_rows 模式則爲優化資源而設計,需要等到所有結果計算執行完畢才返回數據給客戶端。
     
        SELECT /*+ first_rows */ ...

4、書寫高效SQL語句的技巧
        下面給出一些編寫高效SQL語句總的指導原則,而不論Oracle優化器選擇何種優化模式。這些看是簡單的方式但是按照他們去做將收到事半功倍的效果(已經在實踐中被證實)。
        
    a.使用臨時表重寫複雜的子查詢
        Oracle 使用全局臨時表以及WITH操作符去解決那些複雜的SQL子查詢。尤其是那些where子句中的子查詢,SELECT 字句標量子查詢,
        FROM 子句的內聯視圖。使用臨時表實現SQL tuning(以及使用WITH的物化視圖)能夠使得性能得以驚人的提升。
        
    b.使用MINUS 代替EXIST子查詢
        使用MINUS操作代替NOT IN 或NOT EXISTS將產生更高效的執行計劃(譯者按:此需要測試)。
        
    c.使用SQL分析函數
        Oracle 分析函數能夠一次提取數據來做多維聚合運算(象ROLLUP,CUBE)以提高性能。
        
    d.重寫NOT EXISTS和查詢作爲外部連接NOT EXISTS 子查詢
        在一些案例中的NOT 查詢(where 中一個列被定義爲NULL值),能夠將其改寫這個非相關子查詢到IS NULL 的外部鏈接。如下例:
        select book_key from book
        where book_key NOT IN (select book_key from sales);

        下面我們在where子句中使用了外部連接來替代原來的not exits,得到一個更高效的執行計劃。

        select b.book_key from book b, sales s
        where b.book_key = s.book_key(+) and s.book_key IS NULL;

    e.索引NULL值列
        如果你的SQL語句頻繁使用到NULL值,應當考慮基於NULL值創建索引。爲使該查詢最優化,可以創建一個使用基於NULL值索引函數。
        (譯者按,如 create index i_tb_col on tab(nvl(col,null)); create index i_tb_col on tab(col,0);)

    f.避免基於索引的運算
        不要基於索引列做任何運算,除非你創建了一個相應的索引函數。或者重設設計列以使得where子句列上的謂詞不需要轉換。
        -->下面都是低效的SQL寫法
        where salary*5 > :myvalue   
        where substr(ssn,7,4) = "1234"
        where to_char(mydate,mon) = "january"

    g.避免使用NOT IN 和HAVING 
        在合適的時候使用not exists子查詢更高效。

    h.避免使用LIKE謂詞
        在合適地時候,如果能夠使用 = 運算應儘可能避免LIKE操作。

    i.避免數據類型轉換
        如果一個where 子句列是數字型,則不要使用引號。而對一個字符索引列,總是使用引號。下面是數據類型混用的情形。
        where cust_nbr = "123"
        where substr(ssn,7,4) = 1234

    j.使用decode與case
        使用decode 與case 函數能夠最小化查詢表的次數。

    k.不要害怕全表掃描
        並不是所有的OLTP系統在使用索引時是最優化的。如果你的查詢返回了表中的絕大部分數據,則全表掃描性能優於索引掃描。這取決於
        一些因素包括你的配置(db_file_multiblock_read_count, db_block_size),並行查詢,以及表塊和索引塊在buffer cache中的數量。

    l.使用別名
        在參照列的地方總是使用表別名。


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