穩定(固定)執行計划


sql執行計划為什么會變?

為什么我們的SQL語句執行計划會改變?如何才能穩定SQL語句的執行計划?要想回答上面的2個問題,我們就要首先知道SQL語句的執行計划是如何產生的,有那些因素影響執行計划的生成,只有了解了這些因素我們才能對症下葯,穩定我們的SQL語句執行計划。

我們知道,一條SQL語句他的執行計划可能不止一個,數據庫是 如何確定采用那條執行計划的呢?這是由數據庫的優化器所決定,它的作用就是在所有可能的執行計划當中選擇一個最‘優’的執行計划。目前的數據庫的優化器有 2種:基於規則的RBO優化器和基於成本的CBO優化器。RBO優化器是利用一套內部規則決定SQL語句的執行計划,而不管這個執行計划是否是最佳選擇; 而CBO優化器則是通過一套嚴密的換算機制把該SQL每個執行計划的執行成本計算出來,然后選擇成本最‘小’的那個作為該SQL語句的執行計划。從描述中 可以看出,在理想環境中采用CBO優化器決定SQL語句的執行計划更為科學。

目前我們的用戶普遍采用Oracle 10g的數據庫,Oracle 10g默認的優化器CBO即為基於成本的優化器,但是正是由於CBO的這種選擇機制,由於種種原因,可能導致CBO計算出的最‘小’成本執行計划出現變 更,從而導致該SQL的執行計划出現變更。而影響CBO計算成本的因素有io、cpu、網絡、統計信息、初始化參數等,如果這些環境發生了變化,就會導致 CBO在計算SQL語句的執行計划成本值發生改變,從而最終可能導致執行計划的選擇發生改變。

如何讓SQL語句的執行計划穩定不變-存儲概要介紹(轉) - 666 - 666

除了由於優化器的原因可能導致SQL語句執行計划改變外,如果數據結構改變,也可能導致執行計划的變化,最常見的就是如索引的創建或失效、普通表變為分區表等,這些數據結構的改變同樣可能會導致SQL執行計划的改變。

常見穩定SQL執行計划的方法

通過前面的介紹我們了解了SQL語句執行計划變化的原因,為了使SQL語句的執行計划固定,可以采用下面的幾種方式來穩定SQL語句的執行計划,不過雖然它們都能穩定SQL語句的執行計划,但是每種方式都有一定的缺陷,要根據實際情況進行甄酌。

1. 選用規則(RBO)優化器

通過前面的介紹我們知道,RBO下的SQL語句的執行計划是通過一定的規則生成的,也就是說一旦SQL語句確定,那么它的執行計划也就確認了,只要數據結構不變,它的執行計划就始終不會改變。

缺陷:同樣正是由於RBO下SQL語句的執行計划是基於規則生成,首先就要求SQL編寫人員有一定的性能優化知 識,編寫時就充分考慮RBO的規則,否則可能導致執行計划效率不高,這對程序編寫人員的能力要求就比較高,而我們很多程序人員不具備這方面的能力;再次 Oracle公司明確指出不再對RBO進行維護,意味這以后可能逐漸放棄這種優化器而用CBO取代,例如10g以后默認的優化器都是CBO模式,為了產品 的持續發展,也迫使我們選用更合理的CBO模式。

2. 加提示字/*+hints */

雖然數據庫的優化器選擇了CBO模式,但是我們可以對單獨的SQL 語句采用加提示字/*+rule*/的方式指定該語句采用RBO模式,這樣即不違背數據庫整體使用CBO的模式,又對指定SQL使用了RBO,穩定了這些 SQL語句的執行計划,這也是目前我們程序員常用的一種穩定SQL語句執行計划的方式。

缺陷:目前我們程序中大量使用了/*+rule*/的方式穩定指定SQL語句的執行計划,這種方式雖然簡單有 效,但是同樣暴露出很多問題。首先就是同樣由於書寫SQL不規范的原因導致RBO下生成可能不是最高效的執行計划,在用戶處經常出現性能問題,而這時 SQL調整由於有提示字,必須通過程序人員修改程序調整,處理問題的周期變長;另外后期ZLHIS可能采用表分區技術,Oracle有個規則就是,一旦 SQL語句中有分區表,加提示字采用RBO的方式將失效,這勢必帶來性能隱患。

3. 統計信息鎖定

前面我們說了,在CBO環境下,影響執行計划的因素有I/O、CPU、數據庫參數和統計信息,其中在我們用戶環境中,最可能改變的是統計信息,而其 他因素一般情況下一旦服務器部署完成,不會輕易更改。既然統計信息的改變是執行計划不穩定的主要因素,那么我們就想辦法把統計信息鎖定。Oracle提供 了dbms_stats包來把統計信息進行鎖定,這樣統計信息就不會隨實際數據增長而改變,而被鎖定在一個指定的范圍內,如下圖,我們把住院費用記錄表和 其索引的統計信息鎖定

