ORACLE性能優化之SQL語句優化

操作環境:AIX +11g+PLSQL

包含以下內容:

1.  SQL語句執行過程

2.  優化器及執行計劃

3.  合理應用Hints

4.  索引及應用實例

5.   其他優化技術及應用


1.SQL語句執行過程

1.1 SQL語句的執行步驟

  1)語法分析,分析語句的語法是否符合規範,衡量語句中各表達式的意義。

  2)語義分析,檢查語句中涉及的所有數據庫對象是否存在,且用戶有相應的權限。

  3)視圖轉換,將涉及視圖的查詢語句轉換爲相應的對基表查詢語句。

  4)表達式轉換, 將複雜的 SQL 表達式轉換爲較簡單的等效連接表達式。

  5)選擇優化器,不同的優化器一般產生不同的“執行計劃”

  6)選擇連接方式, Oracle 主要有三種連接方式,對多表連接ORACLE會選擇適當的連接方式。

  7)選擇連接順序, 對多表連接 ORACLE 選擇哪一對錶先連接,選擇這兩表中哪張表做爲基礎數據表。

  8)選擇數據的搜索路徑,根據以上條件選擇合適的數據搜索路徑,比如,是選用全表搜索還是利用索引或是其他的方式。

  9)運行“執行計劃”

我們可以通過如下語句來查詢緩存中的執行計劃:

  1. SELECT t1.*,  
  2.          't2-->',  
  3.          t2.*  
  4.     FROM v$sql_plan t1  
  5.     JOIN v$sql t2  
  6.       ON t1.address = t2.address  
  7.      AND t1.hash_value = t2.hash_value  
  8.      AND t1.child_number = t2.child_number;--緩存中的執行計劃。  

1.2 典型SELECT語句完整的執行順序

  1)from子句組裝來自不同數據源的數據;

  2)where子句基於指定的條件對記錄行進行篩選;

  3)group by子句將數據劃分爲多個分組;

  4)使用聚集函數進行計算;

  5)使用having子句篩選分組;

  6)計算所有的表達式;

  7)計算select的字段;

  8)使用order by對結果集進行排序。

1.3 SQL語句執行過程

如下圖所示:


說明:

*這是一張SQL語句執行過程圖

*執行計劃是SQL語句執行過程中必然用到的

*執行計劃是優化器(Optimizer)的產物

*兩種不同的方式:CBO和RBO

查看優化器設置:

方法一:

  1. SELECT VALUE FROM v$parameter t WHERE t.name = 'optimizer_mode';  

方法二(SQLPLUS下執行):
  1. showparameter optimizer_mode  

*CBO用到了字典中的Statistics,而RBO沒有

分析統計信息相關SQL:

  1. analyze table tablename compute statistics;  
  1. analyze table tablename compute statistics for all indexes  
  1. analyze table tablename delete statistics   

2.優化器及執行計劃

2.1 SQL優化方法論

*ORACLE10g以後的版本,SQL優化的本質是基於對CBO和執行計劃的深刻理解,進入CBO時代,一定要理解執行計劃
*查看執行計劃有好多方式,比如使用
PL/SQL Developer工具,選中select語句,按F5鍵就可以顯示其執行計劃,不過顯示的不完全

*最好使用在Oracle官方的sqlplus工具,性能最好,方便直觀,下面介紹兩種查看執行計劃方式(也是最簡單的兩種方式)



關於執行計劃的一些知識:

* Full Table Scans 全表掃描
* Rowid Scans  rowid掃描
* Index Scans 索引掃描
* Index Unique Scans
* Index Range Scans
* Index Range Scans Descending
* Index Skip Scans
* Full Scans
* Fast Full Index Scans(CBO)
* Index Joins
* Bitmap Joins
* Cluster Scans  簇掃描
* Hash Scans   散列掃描
* Sample Table Scans  表取樣掃描

²在RBO時代,關於access path,很簡單,有index就用,而對於join方法,編程人員一般會通過調整關聯表之間的先後順序來獲得比較好的運行結果。有什麼缺點呢?
²有了CBO,簡單就是兩個字-----CBO走的是包辦婚姻:你的事交給我辦。
ORACLE默認情況下,週一到週五每天晚上10點到第二天早上6點以及整個週末期間會自動收集統計信息

可以查看參數:

  1. show parameter STATISTICS_LEVEL  

²問題:CBO執行計劃依賴的statistic不準確(缺失或者太舊),導致在計算執行成本時就會出現偏差,很可能會產生錯誤的執行計劃,怎麼辦呢?
第一步:重新收集統計信息!
第二部:第一部解決不了的情況下,使用Hints

3.合理應用Hints

3.1Hints

