Oracle SQL優化 總結

roominess發表於2012-02-01

之前的blog中零零散散的整理了一些優化相關的內容,找起來比較麻煩,所以總結一下,檢視的時候方便一點。這篇BLog只看SQL 優化的相關的注意事項,資料庫優化部分以後有空在整理。

 

SQL 的優化主要涉及幾個方面:

(1)    相關的統計資訊缺失或者不準確

(2)    索引問題

(3)    SQL 的本身的效率問題,比如使用繫結變數,批量DML 採用bulk等,這個就考驗寫SQL的基本功了,這一點也是最主要的一點。

 

一.SQL 編寫注意事項

 1.1 檢視SQL

對於生產環境上的SQL,可以從AWR 或者 Statspack 報告中獲取相關的SQL 資訊。

 

這部分參考:

Oracle AWR 介紹

http://blog.csdn.net/tianlesoftware/article/details/4682300

 

statspack安裝使用 和report 分析

http://blog.csdn.net/tianlesoftware/article/details/4682329

 

       檢視SQL 的效能怎麼樣,最直接的工具就是通過執行計劃,通過執行計劃可以看到SQL 的執行路徑,邏輯讀,物理讀等資訊,可以這些資訊,可以幫助我們判斷SQL 是否還有優化的餘地。

 

1.2 SQL 編寫的具體注意事項

       這部分工作是基本功。 在SQL 編寫過程中, 避免一些低效的寫法,能將SQL的效率提高几倍。 如:

 

to_char(created,'yyyy') = '2011'

trunc(created,'y') = to_date('01-jan-2011','dd-mon-yyyy')

 

與使用TRUNC 相比,使用TO_CHAR 所用的CPU 時間與前者相差一個數量級(即相差12倍)。因為TO_CHAR 必須把日期轉換為一個串,這要使用一個更大的程式碼路徑,並利用當前的所有NLS來完成這個工作。然後必須執行一個串與串的比較。另一方面,TRUNC 只需把後5 個位元組設定為1.然後將兩個7 位元組的二進位制數進行比較。因此,如果只是要截斷一個DATE 列,你將應該避免使用TO_CHAR。

 

之前從網上轉載了一篇文章,連結如下:

Oracle SQL的優化

http://blog.csdn.net/tianlesoftware/article/details/4672023

 

這是幾年前轉載的文章,其中內容有些也有誤,這裡就不更正了。

 

1.3 多表關聯方式

       表之間的關聯有如下三種方式:

(1)    Nested Loop

Inner table 迴圈與outer table匹配,這種是表有索引,選擇性較好,表之間的差距不大。 ===》兩層for 迴圈,小表匹配大表。

(2)    Hash John

小表做hash ,放記憶體,然後拿大表的每條記錄做hash,然後與之前小表的Hash 值匹配。==》大表匹配小表。

(3)    Sorted Merge Into

表有序,並且沒有索引。

 

具體參考:

       多表連線的三種方式詳解 HASH JOIN MERGE JOINNESTED LOOP

       http://blog.csdn.net/tianlesoftware/article/details/5826546

 

二. 相關理論說明

2.1 Oracle 優化器:CBO 和 RBO

Oracle 的優化器有兩種:

                   RBO(Rule-BasedOptimization): 基於規則的優化器

                   CBO(Cost-BasedOptimization): 基於代價的優化器

 

CBO(Cost Based Optimizer)的思路是讓Oracle 獲取所有執行計劃相關的資訊,通過對這些資訊做計算分析,最後得出一個代價最小的執行計劃作為最終的執行計劃。

       從10g開始,Oracle 已經徹底丟棄了RBO。 即使在表,索引沒有被分析的時候,Oracle依然會使用CBO。此時,Oracle 會使用一種叫做動態取樣的技術,在分析SQL的時候,動態的收集表,索引上的一些資料塊,使用這些資料塊的資訊及字典表中關於這些物件的資訊來計算出執行計劃的代價,從而挑出最優的執行計劃。

 

當表沒有做分析的時候,Oracle 會使用動態取樣來收集統計資訊,這個動作只有在SQL執行的第一次,即硬分析階段使用,後續的軟分析將不在使用動態取樣,直接使用第一次SQL硬分析時生成的執行計劃。

 