sql>exec dbms_stats.lock_table_stats(ownname => 'ZLHIS',tabname => '住院費用記錄')

一旦統計信息鎖定,這時我們對其用dbms_stats,analyze收集統計信息將不起作用,如果要對其進行收集,必須通過下面的語句解鎖才有效。

sql>exec dbms_stats.unlock_table_stats(ownname => 'ZLHIS',tabname => '住院費用記錄')

缺陷:鎖定統計信息的缺陷也顯而易見,鎖定后的統計信息無法反映數據表的真實情況,這樣計算出來的執行計划的成本肯定也是不真實的,而如果我們想要真實的統計信息,就必須首先解鎖,然后還要之前鎖定對象的統計信息重新收集一遍,這勢必耗費大量資源。

4. 采用存儲概要

存儲概要(stored outlines)是Oracle8.1開始出現的一個穩定SQL語句執行計划的特性,在Oracle10g得到了進一步完善和加強,接着我們通過單獨一個章節對其進行詳細的介紹。

存儲概要

1. 什么是存儲概要

Oracle 8i開始提供了一個捕獲SQL語句執行計划並保存為存儲概要的機制,一個存儲概要實際上是一條由DBA或開發者優化過的存儲在‘概要’方案中的SQL語 句,當一條SQL語句被執行且它與存儲概要完全匹配,Oracle就使用存儲概要替換使用的SQL語句,從而使該SQL語句不再進行解析,而直接使用保存 在存儲概要中的信息,其中就包括執行計划。而存儲概要非常靈活,它即可以分階段執行也可以限制到僅那些匹配的會話才能執行。

2. 存儲概要的原理

其實通過前面的定義,我們已經大概知道了存儲概要的工作原理,接着我們通過一個流程圖更加直觀的了解下存儲概要是如何起到穩定執行計划的。

如何讓SQL語句的執行計划穩定不變-存儲概要介紹(轉) - 666 - 666

通過上面的流程圖我們可以看到,如果部署了存儲概要,SQL語句在執行前首先去匹配存儲概要,校驗存儲概要中是否有與執行SQL匹配的語句,如果有 就提取存儲概要中保存的執行計划直接執行SQL,如果沒有再按正常流程生成執行計划,再執行SQL。正是由於存儲概要中保存了SQL語句的執行計划,而每 次執行其中的SQL語句時,不再生成而直接提取保存的執行計划,因此就起到了穩定SQL執行計划的作用。

3. 存儲概要的部署

講了這么多存儲概要,那么存儲概要如何創建和部署呢?接下來我們就通過一個案例進行講解,我們首選知道,存儲概要保存了SQL語句的執行計划,而執 行計划是通過解析得到,因此存儲概要必須要SQL語句被解析的時候才能被創建與應用,如果想創建存儲概要的SQL已經被緩存了,我將會發現這種SQL無法 使用我們新創建的存儲概要, 我們使用存儲概要可以穩固數據庫的所有SQL的執行計划,也可以穩固某些指定的SQL語句,這就需要根據不同的需要具體考慮,我們分別進行演示

3.1. 數據庫所有SQL語句創建存儲概要

要穩定數據庫所有的S QL語句,我們只需要修改數據庫的CREATE_STORED_OUTLINES參數,如下:

sql> alter system set CREATE_STORED_OUTLINES = true;

sql> alter system set CREATE_STORED_OUTLINES = zlhis_outline;

存儲概要是分category(類目)的,當CREATE_STORED_OUTLINES = true時,Oracle使用默認的“default”類目,當CREATE_STORED_OUTLINES = zlhis_outline時,Oracle將使用我們自定義的“zlhis_outline”類目;當我們設置了 CREATE_STORED_OUTLINES參數時,Oracle將自動為了后續所有編譯的SQL創建outline到 CREATE_STORED_OUTLINES指定的類目下,當我們把CREATE_STORED_OUTLINES設置為false時,Oracle就 停止創建outline,這種機制是為了對SQL進行分類管理,這里我們就不再敘述,通過上面的設置,只要數據庫的SQL進行了解析,就會創建該SQL對 應的存儲概要,但是要想讓SQL語句下次執行的時候使用存儲概要,還需要執行下面的操作。

sql> alter system set USE_STORED_OUTLINES =true;

sql> alter system set USE_STORED_OUTLINES =zlhis_outline;

如何查看存儲概要是否生效呢?我們可以通過查詢USER_OUTLINES視圖檢閱,語句如下:

如何讓SQL語句的執行計划穩定不變-存儲概要介紹(轉) - 666 - 666

