看懂ORACLE執行計劃

一:什麼是Oracle執行計劃?數據庫

執行計劃是一條查詢語句在Oracle中的執行過程或訪問路徑的描述,注意,是查詢語句。數據結構

 二:怎樣查看Oracle執行計劃?函數

以PLSQL爲例:工具

執行計劃的經常使用列字段解釋:優化

基數(Rows):Oracle估計的當前操做的返回結果集行數ui

字節(Bytes):執行該步驟後返回的字節數spa

耗費(COST)、CPU耗費:Oracle估計的該步驟的執行成本,用於說明SQL執行的代價,理論上越小越好(該值可能與實際有出入)3d

時間(Time):Oracle估計的當前操做所需的時間對象

1):打開執行計劃:blog

在SQL窗口選中一條  SELECT 語句後,或者選中Tools > Explain Plan,或者按 F5 便可查看剛剛執行的這條查詢語句的執行計劃

打開執行計劃後,能夠點擊配置按鈕進行顯示配置。如圖

進入以下界面

中文版圖解,英語較差的童鞋能夠參考。

執行計劃配置

 

  三:看懂Oracle執行計劃

①:執行順序:

 

根據上圖中description列的縮進來判斷,縮進最多的最早執行,縮進相同時,最上面的最早執行,能夠經過點擊圖中箭頭,查看執行順序。

對圖中動做的一些說明:

1. 上圖中 TABLE ACCESS BY …  即描述的是該動做執行時表訪問(或者說Oracle訪問數據)的方式;

表訪問的幾種方式:(非所有)

  • TABLE ACCESS FULL(全表掃描)
  • TABLE ACCESS BY INDEX ROWID(經過ROWID的表存取)
  • INDEX FULL SCAN(索引掃描)

(1) TABLE ACCESS FULL(全表掃描)

Oracle會讀取表中全部的行,並檢查每一行是否知足SQL語句中的 Where 限制條件;

全表掃描時可使用多塊讀(即一次I/O讀取多塊數據塊)操做,提高吞吐量;

使用建議:數據量太大的表不建議使用全表掃描,除非自己須要取出的數據較多,佔到表數據總量的 5% ~ 10% 或以上

(2) TABLE ACCESS BY ROWID(經過ROWID的表存取) :

先說一下什麼是ROWID?

rowid

ROWID是由Oracle自動加在表中每行最後的一列僞列,既然是僞列,就說明表中並不會物理存儲ROWID的值;

你能夠像使用其它列同樣使用它,只是不能對該列的值進行增、刪、改操做;

一旦一行數據插入後,則其對應的ROWID在該行的生命週期內是惟一的,即便發生行遷移,該行的ROWID值也不變。

讓咱們再回到 TABLE ACCESS BY INDEX ROWID 來,INDEX指索引列,也就是說,這裏走的是索引的表的ROWID。

行的ROWID指出了該行所在的數據文件、數據塊以及行在該塊中的位置,因此經過ROWID能夠快速定位到目標數據上,這也是Oracle中存取單行數據最快的方法;

(3) INDEX FULL SCAN(索引掃描)

在索引塊中,既存儲每一個索引的鍵值,也存儲具備該鍵值的行的ROWID。

一個數字列上建索引後該索引可能的概念結構以下圖:

因此索引掃描其實分爲兩步:

Ⅰ:掃描索引獲得對應的ROWID

Ⅱ:經過ROWID定位到具體的行讀取數據

---------------------------------------------------------------------------索引掃描延伸---------------------華麗麗的分割線--------------------------------------------------------------------------------------------

索引掃描分五種:

  • INDEX UNIQUE SCAN(索引惟一掃描)
  • INDEX RANGE SCAN(索引範圍掃描)
  • INDEX FULL SCAN(索引全掃描)
  • INDEX FAST FULL SCAN(索引快速掃描)
  • INDEX SKIP SCAN(索引跳躍掃描)

a) INDEX UNIQUE SCAN(索引惟一掃描)

針對惟一性索引(UNIQUE INDEX)的掃描,每次至多隻返回一條記錄;

表中某字段存在 UNIQUE、PRIMARY KEY 約束時,Oracle常實現惟一性掃描;

b) INDEX RANGE SCAN(索引範圍掃描)

使用一個索引存取多行數據;