相關連結:

Oracle Optimizer CBO RBO

http://blog.csdn.net/tianlesoftware/article/details/5824886

 

Oracle CBO 與 RBO

http://blog.csdn.net/tianlesoftware/archive/2010/07/11/5709784.aspx

 

Oracle 分析及動態取樣

http://blog.csdn.net/tianlesoftware/article/details/5845028

 

 

2.2 軟解析和硬解析

Oracle對此SQL將進行幾個步驟的處理過程:

    1、語法檢查(syntax check): 檢查此sql的拼寫是否語法。

    2、語義檢查(semantic check): 諸如檢查sql語句中的訪問物件是否存在及該使用者是否具備相應的許可權。

    3、對sql語句進行解析(prase): 利用內部演算法對sql進行解析,生成解析樹(parse tree)及執行計劃(execution plan)。

    4、執行sql,返回結果(execute and return)

 

其中解析分為:

 Hard Parse: 就是上面提到的對提交的Sql完全重新從頭進行解析(當在Shared Pool中找不到時候將會進行此操作),總共有一下5個執行步驟:

          1:語法分析

          2:許可權與物件檢查

         3: 在共享池中檢查是否有完全相同的之前完全解析好的. 如果存在,直接跳過4和5,執行Sql, 此時算soft parse.

         4:選擇執行計劃

         5:產生執行計劃

 

注:建立解析樹、生成執行計劃對於sql的執行來說是開銷昂貴的動作,所以,應當極力避免硬解析,儘量使用軟解析。這就是在很多專案中,倡導開發設計人員對功能相同的程式碼要努力保持程式碼的一致性,以及要在程式中多使用繫結變數的原因。

 

Soft Parse: 就如果是在Shared Pool中找到了與之完全相同的Sql解析好的結果後會跳過Hard Parse中的後面的兩個步驟。

 

Oracle SQL的硬解析和軟解析

http://blog.csdn.net/tianlesoftware/archive/2010/04/08/5458896.aspx

 

Oracle 高 Version counts 問題說明

http://blog.csdn.net/tianlesoftware/article/details/6628232

 

Oracle SQL Parsing FlowDiagram(SQL 解析流程圖)

http://blog.csdn.net/tianlesoftware/article/details/6625683

 

那麼執行計劃放在記憶體的什麼位置,在一下的Blog 有說明:

Oracle Library cache 內部機制 說明

http://blog.csdn.net/tianlesoftware/article/details/6629869

 

與解析相關的一個重要引數:cursor_sharing,它決定什麼情況下使用相同的cursor,從某種意義上講,決定是否需要進行解析,該引數有3個值:

(1)FORCE

       Allowsthe creation of a new cursor if sharing an existing cursor, or if the cursorplan is not optimal.

 (2)SIMILAR

       Causesstatements that may differ in some literals, but are otherwise identical, toshare a cursor, unless the literals affect either the meaning of the statementor the degree to which the plan is optimized.

 (3)EXACT

       Onlyallows statements with identical text to share the same cursor.

       --只有SQL 語句完全相同的情況下,才會使用相同的cursor,即執行計劃。

 

Oracle cursor_sharing 引數 詳解

http://blog.csdn.net/tianlesoftware/article/details/6551723

 

2.3 執行計劃

生成SQL的執行計劃是Oracle在對SQL做硬解析時的一個非常重要的步驟,它制定出一個方案告訴Oracle在執行這條SQL時以什麼樣的方式訪問資料:索引還是全表掃描,是Hash Join還是Nested loops Join等。 

 

Oracle 執行計劃(Explain Plan) 說明

http://blog.csdn.net/tianlesoftware/article/details/5827245

 

Oracle 從快取裡面查詢真實的執行計劃

http://blog.csdn.net/tianlesoftware/article/details/6556850

 

Oracle Recursive Calls 說明  

http://blog.csdn.net/tianlesoftware/article/details/6561620

 

我們也可以使用OracleHint 來強制的改變SQL的執行計劃,當然Oracle 不建議這麼做,因為只要統計資訊正確的情況下,CBO 的分析就過一般都是正確的。

 Oracle Hint

http://blog.csdn.net/tianlesoftware/article/details/5833020

 

2.4 10053 和 10046 事件