NAME是存儲概要的名稱,它命名規則是由數據庫自己定義,SQL_TEXT是SQL語句的腳本,也是存儲概要匹配的依據,USED表示這個存儲概 要是否使用,UNUNSED表示存儲概要創建后還沒有使用,只有等其對應的SQL再次執行匹配了該存儲概要,狀態就會變成USED。

3.2. 指定SQL創建存儲概要

前面對整個數據庫所有SQL語句創建存儲概方法比較簡單,但是大多數時候我們並不需要這樣做,這樣既浪費資源,又不利用管理。我們只需要對指定的某 些SQL語句創建存儲概要就可以了,這里我們就要使用數據庫自帶的dbms_ outln包,通過dbms_ outln.create_outline根據已有的執行正常的SQL游標來創建存儲概要,我們來具體操作下

假如我們想創建指定SQL語句” Select * From 部門表 Where Id=1584”的存儲概要,我們首先需要在SQL游標中找到該語句的hash_value、child_number值,可以通過v$sql視圖進行查找(注意:該視圖中存在上述SQL語句的前提是該SQL已經執行過,如果SQL語句從未執行,則視圖中無法找到該SQL語句的游標),查找語句如下

如何讓SQL語句的執行計划穩定不變-存儲概要介紹(轉) - 666 - 666

找到對應SQL語句的hash_value、child_number值后,接着把這2個值帶入dbms_ outln.create_outline包,創建對應的存儲概要,如下:

sql>exec dbms_ outln.create_outline(hash_value => '3345575829',child_number => 0,category => '測試_存儲概要');

接着同樣要執行命令啟用該存儲概要,如下

sql>alter system set USE_STORED_OUTLINES =測試_存儲概要;

最后查詢USER_OUTLINES視圖,檢閱剛才創建的存儲概要是否生效,如下

如何讓SQL語句的執行計划穩定不變-存儲概要介紹(轉) - 666 - 666

4. 測試存儲概要效果

講了這么多存儲概要,也大概了解了存儲概要的創建方法,那么存儲概要是否真如其描述的能夠穩定SQL語句的執行計划呢?下面我們就來做一個簡單的實驗,看看存儲概要在穩定SQL語句執行計划所起的作用。

實驗思路:我們編寫一段SQL語句,並且確認這段SQL語句在CBO和RBO條件下執行計划是不同的,然后我們在CBO環境下創建該SQL的存儲概要,並使其生效,最后再次比較CBO和RBO環境下的執行計划,看執行計划有什么改變。

1. 我們看下面的相同的SQL語句在CBO和RBO下執行計划存在差異。

如何讓SQL語句的執行計划穩定不變-存儲概要介紹(轉) - 666 - 666

2. 接着我們用前面介紹的方式,創建該SQL語句的概要文件,並使其生效,下面是創建存儲概要的詳細步驟。

如何讓SQL語句的執行計划穩定不變-存儲概要介紹(轉) - 666 - 666

3. 接着我們重復第一步的操作,查看CBO和RBO下該SQL語句的執行計划,得到的結果如下:

如何讓SQL語句的執行計划穩定不變-存儲概要介紹(轉) - 666 - 666

可以清楚的看到,現在無論是在CBO還是RBO下,該SQL語句的執行計划都是一樣的,事實證明存儲概要在穩定SQL執行計划上起了作用,達到我們想要的效果。

5. 存在的缺陷

同其他穩定SQL語句執行計划的方式一樣,存儲概要同樣存在這一定的缺陷。

他是通過SQL文本進行匹配,一旦SQL語句改變就必須生成新的對應的存儲概要,因此管理維護比較麻煩;

存儲概本身有單獨額外的開銷,新解析的SQL語句Oracle都將檢查是否存在一個相關的存儲概要這是一筆不小的代價,另外這些信息的表是創建保存 在system表空間中的,對於一個生產系統來講當你開始創建存儲概要的時候你將發現其在system表空間中使用了大量的空間。

不過Oracle從8i開始,一直對存儲概要進行着不斷的優化改進,功能也越來越完善,例如除了穩定執行計划,還有添加存儲概要hints的功能等而且存儲概要還有個最大的特點就是可以進行遷移,相信只要經過充分的驗證,其在中聯產品中的應用還是大有可為的。

總結

通過本文的介紹,我們應該掌握以下幾點知識:

1.了解影響SQL語句執行計划的因素。

2.常見穩定SQL語句執行計划的方法以及他們的優缺點。

3.對存儲概要有了一定的認識和了解,知道它是如何穩定SQL語句執行計划的,並且能夠進行簡單的部署。

最后本文對存儲概要只是作了粗略的介紹,感興趣的同事可以下來對其進行更深入的研究,相信一定能有可喜的收獲。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM