oracle性能優化

【雲和恩墨,提供7*24最專業的數據恢復(Oracle,MySQL,SQL server)服務,致力於爲您的數據庫系統作最後一道安全防禦!服務熱線:010-59007017-7030】數據恢復|數據庫運維|性能優化|安全保障|Oracle培訓|MySQL培訓html

 

主題介紹:程序員

Oracle執行計劃的另類解讀:調皮的執行計劃 | 誠實的執行計劃 | 樸實的執行計劃web

 

說到執行計劃,oracle的擁躉們天然而然會興奮起來。在ORACLE的世界裏,執行計劃有着其特殊的地位,若是咱們將SQL性能優化當作一個生物,那某種程度上,執行計劃就是DNA。在某搜索網站中,「oracle 執行計劃」關鍵字的搜索結果與「oracle」關鍵字的搜索結果佔比爲1.7%。足可見執行計劃在ORACLE中舉足輕重的地位:數據庫

image.png

而當咱們輸入「oracle執行計劃」時,推薦關鍵字排第一的就是「ORACLE執行計劃怎麼看」安全

image.png

一個標準的執行計劃大體能夠分爲三個部分:訪問方式(表訪問、索引訪問等)、鏈接方式(NESTED LOOP、HASH JOIN等)及訪問順序(驅動表等)性能優化

image.png

咱們對上述SQL稍加改動,再看執行計劃:oracle

image.png

什麼狀況?DEPT表不見了,執行計劃竟然「殘缺」了:運維

一、這是ORACLE的BUG嗎?函數

二、少了一張表,結果正確嗎?工具

三、ORACLE優化器如此大膽,其背後是誰在給他撐腰?

四、ORACLE憑什麼擅做主張?

 

爲了回答上述問題,咱們就進入今天的第一個主題:殘缺的執行計劃。

 

 

殘缺的執行計劃

 

在展開以前,咱們先作數據準備,分別建立兩張表EMP及DEPT,腳本以下:

 

CREATE TABLE DEPT(

        DEPTNO NUMBER(2), 

        DNAME VARCHAR2(14), 

        LOC VARCHAR2(13)); 

 

CREATE TABLE EMP(

        EMPNO NUMBER(4)CONSTRAINT PK_EMP PRIMARYKEY, 

        ENAME VARCHAR2(10), 

        JOB VARCHAR2(9), 

        MGR NUMBER(4), 

        HIREDATE DATE, 

        SAL NUMBER(7,2), 

        COMM NUMBER(7,2), 

        DEPTNO NUMBER(2) ); 

 

如今咱們有以下一條語句:

SELECT COUNT(1)

  FROM EMP E

  LEFTJOIN DEPT D

    ON E.DEPTNO = D.DEPTNO

 

這條語句很是簡單,就是獲取EMP表與DEPT表內關聯後的數據量。在看具體的執行計劃以前,咱們解讀下在常規狀況下,DB是如何處理這樣的數據的

一、分別讀取emp表和DEPT表的數據;

二、對EMP中的DEPTNO與DEPT表中的DEPTNO進行內關聯;

三、對內關聯後的數據進行彙總計算;

四、返回彙總計算結果。

也就是說會存在EMP與DEPT表的內關聯,由於SQL就是這樣寫的。那咱們看下該語句的執行計劃,以下:

image.png

ORACLE優化器果然是按照咱們的預想制定了執行計劃。

 

 

 

1惟一性字段對執行計劃的影響

 

因爲在模型分析時,咱們發現DEPT表的DEPTNO字段是惟一的。因而咱們須要經過以下語句爲該字段建立主鍵:

ALTERTABLE DEPT ADDCONSTRAINT PK_DEPT PRIMARYKEY(DEPTNO);

 

咱們再回過頭來看執行計劃,會發生變化嗎?

image.png

若是此時的你還不能看出問題,那麼咱們就對比下DEPT表的主鍵建立前後執行計劃的變化:

image.png

俗話說:不比不知道,一比嚇一跳。DEPT莫名其妙的被ORACLE優化器弄「丟」了。這不由讓人懷疑:這樣的裁剪是不是不負責任的?也就是說,裁剪後的結果是否會由於裁剪而發生變化?在深刻了解到LEFT JOIN的原理及模型結構後,你就會明白爲什麼ORACLE優化器在DEPT表建立了基於DEPTNO字段的主鍵後,會作這樣的裁剪。

 