慎用hint,可能會產生嚴重的後果,比如append會產生鎖塊,導致併發資源等待等

Hints的分類:
*Hints forOptimization Approaches and Goals(4)
    /*+ ALL_ROWS */
    /*+ FIRST_ROWS ( n )*/
    /*+ CHOOSE */
    /*+ RULE */

*Hints for AccessPaths(12)
    /*+ FULL ( table ) */
    /*+ INDEX ( tableindex) */
    /*+ INDEX_ASC ( tableindex) */
    /*+ INDEX_COMBINE (table index) */
    /*+ INDEX_JOIN (table index) */
    /*+ INDEX_DESC (table index) */
    /*+ INDEX_FFS ( tableindex) */
    /*+ NO_INDEX ( tableindex) */
    /*+ AND_EQUAL ( tableindex index) */

*Hints for QueryTransformations(10)
*Hints for JoinOrders(2)
*Hints for JoinOperations(11)
    /*+ USE_NL ( table )*/
    /*+ USE_MERGE ( table) */
    /*+ USE_HASH ( table) */
    /*+ LEADING ( table )*/

*Hints for ParallelExecution(5)
*Additional Hints(13)

以下爲使用Hints的例子
  1. create table t_1(owner varchar2(30),table_name varchar2(30));  
  2. create table t_2(owner varchar2(30),table_name varchar2(30));  
  3. insert into t_1 SELECT owner,table_name FROM dba_tables;  
  4. insert into t_2 SELECT owner,view_name  FROM dba_views t;  
  5. create index idx_t_1 on t_1(table_name);  
  6. create index idx_t_2 on t_2(table_name);  
  7. analyze table t_1  compute statistics;   
  8. analyze table t_2  compute statistics;   
  9.   
  10. SELECT *  
  11.   FROM (SELECT * FROM t_1  
  12.         UNION ALL  
  13.         SELECT * FROM t_2) aa  
  14.  WHERE aa.table_name LIKE 'Z%';                 ---- Full Table Scans  
  15.    
  16. SELECT /*+ index(AA.t_1 idx_t_1) index(AA.t_2 idx_t_2)*/ *  
  17.   FROM (SELECT * FROM t_1  
  18.         UNION ALL  
  19.         SELECT * FROM t_2) AA  
  20.  WHERE AA.table_name LIKE 'Z%';               ---- Index Scans  

貼上執行圖:


4.索引及應用實例

4.1什麼是索引

*Oracle的索引是一種自平衡的B*Tree存儲結構,其基本存儲單位爲數據塊,稱之爲節點,共有三種類型的節點:根(root)節點,分枝(Branch)節點,葉(leaf)節點。
*分枝節點存儲{索引值,鍵值對應下一級節點塊地址,lmc指針}
*葉節點存儲{索引值及其rowid,當前節點的前後節點的數據塊地址}
所有葉節點上的兩個指針形成一個雙向鏈表,在這個雙向鏈表上的所有索引值,從小到大排列,而對於倒序
desc索引,則是從大到小排列

B*TREE索引圖:

4.2索引分類

邏輯上:
Single column
單列索引
Concatenated
多列索引
Unique
唯一索引
Non-Unique
非唯一索引
Function-based
函數索引
Domain
域索引

物理上:
Partitioned
分區索引
Non-Partitioned
非分區索引
B*tree

 
Normal 正常型B
 
ReverseKey 反轉型B
 
Bitmap 位圖索引

4.3什麼時候使用索引

*如果要檢索全表,不必要建索引,因爲索引會帶來額外的IO操作。
*如果檢索的記錄數佔全部表記錄的10%以下可以考慮建索引(大表)
*表之間的關聯字段可以考慮建索引,特別是一張大表和一張小表的關聯。
*B*Tree索引適合於大量的增、刪、改(OLTP);
     不適合用包含OR操作符的查詢;一般不適用NULL判斷;
     適合高基數的列(重複值少)
*Bitmap索引適合於決策支持系統OLAP
    做UPDATE代價比較高;會鎖塊;
    非常適合OR操作符的查詢;
    適合低基數的列(比如,只有YN兩種值) ;
*Reverse索引反轉了b*tree索引碼中的字節,是索引條目分配更均勻,多用於並行服務器環境下,用於減少索引葉的競爭。
    索引是雙刃劍,在查詢與DML之間尋求平衡

4.4改寫SQL使用索引

*普通索引列 a is not null 按邏輯改爲a>0或a>''

*like操作改寫

*能用union all絕不用union,除非要去重

