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需要考慮
種(
的排列數
即
×
×
×
×
×
=
)可能的連接排序
當需要連接的表的數量超過
時
這個排列問題將變得非常突出
如果需要連接的表格有
個
那麼需要考慮的可能的查詢排列順序超過一萬億種(精確值為
)
在optimizer_search_limit參數中設置限制
你可以通過optimizer_search_limit參數來控制上述問題的發生
該參數用來指定優化器評估的表格連接順序的最大數目
利用這個參數
就可以防止優化器在評估所有可能的表格連接順序中所花費的多余時間
如果查詢中的表的數量少於或者等於optimizer_search_limit
那麼優化器檢查所有的可能表的連接方式
例如
涉及了五個表的查詢一共有
種(
!=
×
×
×
×
=
)可能的連接順序
所以如果參數optimizer_search_limit的值設置為
(默認值)
那麼優化器就會考慮所有的這
種可能的連接順序
optimizer_search_limit參數還用來控制啟動開始連接指示(star join hint)的阈值
當查詢所涉及的表格數量少於參數optimizer_search_limit的設定值
開始連接指示將被設置
另一個工具
optimizer_max_permutations參數
optimizer_max_permutations初始參數用來設定優化器優化范圍的上界(即最多考慮多少種表格連接順序)
它依賴於初始參數optimizer_search_limit
參數optimizer_max_permutations的默認值為
參數optimizer_search_limit 和optimizer_max_permutations一同用來設置優化器所考慮的排列數的上限
優化器不斷的產生可能的表的連接的排列
直到排列數達到參數optimizer_search_limit或者optimizer_max_permutations為止
一旦優化器停止產生新的可能連接排列
它將會從中選擇出耗費最小的排列
用已排序指示來指定一種連接排序
你可以設定優化器評估的排列數的上限
但是對復雜的情況下
即使允許的排列數很大
優化器也很可能在遠遠沒有找到一個比較合適的排列之間就已經停止優化了
你不妨回頭看看我前面舉的那個例子(
個需要連接的表有超過一萬億種排列)
如果設定優化器考慮
種排列
那麼這僅僅考慮了所有可能性的
%
優化器極可能沒有達到最佳的排列
在Oracle SQL中解決這個問題的最好方法就是手工指定一種表格連接順序
這裡需要遵循的大原則就是表格連接順序應該使得查詢計劃盡快得以建立
通常在SQL語句中使用WHERE限制子句
下面以一個對名為emp的表格的並行查詢為例
例子中的代碼強制查詢計劃執行一個嵌套循環連接(nested loop join)
注意
我使用了已排序指示來引導優化器來評估WHERE子句中給出的表格的連接順序
select /*+ ordered use_nl(bonus) parallel(e
) */
e
ename
hiredate
m
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開發者能改變表格的搜索限制參數或者利用已排序指示來手工指定表格的連接順序
這樣可以極大的降低優化以及執行新查詢所花費的時間
From:http://tw.wingwit.com/Article/program/Oracle/201311/17427.html