支持ORACLE作如此大膽裁剪的理由是:

一、 LEFTJOIN在沒有where條件過濾的時候,是不會減小結果數據量的;

二、 若是被關聯的字段是被關聯表的主鍵(或者惟一性字段),那麼是不會使結果數據量增多的。

 

既然結果集的數據量不增長也不減小,那爲什麼還要多訪問一個表,多作一次關聯呢?這就是ORACLE的精明之處:簡單的就是高效的。

 

接下來,咱們繼續上面的實驗(固然是基於上面的模型基礎,即在DEPT表上建立了基於DEPTNO字段的主鍵)。此次,咱們將LEFT JOIN改爲INNER JOIN,看看執行計劃是怎麼樣的:

image.png

表結構和約束關係沒有發生變化,消失的DEPT又回來了。

神馬緣由呢?LEFT JOIN是不會有數據過濾的做用的,可是INNER JOIN則有過濾的功用。

爲了驗證,咱們準備以下數據:

INSERTINTO dept(deptno,dname)VALUES(14,'財務');

INSERTINTO dept(deptno,dname)VALUES(31,'行政');

INSERTINTO EMP(EMPNO, ENAME, DEPTNO)VALUES('001','張三',14);

INSERTINTO EMP(EMPNO, ENAME, DEPTNO)VALUES('002','李四',31);

INSERTINTO EMP(EMPNO, ENAME, DEPTNO)VALUES('003','王五',21);

INSERTINTO EMP(EMPNO, ENAME, DEPTNO)VALUES('004','麻六',14);

如今來看看LEFT JOIN和INNER JOIN的不一樣結果:

image.png

image.png

也就是說,LEFT JOIN和INNER JOIN仍是有差別的,那麼在什麼狀況下才能在執行計劃中將DEPT「槍斃」掉呢?

 

 

2主外鍵約束對執行計劃的影響

 

咱們對EMP和DEPT建立一個主外鍵約束(在建立主外鍵約束前,我須要刪除掉empno=’003’的記錄):

ALTERTABLE EMP ADDCONSTRAINT FK_DEPTNO FOREIGNKEY(DEPTNO)REFERENCES DEPT(DEPTNO);

 

看看效果如何:

image.png

這樣是否是已經很是明確了DEPT再度消失的緣由了?由於建立了主外鍵,也就是等於說EMP全部的DEPTNO必需要存在DEPT表中,既然有這樣的約束,那天然就不須要畫蛇添足的關聯DEPT表了。

 

 

3字段屬性對執行計劃的影響

 

如今咱們往EMP表裏面再添加一條數據:

INSERTINTO EMP (EMPNO, ENAME, DEPTNO)VALUES('005','趙七',NULL);

再看看INNER JOIN的結果:

image.png

結果只有3條數據,明顯剛纔新增的數據是被過濾掉了,由於他的DEPTNO爲null,其null並無存在於dept表中。

而在執行計劃裏面,是沒有DEPT表的:

image.png

也就是說該SQL就應該等價於以下SQL:

SELECT E.EMPNO, E.ENAME

 FROM EMP E

咱們再看結果:

image.png

 

不對呀,說好的等價呢?難道是執行計劃出了問題?仍是咱們對執行計劃的理解錯了?也或許是執行計劃對咱們隱藏了什麼?

以上,咱們都是在ORACLE的第三方開發工具PL SQL DEVELOPER裏面查看的執行計劃。如今咱們換種方式,在SQL PLUS裏面經過explain plan這種最原始的方式來查看執行計劃,以下:

image.png

原來,在這個執行計劃的內容中,明顯的多出了「Predicate Information」,而在這部分信息裏面,filter是:E.DEPTNO IS NOT NULL。

 

好吧,咱們先把這個謂詞放進SQL中,看看效果:

image.png

果真,咱們發現,增長了這個謂詞後,兩個SQL又等價了。此時,咱們會想:天哪,若是再遇到其餘場景,會不會又不等價了?

 

在關聯條件存在主外鍵關係約束的前提下,以下兩個SQL是等價的:

image.png

無論你信不信,反正我信了

 

而此時,咱們來看看EMP.DEPTNO的字段屬性:

image.png

咱們發現其Nullable屬性爲true,便可爲null值。而若是咱們將該屬性值修改成false呢?

DELETEFROM emp WHERE empno ='005';