2.4.1 10053事件

我們在檢視一條SQL的執行計劃的時候,只能看到CBO 最終告訴我們的執行計劃結果,但是不知道CBO 是根據什麼來做的。 如果遇到了執行計劃失真,如:一個SQL語句,很明顯oracle應該使用索引,但是執行計劃卻沒有使用索引。無法進行分析判斷。

10053事件就提供了這樣的功能。它產生的trace檔案提供了Oracle如何選擇執行計劃,為什麼會得到這樣的執行計劃資訊。

 

對於10053事件的trace檔案,我們只能直接閱讀原始的trace檔案,不能使用tkprof工具來處理,tkprof工具只能用來處理sql_trace 和 10046事件產生的trace檔案。

 

10053事件有兩個級別:

         Level2:2級是1級的一個子集,它包含以下內容:

Column statistics

Single Access Paths

Join Costs

Table Joins Considered

Join Methods Considered (NL/MS/HA)

         Level1: 1級比2級更詳細,它包含2級的所有內容,在加如下內容:

Parameters used by the optimizer

Index statistics

 

啟用10053事件:

ALTER SESSION SET EVENTS='10053 trace namecontext forever, level 1';

ALTER SESSION SET EVENTS='10053 trace namecontext forever, level 2';

 

關閉10053事件:

ALTER SESSION SET EVENTS '10053 trace namecontext off';

 

 

說明:

(1)sqlplus中開啟autotrace看到的執行計劃實際上是用explain plan 命令得到的,explain plan 命令不會進行bind peeking。應該通過v$sql_plan檢視SQL的真實的執行計劃。

(2)10053只對CBO有效,而且如果一個sql語句已經解析過,就不會產生新的trace資訊。

 

2.4.2 10046 事件:

10046 事件主要用來跟蹤SQL語句,它並不是ORACLE 官方提供給使用者的命令,在官方文件上也找不到事件的說明資訊。 但是用的卻比較多,因為10046事件獲取SQL的資訊比SQL_TRACE 更多。 更有利於我們對SQL的判斷。

 

10046 事件按照收集資訊內容,可以分成4個級別:

Level 1: 等同於SQL_TRACE 的功能

Level 4: 在Level 1的基礎上增加收集繫結變數的資訊

Level 8: 在Level 1 的基礎上增加等待事件的資訊

Level 12:等同於Level 4+Level 8, 即同時收集繫結變數資訊和等待事件資訊。


 --啟動10046事件

  SQL>alter session set events‘10046 tracename context forever, level 12’; 


-- 關閉10046事件

SQL>alter session set events ‘10046 trace name context off’; 


也可以使用oradebug 命令來執行10046:

SYS@anqing1(rac1)> oradebug setmypid

SYS@anqing1(rac1)> oradebug event 10046trace name context forever,level 8;

SYS@anqing1(rac1)> oradebug event 10046trace name context off;

SYS@anqing1(rac1)> oradebugtracefile_name

/u01/app/oracle/admin/anqing/udump/anqing1_ora_17800.trc

 

具體的內容參考:

Oracle oradebug 命令 使用說明

http://blog.csdn.net/tianlesoftware/article/details/6525628

 

Oracle 跟蹤事件 set event

http://blog.csdn.net/tianlesoftware/article/details/4977827

 

Oracle 10053 事件

http://blog.csdn.net/tianlesoftware/article/details/5859027

      

Event 10053 執行計劃繫結變數 Bind peeking

http://blog.csdn.net/tianlesoftware/article/details/5544307

 

Oracle SQL Trace 和 10046 事件

http://blog.csdn.net/tianlesoftware/article/details/5857023

 

使用 Tkprof 分析 ORACLE 跟蹤檔案

http://blog.csdn.net/tianlesoftware/article/details/5632003

 

2.5 統計資訊

 優化器收集的統計資訊包括如下內容:

           1)Table statistics

                        Number of rows

                        Number of blocks

                        Average row length

           2)Column statistics

                        Number of distinctvalues (NDV) in column

                        Number of nulls incolumn

                        Data distribution(histogram)

           3)Index statistics

                        Number of leaf blocks

                        Levels

                        Clustering factor

           4)System statistics

                        I/O performance and utilization

                        CPU performance andutilization

 

 

統計資訊收集如下資料:

(1)表自身的分析: 包括表中的行數,資料塊數,行長等資訊。

(2)列的分析:包括列值的重複數,列上的空值,資料在列上的分佈情況。

(3)索引的分析: 包括索引葉塊的數量,索引的深度,索引的聚合因子等。

 

這些統計資訊存放在資料字典裡,如:

(1).  DBA_TABLES

(2).  DBA_OBJECT_TABLES

(3).  DBA_TAB_STATISTICS

(4).  DBA_TAB_COL_STATISTICS

(5).  DBA_TAB_HISTOGRAMS

(6).  DBA_INDEXES

(7).  DBA_IND_STATISTICS

(8).  DBA_CLUSTERS

(9).  DBA_TAB_PARTITIONS

(10).                      DBA_TAB_SUBPARTITIONS

(11).                      DBA_IND_PARTITIONS

(12).                      DBA_IND_SUBPARTITIONS

(13).                      DBA_PART_COL_STATISTICS

(14).                      DBA_PART_HISTOGRAMS

(15).                      DBA_SUBPART_COL_STATISTICS

(16).                      DBA_SUBPART_HISTOGRAMS

 

統計資訊的準確程度,直接決定SQL的效率。 所以需要定期的收集相關物件的統計資訊。Oracle 的Statistic 資訊的收集分兩種:自動收集和手工收集。

 

Oracle 的Automatic StatisticsGathering 是通過Scheduler 來實現收集和維護的。 Job 名稱是GATHER_STATS_JOB, 該Job收集資料庫所有物件的2種統計資訊:

            (1)Missing statistics(統計資訊缺失)

            (2)Stale statistics(統計資訊陳舊)

 

該Job 是在資料庫建立的時候自動建立,並由Scheduler來管理。Scheduler 在maintenance windows open時執行gather job。 預設情況下,job 會在每天晚上10到早上6點和週末全天開啟。該過程首先檢測統計資訊缺失和陳舊的物件。然後確定優先順序,再開始進行統計資訊。

 

Scheduler Job的 stop_on_window_close 屬性控制GATHER_STATS_JOB 是否繼續。該屬性預設值為True. 如果該值設定為False,那麼GATHER_STATS_JOB 會中斷,而沒有收集完的物件將在下次啟動時繼續收集。

 

Gather_stats_job 呼叫dbms_stats.gather_database_stats_job_proc過程來收集statistics 的資訊。 該過程收集物件statistics的條件如下:

(1)物件的統計資訊之前沒有收集過。

(2)當物件有超過10%的rows 被修改,此時物件的統計資訊也稱為stale statistics。

 

Oracle Statistic 統計資訊 小結

http://blog.csdn.net/tianlesoftware/article/details/4668723

 

Oracle 判斷 並 手動收集 統計資訊 指令碼

http://blog.csdn.net/tianlesoftware/article/details/6445868

 

 

三.索引

3.1 索引分類

索引對DB的效能中起著重要的作用。 Oracle 有如下型別的索引:

B樹索引(預設型別)
點陣圖索引
HASH索引
索引組織表索引
反轉鍵(reverse key)索引
基於函式的索引
分割槽索引(本地和全域性索引)
點陣圖連線索引

 

3.2 索引限制

這部分內容應該放到SQL 編寫部分,不過為了強調,還是放到這塊。

 

即使相關欄位上有索引,在如下4種情況,也不會走作引:

(1)    使用不等於操作符(<>、!=)    

(2)    使用IS NULL 或IS NOT NULL

(3)    使用函式

(4)    比較不匹配的資料型別  

 

所以在SQL 編寫過程中,儘量避免以上4種情況。

 

具體參考:

Oracle 索引詳解

http://blog.csdn.net/tianlesoftware/article/details/5347098

 

3.3 索引維護

這部分內容包括:

(1)  索引的選擇性: distinct/rows, 接近與1,選擇性越高,直方圖--》資料傾斜。

(2) 索引的擴充套件:index segment 是由extents組成,如果extents大於10,可以考慮重建索引。

(3) 索引碎片:查詢index_stats表以確定索引中刪除的、未填滿的葉子(Leaf)行的百分比 和 height 欄位。 如果索引的葉子行的碎片超過10%,或者 index_stats中height > =4, 可以考慮對索引進行重建。

              select name,height, del_lf_rows, lf_rows,round((del_lf_rows/(lf_rows+0.0000000001))*100) "Frag Percent" from index_stats

 

具體參考:

索引維護:

http://blog.csdn.net/tianlesoftware/article/details/5680706

 

3.4 索引的 Clustering Factor 引數

       這個引數是個神奇的引數,先看一種情況,有的人應該遇到過,就是表的欄位上有索引,但根據這個欄位做查詢時,卻發現Oracle並沒有使用索引?  並且查詢條件沒有限制索引。那麼Oracle 為什麼不走索引? 很可能就是和這個引數值有關。

       可以從dba_indexes 表裡檢視到每個具體物件的Clustering Factor值:

 select owner,index_name, clustering_factor, num_rows from dba_indexes whereowner='SYS' and index_name='IDX_T_ID';

 

       該引數反應的是資料在物理block的上的連續性,如果該值接近於物件的block數,那麼資料在block上的儲存就是有序的,如果接近與表的行數,可以反應出資料的儲存無序。

       當資料有序時,我們查詢一個資料時,可以從一個block裡一次性讀出,如果無序,那麼我們可能就需要讀取多個block,這樣I/O次數增加,CBO 就會認為這種代價更大,從而選擇全表掃描來代替索引。從而導致即使有索引,也不會走。

       但是隨著系統使用時間的越長,Clustering Factor值是會越來越大的。解決這個問題的唯一方法就是對錶進行move。

 

具體參考:

Oracle Index Clustering Factor 說明

http://blog.csdn.net/tianlesoftware/article/details/6585453

 

3.5 索引掃描5種型別:

(1)index unique scan: 查詢結果返回一行記錄

(2)index range scan: 查詢結果返回多行記錄。

 

(3)index full scan:可能進行全Oracle索引掃描而不是範圍掃描,需要注意的是全Oracle索引掃描只在CBO模式下才有效。      CBO根據統計數值得知進行全Oracle索引掃描比進行全表掃描更有效時,才進行全Oracle索引掃描,而且此時查詢出的資料都必須從索引中可以直接得到。

 

(4)index fast full scan: 與 index full scan很類似,但是一個顯著的區別就是它不對查詢出的資料進行排序,即資料不是以排序順序被返回。在這種存取方法中,可以使用多塊讀功能,也可以使用並行讀入,以便獲得最大吞吐量與縮短執行時間。

 

(5)index skip scan: INDEX SKIP SCAN,發生在多個列建立的複合索引上,如果SQL中謂詞條件只包含索引中的部分列,並且這些列不是建立索引時的第一列時,就可能發生INDEX SKIP SCAN。這裡SKIP的意思是因為查詢條件沒有第一列或前面幾列,被忽略了。

 

具體參考:

Oracle 索引掃描的五種型別

http://blog.csdn.net/tianlesoftware/article/details/5852106

 

四.繫結變數

這個也是SQL 編寫的基本功.  簡單的說,使用繫結變數可以避免進行硬解析,減少對資源的消耗。

 

Oracle裡的所有SQL 語句都是implicitly sharable的。 SQL 在執行之前,要通過一個hash 運算,生成相關的cursor。 如果通過hash 運算之後,發現已經了有對應的cursor,那就可以直接使用之前的cursor 和plan。如果不存在,就需要進行硬解析,而硬解析是一個非常耗資源的操作。需要儘量減少硬解析。

如何保證每次HASH 運算之後的hash 值都一樣,那麼這就需要通過繫結變來來實現。

 

在第一次執行執行SQL 之後,如果使用了繫結變數,那麼Oracle 在硬解析的時候,會進行一個叫Peek的操作。 也可以稱為偷窺。就是把實際值帶進去,幫助產生更加準確的執行計劃。比如對應的Peek列上有嚴重的資料傾斜,假設我們已經對錶進行了統計資訊收集,oracle 會產生該列的直方圖(histogram),在peek的時候,就會根據直方圖來決定,是走索引還是走全表掃描更划算。

 