發生索引範圍掃描的三種狀況:

  • 在惟一索引列上使用了範圍操做符(如:>   <   <>   >=   <=   between)
  • 在組合索引上,只使用部分列進行查詢(查詢時必須包含前導列,不然會走全表掃描)
  • 對非惟一索引列上進行的任何查詢

c) INDEX FULL SCAN(索引全掃描)

進行全索引掃描時,查詢出的數據都必須從索引中能夠直接獲得(注意全索引掃描只有在CBO模式下才有效)

 

-----------------Oracle優化器簡述----------------華麗轉身到ORACLE   11G  ,丟掉8i 之前的舊觀念-------------------------------------------------------------------------------------

 

Oracle中的優化器是SQL分析和執行的優化工具,它負責生成、制定SQL的執行計劃。

Oracle的優化器有兩種:

  • RBO(Rule-Based Optimization) 基於規則的優化器
  • CBO(Cost-Based Optimization) 基於代價的優化器

RBO:

RBO有嚴格的使用規則,只要按照這套規則去寫SQL語句,不管數據表中的內容怎樣,也不會影響到你的執行計劃;

換句話說,RBO對數據「不敏感」,它要求SQL編寫人員必需要了解各項細則;

RBO一直沿用至ORACLE 9i,從ORACLE 10g開始,RBO已經完全被拋棄。

CBO:

CBO是一種比RBO更加合理、可靠的優化器,在ORACLE 10g中徹底取代RBO

CBO經過計算各類可能的執行計劃的「代價」,即COST,從中選用COST最低的執行方案做爲實際運行方案;

它依賴數據庫對象的統計信息,統計信息的準確與否會影響CBO作出最優的選擇,也就是對數據「敏感」。

-------------------------------------------------------------------------------回到正題---------------------------------------------------------------

d) INDEX FAST FULL SCAN(索引快速掃描):

掃描索引中的全部的數據塊,與 INDEX FULL SCAN 相似,可是一個顯著的區別是它不對查詢出的數據進行排序(即數據不是以排序順序被返回)

e) INDEX SKIP SCAN(索引跳躍掃描)

Oracle 9i後提供,有時候複合索引的前導列(索引包含的第一列)沒有在查詢語句中出現,oralce也會使用該複合索引,這時候就使用的INDEX SKIP SCAN;

何時會觸發 INDEX SKIP SCAN 呢?

前提條件:表有一個複合索引,且在查詢時有除了前導列(索引中第一列)外的其餘列做爲條件,而且優化器模式爲CBO時

當Oracle發現前導列的惟一值個數不多時,會將每一個惟一值都做爲常規掃描的入口,在此基礎上作一次查找,最後合併這些查詢;

例如:

假設表emp有ename(僱員名稱)、job(職位名)、sex(性別)三個字段,而且創建瞭如 create index idx_emp on emp (sex, ename, job) 的複合索引;

由於性別只有 '男' 和 '女' 兩個值,因此爲了提升索引的利用率,Oracle可將這個複合索引拆成 ('男', ename, job),('女', ename, job) 這兩個複合索引;

當查詢 select * from emp where job = 'Programmer' 時,該查詢發出後:

Oracle先進入sex爲'男'的入口,這時候使用到了 ('男', ename, job) 這條複合索引,查找 job = 'Programmer' 的條目;

再進入sex爲'女'的入口,這時候使用到了 ('女', ename, job) 這條複合索引,查找 job = 'Programmer' 的條目;

最後合併查詢到的來自兩個入口的結果集。

----------------------------------------------

 

2. 上圖中的 NESTED LOOPS … 描述的是錶鏈接方式;

JOIN 關鍵字用於將兩張表做鏈接,一次只能鏈接兩張表,JOIN 操做的各步驟通常是串行的(在讀取作鏈接的兩張表的數據時能夠並行讀取);

表(row source)之間的鏈接順序對於查詢效率有很大的影響,對首先存取的表(驅動表)先應用某些限制條件(Where過濾條件)以獲得一個較小的row source,可使得鏈接效率提升。

-------------------------延伸閱讀:驅動表(Driving Table)與匹配表(Probed Table)-------------------------

驅動表(Driving Table):

錶鏈接時首先存取的表,又稱外層表(Outer Table),這個概念用於 NESTED LOOPS(嵌套循環) 與 HASH JOIN(哈希鏈接)中;

若是驅動表返回較多的行數據,則對全部的後續操做有負面影響,故通常選擇小表(應用Where限制條件後返回較少行數的表)做爲驅動表。