COMMIT;

ALTERTABLE EMP MODIFY DEPTNO NOTNULL;

再看執行計劃:

image.png

咱們發現原來的「Predicate Information」不見了,也就沒有了E.DEPTNO IS NOT NULL的謂詞約束。

 

 

4程序員與ORACLE的較量

 

在上面,咱們在極力「宣傳」着oracle是多麼多麼的智能化,而事實上,她的智能程度也是存在侷限性的,好比她對SQL語句的取捨絕對的依賴於物理模型結構及約束,而一旦這種物理模型結構及約束不存在,那麼ORACLE這位「巧婦」顯然也只能「難爲無米之炊」了。即使咱們在SQL中進行了(惟一性)約束,ORACLE也會選擇視而不見,好比以下SQL:

SELECTCOUNT(1)

FROM EMP E

LEFT JOIN (SELECTDISTINCT DEPTNO FROM DEPT) D

ON E.DEPTNO =D.DEPTNO;

 

按照咱們在上面的理解,因爲在子查詢D中,已經對DEPTNO進行了distinct處理,也就意味着在子查詢D中,DEPTNO絕對是惟一性的,即子查詢D對整個SQL返回的結果是沒有任何影響的,該SQL徹底等價於以下SQL:

SELECT COUNT(1) FROM EMP E

 

而事實上呢,咱們看看ORACLE的執行計劃:

image.png

 

這一次很讓咱們意外,ORACLE竟然沒有識別出子查詢D的做用。由此看來,在某些時候,尤爲是在錯綜複雜的業務邏輯面前,oracle每每束手無措,遠沒有程序員聰明,因此在ORACLE這位巨無霸面前,咱們也大可沒必要妄自菲薄。

 

 

5總結

 

 

至此,咱們能夠爲第一個主題作出以下總結:

一、ORACLE優化器爲達性能之目的,會不擇手段的簡化Operation;

二、ORACLE優化器的手段之一就是充分利用數據庫約束,包括但不侷限於:惟一性約束、主外鍵參照性約束、Nullable約束;

三、在約束條件內,ORACLE會簡化SQL,在Operation時再也不重複約束;

四、所以,在平常模型設計時,應儘量的創建約束,最大程度上減小重複約束帶來的「非戰鬥性減員」,從而提高SQL性能

 

 

完整的執行計劃

 

在上一節的最後示例中,爲了更全面闡述問題,咱們「拋棄」了在PL SQL DEVELOPER經過F5獲得的執行計劃,轉而選擇了最原始最古老的explain。由於咱們發現:

image.png

這幾列還不足夠支撐咱們瞭解ORACLE優化器的意圖,或者說還不夠讓咱們拼湊出ORACLE優化器對SQL改寫後的全貌。至少咱們還須要謂詞(Predicate)。因此,一個完整的執行計劃除了:訪問方式(表訪問、索引訪問等)、鏈接方式(NESTED LOOP、HASH JOIN等)及訪問順序外,還應包括謂詞(Predicate),經過結合謂詞,咱們更能還原ORACLE優化器對SQL作了哪些改動?

 

爲了直觀期間,咱們仍是繼續在PL SQL DEVELOPER中演示,只是執行計劃的正確打開方式是這樣的:

image.png

那麼咱們能從謂詞中發現什麼呢?

 

咱們都知道,在表的統計信息採集及時的場景下,若是某個索引字段存在條件過濾,而執行計劃中沒有經過索引訪問,而是table access full。那麼緣由無非就是:該過濾條件值的數據量太大(好比超過全表數據量的20%),或者是SQL的寫法不當(該字段上應用了函數、表達式等)。

 

其實,除了上述兩種場景外,還有一種場景也會致使table access full。咱們先來看一個很是簡單的案例,咱們在EMP.DEPTNO上建立一個索引,由於常常會遇到查詢某個特定部門的員工信息。

CREATEINDEX EMP_I1 ON EMP(DEPTNO);

由於在DEPT表中,DEPTNO的數據類型爲NUMBER(2),在查deptno爲14的員工信息時,咱們會習慣性的寫成:

SELECT*FROM emp WHERE deptno =14;

 

咱們的如意算盤是經過索引EMP_I1來訪問EMP表。而事實上,從執行計劃看,倒是table access full的訪問方式:

image.png

 

儘管deptno=14的數據量爲0,而且也沒有在deptno上有任何的函數或者表達式。那麼問題出在哪裏呢?

 