因此第一執行產生的執行計劃肯定是最優的。現在假設資料傾斜有2個值,一個佔90%,一個佔10%。 我們第一使用10%的值,所以第一次走索引。 那麼如果我們以後在繫結時,使用了90%的值,那麼這時候,Oracle 還是會使用之前的cursor,繼續走索引,此時走索引就不是最優的了。

這個就是Oracle 10g裡繫結變數的一個問題。 在第一次硬解析之後,以後所有的操作都會使用之前產生的cursor 和plan。所以在10g裡,對於列上有嚴重資料傾斜的,最好是不採用繫結變數。

 

在Oracle 11g裡,對這個問題,進行了優化,使用了Adaptive Cursor Sharing,它可以產生多個共享cursor。如果是90%的值,就使用cursor 1,如果是10%的cursor,就使用corsor 2. 在這個轉換的過程中還是有可能再次產生硬解析。

 

Oracle 11g的繫結變數處理過程如下:

       當我們第一去執行一個帶有繫結變數的SQL時,Oracle 會進行硬解析,但是硬解析不能確定最優的執行計劃,所以這時候有了Peek。 也可以說是偷窺,即把實際值帶入,來生成一個selectivity estimate。 然後來選擇最優的一個執行計劃來執行。

       這是第一次執行SQL語句。以後執行時就會使用已經存在的plan和cursor。 Oracle 通過Adaptive Cursor Sharing特性允許同一個SQL 可以使用多個執行計劃。

       在每次執行時,Oracle會根據Peek 的selectivity estimate 值和直方圖(如果存在)來判斷已經存在的cursor 是否是最優的,如果不是,就重新建立一個child cursor,並講Bind-Sensitive 標記為Y。

       而且Oracle在SQL 每次執行時,都會收集相關的統計資訊,然後根據統計資訊進行判斷,如果比上次的更好,就在建立一個child cursor,並將Bind-Aware 標記為Y。

       當標記為bind-aware cursor 的cursor在下次執行時,Oracle根據新的bind value 來生成新的plan和cursor,並將原來的cursor標記為非共享,即V$SQL.IS_SHAREABLE 設定為 N,當這種cursor 長期不被使用時, 就會被移出shared SQL area.

       在bind-aware cursor建立新的cursor 之後,如果這個cursor 和之前某個存在的cursor一樣,那麼Oracle 會對他們進行合併。

       如果在cache裡不能找到bind-aware對應的plan,那麼就會重新進行一次硬解析,來生成plan 和cursor,如果這個plan 以後被新的cursor 使用,那麼Oracle 會將這2個cursor 進行合併。

 

Oracle 繫結變數 詳解

http://blog.csdn.net/tianlesoftware/article/details/5856430

 

Oracle 繫結變數 示例

http://blog.csdn.net/tianlesoftware/article/details/6324243

 

Oracle 10g 與 11g 繫結變數(Bind Variable) 區別 說明

http://blog.csdn.net/tianlesoftware/article/details/6591222

 

對於繫結變數的cursor 能否重用,與Cursor_sharing 引數有很大關係。具體參考:

Oracle cursor_sharing 引數 詳解

http://blog.csdn.net/tianlesoftware/article/details/6551723

 

Oracle Library cache 內部機制 說明

http://blog.csdn.net/tianlesoftware/article/details/6629869

 

 

五.其他與SQL 效能相關的連結

 

Oracle Sequence Cache 引數說明

http://blog.csdn.net/tianlesoftware/article/details/5995051

 

Oracle bulk 示例

http://blog.csdn.net/tianlesoftware/article/details/6599003

 

Oracle Bulk 與 效能優化 說明

http://blog.csdn.net/tianlesoftware/article/details/6578351

 

Oracle Pipelined TableFunctions 與 效能優化 說明

http://blog.csdn.net/tianlesoftware/article/details/6601540

 

Oracle arraysize 和 fetchsize 引數 與 效能優化 說明

http://blog.csdn.net/tianlesoftware/article/details/6579913

 

Oracle 利用 rowid 提升 update 效能

http://blog.csdn.net/tianlesoftware/article/details/6576156

 

Oracle Parallel Execution(並行執行)

http://blog.csdn.net/tianlesoftware/article/details/5854583

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/15880878/viewspace-715423/,如需轉載,請註明出處,否則將追究法律責任。

相關文章