用智慧型優化限制來提高Oracle效能

2021-04-13 12:54:26 字數 3436 閱讀 1030

oracle sql執行時間的最主要的組成部分是花在為執行準備新的sql語句上的時間。不過,如果了解了可執行計畫產生的內在機制,你就可以控制oracle花費在評估表的連線順序的時間,並在總體上提高查詢的效能。

準備為執行提供的sql語句

在乙個sql語句進入oracle庫的cache之後、而真正被執行之前,將會依次發生如下事件:

語法檢查——檢查該sql語句的拼寫和詞序是否正確。

語**析——oracle根據資料詞典(data dictionary)來驗證所有的**(table)和列(column)。

已儲存綱要檢查——oracle檢查詞典以確認對應該sql語句是否已存在已儲存的綱要(stored outline)。

產生執行計畫——oracle根據一種罰值(cost-based)優化演算法和資料詞典中的統計資料來決定如何生成最優執行計畫。

產生二進位制**——oracle在執行計畫的基礎上生成可執行的二進位制**。

一旦開始準備執行sql語句,上述的過程很快就會執行,這是因為oracle可以識別出同樣的sql語句並對同樣的sql語句重複使用對應的可執行**。然而,對產生ad hoc sql的系統以及sql中嵌入文字值(literal value)的情況,sql執行計畫的生成時間就會變得相當長,而且以前的執行計畫也常常不能被再次利用。對那些牽涉到許多**的查詢,oracle可能要花上很長的時間來決定把連線這些**的順序。

評估連線**的順序

生成可執行計畫的時間往往是sql的準備過程中最大的開銷組成部分,尤其是在處理有多個表的連線的查詢的情況下。當oracle評估表的連線順序時,它必須考慮每一種可能的排序。例如,當有六個**需要連線時,oracle需要考慮720種(6的排列數,即6×5×4×3×2×1=720)可能的連線排序。當需要連線的表的數量超過10時,這個排列問題將變得非常突出:如果需要連線的**有15個,那麼需要考慮的可能的查詢排列順序超過一萬億種(精確值為1,307,674,368,000)。

在optimizer_search_limit引數中設定限制

你可以通過optimizer_search_limit引數來控制上述問題的發生,該引數用來指定優化器評估的**連線順序的最大數目。利用這個引數,就可以防止優化器在評估所有可能的**連線順序中所花費的多餘時間。如果查詢中的表的數量少於或者等於optimizer_search_limit,那麼優化器檢查所有的可能表的連線方式。

例如,涉及了五個表的查詢一共有120種(5!=5×4×3×2×1=120)可能的連線順序,所以如果引數optimizer_search_limit的值設定為5(預設值),那麼優化器就會考慮所有的這120種可能的連線順序。optimizer_search_limit引數還用來控制啟動開始連線指示(star join hint)的閾值。當查詢所涉及的**數量少於引數optimizer_search_limit的設定值,開始連線指示將被設定。

另乙個工具:optimizer_max_permutations引數

optimizer_max_permutations初始引數用來設定優化器優化範圍的上界(即最多考慮多少種**連線順序),它依賴於初始引數optimizer_search_limit。引數optimizer_max_permutations的預設值為8000。

引數optimizer_search_limit 和optimizer_max_permutations一同用來設定優化器所考慮的排列數的上限。優化器不斷的產生可能的表的連線的排列,直到排列數達到引數optimizer_search_limit或者optimizer_max_permutations為止。一旦優化器停止產生新的可能連線排列,它將會從中選擇出耗費最小的排列。

用已排序指示來指定一種連線排序

你可以設定優化器評估的排列數的上限。但是對複雜的情況下,即使允許的排列數很大,優化器也很可能在遠遠沒有找到乙個比較合適的排列之間就已經停止優化了。你不妨回頭看看我前面舉的那個例子(15個需要連線的表有超過一萬億種排列)。如果設定優化器考慮80,000種排列,那麼這僅僅考慮了所有可能性的0.000006%,優化器極可能沒有達到最佳的排列。

在oracle sql中解決這個問題的最好方法就是手工指定一種**連線順序。這裡需要遵循的大原則就是**連線順序應該使得查詢計畫盡快得以建立,通常在sql語句中使用where限制子句。

下面以乙個對名為emp的**的並行查詢為例,例子中的**強制查詢計畫執行乙個巢狀迴圈連線(nested loop join)。注意,我使用了已排序指示來引導優化器來評估where子句中給出的**的連線順序。

select /*+ ordered use_nl(bonus) parallel(e, 4) */

e.ename,

hiredate,

b.comm.

from

emp e,

bonus b

where

e.ename = b.ename

;上面的例子要求優化器按照sql語句中from子句指定的順序連線**,from子句中第乙個的**指定為驅動**(driving table)。已排序指示常常與其它指示聯合使用以確保多個**按照適當的順序連線起來。在遇到涉及四個以上**的資料倉儲查詢時常常也是這樣處理。

下面另給出乙個例子,在這個例子中,我們使用乙個已排序指示(ordered hint)來把**按照乙個特定的順序(先是emp,然後是dep和sal,最後是bonus)連線起來。進一步改進執行計畫,我指定emp**到dept**的連線使用hash連線,sal**到bonus**使用巢狀迴圈連線。

select /*+ ordered use_hash (emp, dept) use_nl (sal, bonus) */

from

emp,

dept,

sal,

bonus

where . . .

對實際應用的建議

在實際應用場合下,減小optimizer_max_permutations引數並使用已儲存的優化計畫或者已儲存綱要(這樣在查詢涉及到許多**時,就可以避免重新解析查詢所花費的實際)會更有效率。一旦找到最好的**連線順序,你可以手工指定**的連線順序(通過已排序指示)並儲存綱要,這樣就永久儲存該**連線順序。

當執行乙個新的查詢時,你可以首先把optimizer_search_limit設定為該查詢所涉及的**數,這樣優化器將從所有的連線順序中找出最佳的那種。以後執行該查詢時,你就可以在where子句中按照最佳連線順序排列**名稱,並設定已儲存指示和已儲存綱要,這樣就可以按照最佳順序連線**而無需重複評估各種可能排序。這樣查詢的速度將會得到顯著的提高。

已排序指示的優先順序高於optimizer_search_limit和 optimizer_max_permutations引數。如果設定了已排序指示,那麼**就會按照查詢命令中的from子句給出的順序連線,這樣這個過程就沒有優化器優化**的連線順序這一步驟了。

作為oracle的專業人士,你應該知道sql語句進入庫cache中有乙個明顯的起始延時。但是聰明的oracle資料庫管理員以及oracle開發者能改變**的搜尋限制引數或者利用已排序指示來手工指定**的連線順序,這樣可以極大的降低優化以及執行新查詢所花費的時間。

用GetString來提高ASP的速度

許多asp程式設計師都有過執行資料庫查詢,然後將查詢結果用html 的形式顯示出來的經歷吧。通常我們是這麼做的 以下為引用的內容 create connection recordset populate data into recordset object do while not rs.eof r...

用GetString來提高ASP的速度

許多asp程式設計師都有過執行資料庫查詢,然後將查詢結果用html 的形式顯示出來的經 曆吧.通常我們是這麼做的 create connection recordset populate data into recordset object do while not rs.eof rs field1...

用異常處理來提高程式效率

one 乙個字典,包含姓名,年齡,職業 不確定有沒有 要求 輸出資訊,有職業就連職業一起輸出,沒有就不輸出 用if else很簡單就處理了,但是用try except效率會高一點。def out info one print name s one name print age s one age i...