我再來看看謂詞:

image.png

很明顯,在實際的執行過程當中,DEPTNO是被TO_NUMBER函數包了一層,天然就走不了索引。那麼是什麼讓ORACLE如此「昏庸」,以至「無事生非」的添加一個函數呢?

 

咱們再看看EMP.DEPTNO的數據類型:

image.png

原來,EMP.DEPTNO的數據類型並無同DEPT.DEPTNO保持一致,被設計成了VARCHAR2。所以要想走索引,就有三種辦法:將DEPTNO的數據類型修改成NUMBER(2)、建立TO_NUMBER(DEPTNO)的函數索引、將過濾條件有以前的DEPTNO=14修改爲DEPTNO=’14’。

 

咱們就看下第三種方案的執行計劃:

image.png

這纔是咱們想要的執行計劃,卻不是咱們想要的表模型。這三種方案孰優孰劣不在本次分享主題範圍內,若有機會,再行討論。

 

沒錯,這就是隱式強行轉換的風險,而全部的字段隱式轉換在執行計劃中都會被「曝光」

隱式轉換都是「無心爲之」,有兩種場景:其一是對過濾字段的數據類型「想固然」的認爲;其二是對過濾值類型的錯誤判斷。剛纔的案例屬於第一種,那麼第二種又是怎麼回事呢?

 

如下是一個真實的案例:

系統中存在一個日誌表,數據量很是大,咱們對日誌表按照日誌時間(log_date)作了分區。在頁面,要求強制按照log_date過濾,以命中分區而提升效率。可是分區+強制過濾並無收到預期的性能效果,可是將一樣的查詢條件直接在DB中執行卻很是快。經過對比執行計劃發現,經過頁面調用執行時,並無命中分區,而在訪問謂詞中,log_date字段過濾時,多出了函數INTERNAL_FUNCTION。也就是將log_date字段隱式強制轉換成了timestamp。而致使這種問題的緣由是JAVA數據類型與ORACLE數據類型之間的轉換出現了問題。最後經過JAVA傳STRING到ORACLE,而後在SQL中將變量值TO_DATE成DATE類型解決。

 

咱們也能夠簡單模擬下。好比咱們在EMP中建立基於HIREDATE的索引:

CREATEINDEX EMP_I2 ON EMP(HIREDATE);

咱們如今要查找今年以來入職的員工信息,SQL以下:

SELECT*FROM EMP WHERE HIREDATE >SYSDATE

其執行計劃以下:

image.png

而若是咱們將date’2016-01-01’轉換成timestamp,則執行計劃以下:

 

在這個案例中,若是不查看謂詞,就很難找到性能的根源。

 

 

樸實的執行計劃

 

咱們繼續執行計劃中的「謂詞」,看看還能給咱們哪些意外之喜?

在上個章節中,咱們注意到,在查詢今年入職的員工信息是,咱們用了DATE’2016-01-01’。這種寫法不多見諸於正式書籍中,由於這是非標準寫法。那麼將VARCHAR2轉換成DATE的標準寫法是什麼呢?

 

執行計劃會告訴你:

image.png

 

原來DATE’2016-01-01’被轉換成了TO_DATE(‘2016-01-01’, ‘SYYYY-MM-DDHH24:MI:SS’),這樣是爲何DATE只能支持YYYY-MM-DD格式的字符串的緣由:

image.png

可見,ORACLE優化器會將SQL中一些非標準的寫法轉換成標準的樸實的寫法。有的時候,最樸實的寫法,最容易讓人理解。

好比,當你拿到以下SQL時:

SELECT ENAME, SAL

  FROM EMP

 WHERE SAL >SOME(SELECT SAL FROM EMP WHERE DEPTNO =10);

你會不會很懵菜?會不會去查資料,研究SOME的做用和用法?或許大半天后,你仍然被SOME\ANY\ALL弄得雲山霧罩的。

 

如今,咱們試着從執行計劃去探究>SOME的含義。

image.png

 

咱們將子查詢替換成具體的LIST(100,200,300),發如今執行計劃中,謂詞變成了SAL > 100,意思就是大於最小值。換言之,原來的SQL就是要查詢大於DEPTNO=10部門最低工資的員工信息。

 

 來源:http://www.enmotech.com/web/detail/1/317/2.html
相關文章
相關標籤/搜索