匹配表(Probed Table):

又稱爲內層表(Inner Table),從驅動表獲取一行具體數據後,會到該表中尋找符合鏈接條件的行。故該表通常爲大表(應用Where限制條件後返回較多行數的表)。

---------------------------------------------------------------------------------------------------------

錶鏈接的幾種方式:

  • SORT MERGE JOIN(排序-合併鏈接)
  • NESTED LOOPS(嵌套循環)
  • HASH JOIN(哈希鏈接)
  • CARTESIAN PRODUCT(笛卡爾積)

注:這裏將首先存取的表稱做 row source 1,將以後參與鏈接的表稱做 row source 2

(1) SORT MERGE JOIN(排序-合併鏈接)

假設有查詢:select a.name, b.name from table_A a join table_B b on (a.id = b.id)

內部鏈接過程:

a) 生成 row source 1 須要的數據,按照鏈接操做關聯列(如示例中的a.id)對這些數據進行排序

b) 生成 row source 2 須要的數據,按照與 a) 中對應的鏈接操做關聯列(b.id)對數據進行排序

c) 兩邊已排序的行放在一塊兒執行合併操做(對兩邊的數據集進行掃描並判斷是否鏈接)

延伸:

若是示例中的鏈接操做關聯列 a.id,b.id 以前就已經被排過序了的話,鏈接速度即可大大提升,由於排序是很費時間和資源的操做,尤爲對於有大量數據的表。

故能夠考慮在 a.id,b.id 上創建索引讓其能預先排好序。不過遺憾的是,因爲返回的結果集中包括全部字段,因此一般的執行計劃中,即便鏈接列存在索引,也不會進入到執行計劃中,除非進行一些特定列處理(如僅僅只查詢有索引的列等)。

排序-合併鏈接的表無驅動順序,誰在前面均可以;

排序-合併鏈接適用的鏈接條件有: <   <=   =   >   >= ,不適用的鏈接條件有: <>    like

(2) NESTED LOOPS(嵌套循環)

內部鏈接過程:

a) 取出 row source 1 的 row 1(第一行數據),遍歷 row source 2 的全部行並檢查是否有匹配的,取出匹配的行放入結果集中

b) 取出 row source 1 的 row 2(第二行數據),遍歷 row source 2 的全部行並檢查是否有匹配的,取出匹配的行放入結果集中

c) ……

若 row source 1 (即驅動表)中返回了 N 行數據,則 row source 2 也相應的會被全表遍歷 N 次。

由於 row source 1 的每一行都會去匹配 row source 2 的全部行,因此當 row source 1 返回的行數儘量少而且能高效訪問 row source 2(如創建適當的索引)時,效率較高。

延伸:

嵌套循環的表有驅動順序,注意選擇合適的驅動表。

嵌套循環鏈接有一個其餘鏈接方式沒有的好處是:能夠先返回已經鏈接的行,而沒必要等全部的鏈接操做處理完才返回數據,這樣能夠實現快速響應。

應儘量使用限制條件(Where過濾條件)使驅動表(row source 1)返回的行數儘量少,同時在匹配表(row source 2)的鏈接操做關聯列上創建惟一索引(UNIQUE INDEX)或是選擇性較好的非惟一索引,此時嵌套循環鏈接的執行效率會變得很高。若驅動表返回的行數較多,即便匹配錶鏈接操做關聯列上存在索引,鏈接效率也不會很高。

(3)HASH JOIN(哈希鏈接) :

哈希鏈接只適用於等值鏈接(即鏈接條件爲  =  )

HASH JOIN對兩個表作鏈接時並不必定是都進行全表掃描,其並不限制表訪問方式;

內部鏈接過程簡述:

a) 取出 row source 1(驅動表,在HASH JOIN中又稱爲Build Table) 的數據集,而後將其構建成內存中的一個 Hash Table(Hash函數的Hash KEY就是鏈接操做關聯列),建立Hash位圖(bitmap)

b) 取出 row source 2(匹配表)的數據集,對其中的每一條數據的鏈接操做關聯列使用相同的Hash函數並找到對應的 a) 裏的數據在 Hash Table 中的位置,在該位置上檢查可否找到匹配的數據

----------------延伸閱讀:Hash Table相關----------------

來自Wiki的解釋:

In computing, a hash table (hash map) is a data structure used to implement an associative array, a structure that can map keys to values. A hash table uses a hash function to compute an index into an array of buckets or slots, from which the desired value can be found.

散列(hash)技術:在記錄的存儲位置和記錄具備的關鍵字key之間創建一個對應關係 f ,使得輸入key後,能夠獲得對應的存儲位置 f(key),這個對應關係 就是散列(哈希)函數;

採用散列技術將記錄存儲在一塊連續的存儲空間中,這塊連續的存儲空間就是散列表(哈希表);

 不一樣的key經同一散列函數散列後獲得的散列值理論上應該不一樣,可是實際中有可能相同,相同時便是發生了散列(哈希)衝突,解決散列衝突的辦法有不少,好比HashMap中就是用鏈地址法來解決哈希衝突;

哈希表是一種面向查找的數據結構,在輸入給定值後查找給定值對應的記錄在表中的位置以獲取特定記錄這個過程的速度很快。

--------------------------------------------------------

HASH JOIN的三種模式:

  • OPTIMAL HASH JOIN
  • ONEPASS HASH JOIN
  • MULTIPASS HASH JOIN

1) OPTIMAL HASH JOIN

OPTIMAL 模式是從驅動表(也稱Build Table)上獲取的結果集比較小,能夠把根據結果集構建的整個Hash Table都創建在用戶可使用的內存區域裏。

optimal_hash_join

鏈接過程簡述:

Ⅰ:首先對Build Table內各行數據的鏈接操做關聯列使用Hash函數,把Build Table的結果集構建成內存中的Hash Table。如圖所示,能夠把Hash Table看做內存中的一塊大的方形區域,裏面有不少的小格子,Build Table裏的數據就分散分佈在這些小格子中,而這些小格子就是Hash Bucket(見上面Wiki的定義)。

Ⅱ:開始讀取匹配表(Probed Table)的數據,對其中每行數據的鏈接操做關聯列都使用同上的Hash函數,定位Build Table裏使用Hash函數後具備相同值數據所在的Hash Bucket。

Ⅲ:定位到具體的Hash Bucket後,先檢查Bucket裏是否有數據,沒有的話就立刻丟掉匹配表(Probed Table)的這一行。若是裏面有數據,則繼續檢查裏面的數據(驅動表的數據)是否和匹配表的數據相匹配。

2): ONEPASS HASH JOIN :

從驅動表(也稱Build Table)上獲取的結果集較大,沒法將根據結果集構建的Hash Table所有放入內存中時,會使用 ONEPASS 模式。

one_pass_hash_join

鏈接過程簡述:

Ⅰ:對Build Table內各行數據的鏈接操做關聯列使用Hash函數,根據Build Table的結果集構建Hash Table後,因爲內存沒法放下全部的Hash Table內容,將致使有的Hash Bucket放在內存裏,有的Hash Bucket放在磁盤上,不管放在內存裏仍是磁盤裏,Oracle都使用一個Bitmap結構來反映這些Hash Bucket的狀態(包括其位置和是否有數據)。

Ⅱ:讀取匹配表數據並對每行的鏈接操做關聯列使用同上的Hash函數,定位Bitmap上Build Table裏使用Hash函數後具備相同值數據所在的Bucket。若是該Bucket爲空,則丟棄匹配表的這條數據。若是不爲空,則須要看該Bucket是在內存裏仍是在磁盤上。

若是在內存中,就直接訪問這個Bucket並檢查其中的數據是否匹配,有匹配的話就返回這條查詢結果。

若是在磁盤上,就先把這條待匹配數據放到一邊,將其先暫存在內存裏,等之後積累了必定量的這樣的待匹配數據後,再批量的把這些數據寫入到磁盤上(上圖中的 Dump probe partitions to disk)。

Ⅲ:當把匹配表完整的掃描了一遍後,可能已經返回了一部分匹配的數據了。接下來還有Hash Table中一部分在磁盤上的Hash Bucket數據以及匹配表中部分被寫入到磁盤上的待匹配數據未處理,如今Oracle會把磁盤上的這兩部分數據從新匹配一次,而後返回最終的查詢結果。

3): MULTIPASS HASH JOIN

當內存特別小或者相對而言Hash Table的數據特別大時,會使用 MULTIPASS 模式。MULTIPASS會屢次讀取磁盤數據,應儘可能避免使用該模式。

相關文章
相關標籤/搜索