*in操作雖然簡單易懂,但oracle內部會轉換爲表連接查詢,使用in會多一步轉換操作,所以建議使用表關聯查詢
*not in 強烈建議使用not exists或(外連接+判斷爲空)
*<>(不等於)操作不走索引,推薦a<>0改爲(a>0 ora<0)    a<>’’改爲a>’’
*提防隱式類型轉換, oracle內部處理a=0a=‘0’是完全不同的,甚至會導致不走索引,這個深有體會,最近一個項目就是這個隱式類型轉換出了問題,導致速度
很慢

4.5索引應用

1.用合適的索引來避免不必要的全表掃

    如果要在索引列查詢is not null條件,建議列加上is not null約束,默認值約束,

    然而確實由於某種原因索引列設計爲null,還想通過is null條件走索引,該如何是好呢?請看

  1. drop table t_tab1;  
  2. create table t_tab1 as   
  3.     SELECT t.owner,  
  4.        t.object_name,  
  5.        t.object_type,  
  6.        t.created,  
  7.        t.last_ddl_time  
  8.     FROM dba_objects t;  
  9. analyze table t_tab1  compute statistics;  
  10. create index idx01_t_tab1 on t_tab1(last_ddl_time);--普通索引  
  11. set autotrace trace;  
  12. SELECT * FROM t_tab1 t where t.last_ddl_time is null;  

執行計劃如下圖:


如上情況調整爲複合索引

  1. drop index idx01_t_tab1;  
  2. create index idx01_t_tab1 on t_tab1(last_ddl_time,1);--加了個常量  
  3. set autotrace trace;  
  4. SELECT * FROM t_tab1 t where t.last_ddl_time is null;  

執行計劃如下圖:



 例2:用合適的函數索引來避免看似無法避免的全表掃描
  1. drop table t_tab1 purge;  
  2. create table t_tab1 as   
  3.         SELECT t.owner,  
  4.             t.object_name,  
  5.         t.object_type,  
  6.         t.OBJECT_ID,  
  7.         t.created,  
  8.         t.last_ddl_time  
  9.     FROM dba_objects t;  
  10. CREATE INDEX IDX01_T_TAB1 ON T_TAB1(object_name);  
  11. analyze table t_tab1  compute statistics;   
  12. set autot trace  
  13. SELECT * FROM t_tab1 t where t.object_name like '%20121231';  

執行計劃如下:


改進索引,此處使用反轉函數索引,此外經常用到的函數索引還有,instr(),substr()

  1. drop index IDX01_T_TAB1;  
  2. CREATE INDEX IDX02_T_TAB1 ON T_TAB1(reverse(object_name));  
  3. analyze table t_tab1  compute statistics;   
  4. SELECT * FROM t_tab1 t where reverse(t.object_name) like reverse('%20121231');  
執行計劃如下:



5.其他優化技術及應用

5.1其他優化技術及思路

並行技術,並行執行目標SQL語句,這實際上是以額外的資源消耗來換取執行時間的縮短,很多情況下使用並行是針對某些SQL的唯一優化手段。

使用shell調度或其他調度工具。

      SQL語句級別的並行:/*+parallel*/

       /*+ parallel(table_name 4)*/

表壓縮技術

  compress

NOLOGGING

  減少日誌

Partition技術

  分而治之

中間表/臨時表事務分解思路

  ‘大事化小’ 

求平衡

    CPU,Memory很強大,IO存在瓶頸(最普遍的情況)

使用新特性

     insertall 啦       使用listagg()比wm_concat()快大概50倍、row_number()等分析函數

軟硬件資源合理搭配

       黔驢技窮,要求加硬件資源? Boss會對你說,找會計去吧,提前給你開工資 ……

5.2 SQL優化總結

SQL的優化的手段是五花八門、不一而足的,包括但不限於如下措施:

*如果是統計信息不準或是因爲CBO計算某些SQL的執行路徑(Access Path)的成本所用公式的先天不足而導致的SQL性能問題,
 我們可以通過重新收集統計信息或者手工修改統計信息或者使用
Hint來加以解決;
*如果是SQL語句的寫法問題,我們可以通過在不更改業務邏輯的情況下改寫SQL來加以解決;
*如果是不必要的全表掃描/排序而導致了目標SQL的性能問題,我們可以通過建立合適的索引(包括函數索引、位圖索引等)來加以解決;
*如果是表或者索引的不良設計導致的目標SQL的性能問題,我們可以通過重新設計表/索引,重新組織表裏的數據來加以解決;
*如果上述調整措施都失效,我們可以考慮用並行來縮短目標SQL的執行時間;
*如果上述調整措施、包括並行都失效,我們還可以在聯繫實際業務的基礎上更改目標SQL的執行邏輯,甚至不執行目標SQL,這是最徹底的優化:)
發佈了37 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章