No1:選擇合適的優化器No2:共享SQL為了不重復解析相同的SQL語句,在第一次解析之后, Oracle將SQL語句存放在內存中.這塊位于系統全局區域SGA(system global area)的共享池(shared buffer pool)中的內存可以被所有的數據庫用戶共享. 因此,當你執行一個SQL語句(有時被稱為一個游標)時,假如它和之前的執行過的語句完全相同, ORACLE就能很快獲得已經被解析的語句以及最好的執行路徑. ORACLE的這個功能大大地提高了SQL的執行性能并節省了內存的使用. 可惜的是ORACLE只對簡單的表提供高速緩沖(cache buffering) ,這個功能并不適用于多表連接查詢.數據庫治理員必須在init.ora中為這個區域設置合適的參數,當這個內存區域越大,就可以保留更多的語句,當然被共享的可能性也就越大了.當你向ORACLE 提交一個SQL語句,ORACLE會首先在這塊內存中查找相同的語句. 這里需要注明的是,ORACLE對兩者采取的是一種嚴格匹配,要達成共享,SQL語句必須完全相同(包括空格,換行等).共享的語句必須滿足三個條件: A. 字符級的比較:B. 兩個語句所指的對象必須完全相同: C. 兩個SQL語句中必須使用相同的名字的綁定變量(bind variables)No3. 選擇最有效率的表名順序(只在基于規則的優化器中有效)ORACLE的解析器按照從右到左的順序處理FROM子句中的表名,因此FROM子句中寫在最后的表(基礎表 driving table)將被最先處理. 在FROM子句中包含多個表的情況下,你必須選擇記錄條數最少的表作為基礎表.當ORACLE處理多個表時, 會運用排序及合并的方式連接它們.首先,掃描第一個表(FROM子句中最后的那個表)并對記錄進行排序,然后掃描第二個表(FROM子句中最后第二個表),最后將所有從第二個表中檢索出的記錄與第一個表中合適記錄進行合并.假如有3個以上的表連接查詢, 那就需要選擇交叉表(intersection table)作為基礎表, 交叉表是指那個被其他表所引用的表. No4:WHERE子句中的連接順序ORACLE采用自下而上的順序解析WHERE子句,根據這個原理,表之間的連接必須寫在其他WHERE條件之前, 那些可以過濾掉最大數量記錄的條件必須寫在WHERE子句的末尾.No5:避免使用select *當你想在SELECT子句中列出所有的COLUMN時,使用動態SQL列引用 ‘*’ 是一個方便的方法.不幸的是,這是一個非常低效的方法. 實際上,ORACLE在解析的過程中, 會將’*’ 依次轉換成所有的列名, 這個工作是通過查詢數據字典完成的, 這意味著將耗費更多的時間. No6:減少訪問數據庫的次數當執行每條SQL語句時, ORACLE在內部執行了許多工作: 解析SQL語句, 估算索引的利用率, 綁定變量 , 讀數據塊等等. 由此可見, 減少訪問數據庫的次數 , 就能實際上減少ORACLE的工作量.No7:用TRUNCATE替代DELETE當刪除表中的記錄時,在通常情況下, 回滾段(rollback segments ) 用來存放可以被恢復的信息. 假如你沒有COMMIT事務,ORACLE會將數據恢復到刪除之前的狀態(準確地說是恢復到執行刪除命令之前的狀況)而當運用TRUNCATE時, 回滾段不再存放任何可被恢復的信息.當命令運行后,數據不能被恢復.因此很少的資源被調用,執行時間也會很短. No8:適當的使用commit這樣程序的性能得到提高,需求也會因為COMMIT所釋放的資源而減少: COMMIT所釋放的資源:a. 回滾段上用于恢復數據的信息.b. 被程序語句獲得的鎖c. redo log buffer 中的空間d. ORACLE為治理上述3種資源中的內部花費在使用COMMIT時必須要注重到事務的完整性,現實中效率和事務完整性往往是魚和熊掌不可得兼,另外過于頻繁的使用commit效率也會降低。No9:減少對表的查詢在含有子查詢的SQL語句中,要非凡注重減少對表的查詢.Update 多個Column 例子: 低效: UPDATE EMP SET EMP_CAT = (SELECT MAX(CATEGORY) FROM EMP_CATEGORIES), SAL_RANGE = (SELECT MAX(SAL_RANGE) FROM EMP_CATEGORIES) WHERE EMP_DEPT = 0020; 高效: UPDATE EMP SET (EMP_CAT, SAL_RANGE) = (SELECT MAX(CATEGORY) , MAX(SAL_RANGE) FROM EMP_CATEGORIES) WHERE EMP_DEPT = 0020;No10:IN與EXIST先說IN他相當對inner table執行一個個帶有distinct的子查詢語句,然后得到的查詢結果集再與outer table進行連接,當然連接的方式和索引的使用仍然同于普通的兩表連接。
select * from T1 where x in (select y from T2);可以轉換成如下select * from T1,(select distinct y from T2) T2where T1.x=T2.y;再說exists實際上exists相當于對outer table進行全表掃描,用從中檢索到的每一行與inner table做循環匹配輸出相應的符合條件的結果,其主要開銷是對outer table的全表掃描(full scan),而連接方式是nested loop方式。可以寫成select * from T1 where exists (select NULL from T2 where T2.y=T1.x);轉換成for cursor1 in (select * from T1)loopif (exists (select NULL from T2 where T2.y=cursor1.x))then 返回匹配的記錄;end if;end loop;通過上面的解釋,現在很輕易明白當T2數據量巨大且索引情況不好(大量重復值等),則不宜使用產生對T2的distinct檢索而導致系統開支巨大的IN操作,反之當T1表數據量巨大(不受索引影響)而T2表數據較少且索引良好則不宜使用引起T1全表掃描的EXISTS操作No11:NOT IN, NOT EXIST在子查詢中,NOT IN子句將執行一個內部的排序和合并. 無論在哪種情況下,NOT IN都是最低效的 (因為它對子查詢中的表執行了一個全表遍歷). 為了避免使用NOT IN ,我們可以把它改寫成外連接(Outer Joins)或NOT EXISTS. No12:使用表連接連替換EXIST通常來說 , 采用表連接的方式比EXISTS更有效率SELECT ENAME FROM EMP E WHERE EXISTS (SELECT ‘X’ FROM DEPT WHERE DEPT_NO = E.DEPT_NO AND DEPT_CAT = ‘A’); (更高效) SELECT ENAME FROM DEPT D,EMP E WHERE E.DEPT_NO = D.DEPT_NO AND DEPT_CAT = ‘A’ ;No13:用索引提高效率1.索引基礎索引是表的一個概念部分,用來提高檢索數據的效率. 實際上,ORACLE使用了一個復雜的自平衡B-tree結構. 通常,通過索引查詢數據比全表掃描要快. 當ORACLE找出執行查詢和Update語句的最佳路徑時, ORACLE優化器將使用索引. 同樣在聯結多個表時使用索引也可以提高效率. 另一個使用索引的好處是,它提供了主鍵(PRimary key)的唯一性驗證.除了那些LONG或LONG RAW數據類型, 你可以索引幾乎所有的列. 通常, 在大型表中使用索引非凡有效. 當然,你也會發現, 在掃描小表時,使用索引同樣能提高效率.雖然使用索引能得到查詢效率的提高,但是我們也必須注重到它的代價. 索引需要空間來存儲,也需要定期維護, 每當有記錄在表中增減或索引列被修改時, 索引本身也會被修改. 這意味著每條記錄的INSERT , DELETE , UPDATE將為此多付出4 , 5 次的磁盤I/O . 因為索引需要額外的存儲空間和處理,那些不必要的索引反而會使查詢反應時間變慢.ORACLE對索引有兩種訪問模式. 索引唯一掃描 ( INDEX UNIQUE SCAN) 大多數情況下, 優化器通過WHERE子句訪問INDEX. 例如:表LODGING有兩個索引 : 建立在LODGING列上的唯一性索引LODGING_PK和建立在MANAGER列上的非唯一性索引LODGING$MANAGER. SELECT * FROM LODGINGWHERE LODGING = ‘ROSE HILL’; 在內部 , 上述SQL將被分成兩步執行, 首先 , LODGING_PK 索引將通過索引唯一掃描的方式被訪問 , 獲得相對應的ROWID, 通過ROWID訪問表的方式 執行下一步檢索. 假如被檢索返回的列包括在INDEX列中,ORACLE將不執行第二步的處理(通過ROWID訪問表). 因為檢索數據保存在索引中, 單單訪問索引就可以完全滿足查詢結果. 下面SQL只需要INDEX UNIQUE SCAN 操作. SELECT LODGING FROM LODGINGWHERE LODGING = ‘ROSE HILL’; 索引范圍查詢(INDEX RANGE SCAN) 適用于兩種情況:1. 基于一個范圍的檢索2. 基于非唯一性索引的檢索 例1: SELECT LODGING FROM LODGINGWHERE LODGING LIKE ‘M%’; WHERE子句條件包括一系列值, ORACLE將通過索引范圍查詢的方式查詢LODGING_PK . 由于索引范圍查詢將返回一組值, 它的效率就要比索引唯一掃描低一些. 例2: SELECT LODGING FROM LODGINGWHERE MANAGER = ‘BILL GATES’; 這個SQL的執行分兩步, LODGING$MANAGER的索引范圍查詢(得到所有符合條件記錄的ROWID) 和下一步同過ROWID訪問表得到LODGING列的值. 由于LODGING$MANAGER是一個非唯一性的索引,數據庫不能對它執行索引唯一掃描. 由于SQL返回LODGING列,而它并不存在于LODGING$MANAGER索引中, 所以在索引范圍查詢后會執行一個通過ROWID訪問表的操作. 2.存在下面情況的SQL,不會用到索引 存在數據類型隱形轉換的,如: select * from staff_member where staff_id=’123’; 列上有數學運算的,如: select * from staff_member where salary*2<10000; 使用不等于(<> )運算的,如: select * from staff_member where dept_no<>2001;記住, 索引只能告訴你什么存在于表中, 而不能告訴你什么不存在于表中. 使用substr字符串函數的,如: select * from staff_member where substr(last_name,1,4)=’FRED’; ‘%’通配符在第一個字符的,如: select * from staff_member where first_name like ‘%DON’; 字符串連接()的,如: select * from staff_member where first_name’’=’DONALD’ 避免在索引中使用任何可以為空的列,ORACLE將無法使用該索引 .對于單列索引,假如列包含空值,索引中將不存在此記錄. 對于復合索引,假如每個列都為空,索引中同樣不存在此記錄. 假如至少有一個列不為空,則記錄存在于索引中. 通常, 我們要避免在索引列上使用NOT, NOT會產生在和在索引列上使用函數相同的影響. 當ORACLE”碰到”NOT,他就會停止使用索引轉而執行全表掃描. 假如一定要對使用函數的列啟用索引, ORACLE新的功能: 基于函數的索引(Function-Based Index) 也許是一個較好的方案. CREATE INDEX EMP_I ON EMP (UPPER(ename)); /*建立基于函數的索引*/ SELECT * FROM emp WHERE UPPER(ename) = ‘BLACKSNAIL’; /*將使用索引*/3.多個索引情況下的選擇當SQL語句的執行路徑可以使用分布在多個表上的多個索引時, ORACLE會同時使用多個索引并在運行時對它們的記錄進行合并, 檢索出僅對全部索引有效的記錄.在ORACLE選擇執行路徑時,唯一性索引的等級高于非唯一性索引. 然而這個規則只有當WHERE子句中索引列和常量比較才有效.假如索引列和其他表的索引類相比較. 這種子句在優化器中的等級是非常低的.假如不同表中兩個相同等級的索引將被引用, FROM子句中表的順序將決定哪個會被率先使用. FROM子句中最后的表的索引將有最高的優先級.假如相同表中兩個想同等級的索引將被引用, WHERE子句中最先被引用的索引將有最高的優先級.舉例: DEPTNO上有一個非唯一性索引,EMP_CAT也有一個非唯一性索引. SELECT ENAME, FROM EMP WHERE DEPT_NO = 20 AND EMP_CAT = ‘A’;這里,DEPTNO索引將被最先檢索,然后同EMP_CAT索引檢索出的記錄進行合并. 執行路徑如下: TABLE access BY ROWID ON EMP AND-EQUAL INDEX RANGE SCAN ON DEPT_IDX INDEX RANGE SCAN ON CAT_IDX當ORACLE無法判定索引的等級高低差別,優化器將只使用一個索引,它就是在WHERE子句中被列在最前面的. 4.自動選擇索引假如表中有兩個以上(包括兩個)索引,其中有一個唯一性索引,而其他是非唯一性.在這種情況下,ORACLE將使用唯一性索引而完全忽略非唯一性索引. 舉例:SELECT ENAMEFROM EMPWHERE EMPNO = 2326 AND DEPTNO = 20 ; 這里,只有EMPNO上的索引是唯一性的,所以EMPNO索引將用來檢索記錄.TABLE ACCESS BY ROWID ON EMP INDEX UNIQUE SCAN ON EMP_NO_IDX5.等式比較和范圍比較 當WHERE子句中有索引列, ORACLE不能合并它們,ORACLE將用范圍比較. 舉例: DEPTNO上有一個非唯一性索引,EMP_CAT也有一個非唯一性索引. SELECT ENAME FROM EMP WHERE DEPTNO > 20 AND EMP_CAT = ‘A’; 這里只有EMP_CAT索引被用到,然后所有的記錄將逐條與DEPTNO條件進行比較. 執行路徑如下: TABLE ACCESS BY ROWID ON EMP INDEX RANGE SCAN ON CAT_IDX 3.組合索引總是使用索引的第一個列假如索引是建立在多個列上, 只有在它的第一個列(leading column)被where子句引用時,優化器才會選擇使用該索引.6.CBO下使用更具選擇性的索引基于成本的優化器(CBO, Cost-Based Optimizer)對索引的選擇性進行判定來決定索引的使用是否能提高效率.假如索引有很高的選擇性, 那就是說對于每個不重復的索引鍵值,只對應數量很少的記錄.比如, 表中共有100條記錄而其中有80個不重復的索引鍵值. 這個索引的選擇性就是80/100 = 0.8 . 選擇性越高, 通過索引鍵值檢索出的記錄就越少. 假如索引的選擇性很低, 檢索數據就需要大量的索引范圍查詢操作和ROWID 訪問表的操作. 也許會比全表掃描的效率更低.下列經驗請參閱:a. 假如檢索數據量超過30%的表中記錄數.使用索引將沒有顯著的效率提高. b. 在特定情況下, 使用索引也許會比全表掃描慢, 但這是同一個數量級上的區別. 而通常情況下,使用索引比全表掃描要塊幾倍乃至幾千倍! 7.用UNION (ALL)替換OR (適用于索引列)通常情況下, 用UNION替換WHERE子句中的OR將會起到較好的效果. 對索引列使用OR將造成全表掃描. 注重, 以上規則只針對多個索引列有效. 假如有column沒有被索引, 查詢效率可能會因為你沒有選擇OR而降低. 在下面的例子中, LOC_ID 和REGION上都建有索引.高效: SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE LOC_ID = 10 UNION SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE REGION = “MELBOURNE” 低效: SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE LOC_ID = 10 OR REGION = “MELBOURNE” 假如你堅持要用OR, 那就需要返回記錄最少的索引列寫在最前面. 注重: WHERE KEY1 = 10 (返回最少記錄)OR KEY2 = 20 (返回最多記錄) ORACLE 內部將以上轉換為WHERE KEY1 = 10 AND((NOT KEY1 = 10) AND KEY2 = 20)8.用>=替代>DEPT>3和DEPT>=4兩者的區別在于, 前者DBMS將直接跳到第一個DEPT等于4的記錄而后者將首先定位到DEPTNO=3的記錄并且向前掃描到第一個DEPT大于3的記錄. No14:用UNION ALL 替換UNION ( 假如有可能的話) 當SQL語句需要UNION兩個查詢結果集合時,這兩個結果集合會以UNION-ALL的方式被合并, 然后在輸出最終結果前進行排序.假如用UNION ALL替代UNION, 這樣排序就不是必要了. 效率就會因此得到提高.No15:使用提示(Hints)對于表的訪問,可以使用兩種Hints.FULL 和 ROWIDFULL hint 告訴ORACLE使用全表掃描的方式訪問指定表.例如: SELECT /*+ FULL(EMP) */ * FROM EMP WHERE EMPNO = 7893; ROWID hint 告訴ORACLE使用TABLE ACCESS BY ROWID的操作訪問表. 通常, 你需要采用TABLE ACCESS BY ROWID的方式非凡是當訪問大表的時候, 使用這種方式, 你需要知道ROIWD的值或者使用索引. 假如一個大表沒有被設定為緩存(CACHED)表而你希望它的數據在查詢結束是仍然停留在SGA中,你就可以使用CACHE hint 來告訴優化器把數據保留在SGA中. 通常CACHE hint 和 FULL hint 一起使用.例如:SELECT /*+ FULL(WORKER) CACHE(WORKER)*/ *FROM WORK; 索引hint 告訴ORACLE使用基于索引的掃描方式. 你不必說明具體的索引名稱例如: SELECT /*+ INDEX(LODGING) */ LODGING FROM LODGING WHERE MANAGER = ‘BILL GATES’; 在不使用hint的情況下, 以上的查詢應該也會使用索引,然而,假如該索引的重復值過多而你的優化器是CBO, 優化器就可能忽略索引. 在這種情況下, 你可以用INDEX hint強制ORACLE使用該索引. ORACLE hints 還包括ALL_ROWS, FIRST_ROWS, RULE,USE_NL, USE_MERGE, USE_HASH 等等. 使用hint , 表示我們對ORACLE優化器缺省的執行路徑不滿足,需要手工修改.這是一個很有技巧性的工作. 我建議只針對特定的,少數的SQL進行hint的優化.對ORACLE的優化器還是要有信心(非凡是CBO)No16:避免使用耗費資源的操作帶有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL語句會啟動SQL引擎執行耗費資源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要執行兩次排序.例如,一個UNION查詢,其中每個查詢都帶有GROUP BY子句, GROUP BY會觸發嵌入排序(NESTED SORT) ; 這樣, 每個查詢需要執行一次排序, 然后在執行UNION時, 又一個唯一排序(SORT UNIQUE)操作被執行而且它只能在前面的嵌入排序結束后才能開始執行. 嵌入的排序的深度會大大影響查詢的效率.通常, 帶有UNION, MINUS , INTERSECT的SQL語句都可以用其他方式重寫. 假如你的數據庫的SORT_AREA_SIZE調配得好, 使用UNION , MINUS, INTERSECT也是可以考慮的, 究竟它們的可讀性很強No17:分離表和索引總是將你的表和索引建立在不同的表空間內(TABLESPACES). 決不要將不屬于ORACLE內部系統的對象存放到SYSTEM表空間里. 同時,確保數據表空間和索引表空間置與不同的硬盤控制卡控制的硬盤上.No18:排序發生的情況 SQL中包含group by 子句 SQL 中包含order by 子句 SQL 中包含 distinct 子句 SQL 中包含 minus 或 union操作 創建索引時這些情況慢。
No19:execute immediate, DBMS_SQL動態SQL中,盡量多用execute immediate,而少用DBMS_SQL,前者綜合效率優于后者No20:用like替換和substr對于‘like’和‘substr’,其效率并沒有多大分別。但是,當所搜索的值不存在時,使用‘like’的速度明顯大于‘substr’。所以:select * from a where substr(a1,1,4) = '5378' 可以用like替代select * from a where a1 like ‘5378%’No20:DML語句優化 1.假如有可能的話truncate 替代delete 2.大表的刪除轉化為對剩余部分建表,truncate原表然后將新建的表改名為原表. 3.Update多列的時候,盡量不要用多個set;如:UPDATE EMP SET (EMP_CAT, SAL_RANGE) = (SELECT MAX(CATEGORY) , MAX(SAL_RANGE) FROM EMP_CATEGORIES) WHERE EMP_DEPT = 0020; 4.假如有索引,刪除索引后執行操作,操作完成后重建索引。 5.環境答應的話使用并行No21:使用并行hint /*+ parallel(tablename,parallel-degree)*/調整并行執行的目的是:最大地發揮硬件的能力。假如你有一個高性能的系統,有高優先的SQL語句在運行,則并行語句就可以使用所有有效的資源。Oracle可以執行的下面的并行: l 并行查詢;l 并行DML(包括 INSERT, UPDATE, DELETE; APPEND提示,并行索引掃描);l 并行 DDL;假如你的系統缺少以下這些特點,則并行可能不會有多大改善。l 對稱多處理器(SMP), 集群或強大的并行系統;l 有效的I/O帶寬;l 低利用的或閑置的CPU(如CPU使用小于30%);l 對附加的內存無效,如分類、哈西索引及I/O緩沖區等。假如指定的并行度大于實際可用的資源(硬件資源>parallel_max_server>你指定的并行度),將會使用最大的可用資源的并行度來處理。假如多人同時使用并行, sum(parallel_degree)>parallel_max_server ;可能產生等待使效率下降。