數據庫---Oracle如何看懂執行計劃

前言

參考文章地址 —>看懂ORACLE執行計劃

1. 什麼是執行計劃?

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

2. 怎麼查看sql執行計劃

以PLSQL爲例:

執行計劃的常用列字段解釋:

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

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

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

時間(Time):Oracle估計的當前操作所需的時間

2.1 打開執行計劃

在SQL窗口選中一條 SELECT 語句後,或者選中工具> 解釋計劃,或者按 F5 即可查看剛剛執行的這條查詢語句的執行計劃
打開解釋計劃
打開執行計劃後,可以點擊配置按鈕進行顯示配置。如圖
配置解釋計劃列
然後根據自己需要來選擇對應顯示內容

3. 看懂解釋計劃

3.1 執行順序

解釋計劃執行順序選擇
根據上圖中description列的縮進來判斷,縮進最多的最先執行,縮進相同時,最上面的最先執行,可以通過點擊圖中箭頭,查看執行順序。

3.2 動作說明

3.2.1 TABLE ACCESS BY

上圖中 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是由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(索引跳躍掃描)

  1. INDEX UNIQUE SCAN(索引唯一掃描):

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

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

  2. INDEX RANGE SCAN(索引範圍掃描):

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

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

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

  3. INDEX FULL SCAN(索引全掃描):

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

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

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

  5. 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’ 的條目;

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


oracle優化器
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做出最優的選擇,也就是對數據“敏感”。

3.2.2 NESTED LOOPS

上圖中的 NESTED LOOPS … 描述的是表連接方式;

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

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

表連接的幾種方式:

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),這個對應關係 f 就是散列(哈希)函數;

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

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

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


4.從執行計劃看如何優化sql

4.1 索引創建原則
  1. 選擇唯一性索引
  2. 儘量使用數據量少的索引
  3. 對於查詢頻率高的字段創建索引;
  4. 對排序、分組、聯合查詢頻率高的字段創建索引;
  5. 索引的數目不宜太多,原因:每創建一個索引都會佔用相應的物理控件;過多的索引會導致insert、update、delete語句的執行效率降低;
4.2 關聯查詢優先級
  1. 儘量是用 = 來關聯;
  2. 儘量避免全表掃描;
  3. inner join 的效率遠遠大於left join,前提是保證數據的正確時;

5.End

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章