執行計划:一條查詢語句在ORACLE中的執行過程或訪問路徑的描述。即就是對一個查詢任務,做出一份怎樣去完成任務的詳細方案。
如果要分析某條SQL的性能問題,通常我們要先看SQL的執行計划,看看SQL的每一步執行是否存在問題。 看懂執行計划也就成了SQL優化的先決條件。 通過執行計划定位性能問題,定位后就通過建立索引、修改sql等解決問題。
一、執行計划的查看
1.1 設置autotrace
autotrace命令如下
序號 |
命令 |
解釋 |
1 |
SET AUTOTRACE OFF |
此為默認值,即關閉Autotrace |
2 |
SET AUTOTRACE ON EXPLAIN |
只顯示執行計划 |
3 |
SET AUTOTRACE ON STATISTICS |
只顯示執行的統計信息 |
4 |
SET AUTOTRACE ON |
包含2,3兩項內容 |
5 |
SET AUTOTRACE TRACEONLY |
與ON相似,但不顯示語句的執行結果 |
1.2 使用SQL
在執行的sql前面加上EXPLAIN PLAN FOR
SQL> EXPLAIN PLAN FOR SELECT * FROM EMP; 已解釋。 SQL> SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE')); 或者: SQL> select * from table(dbms_xplan.display);
1.3 使用PL/SQL Developer,Navicat, Toad等客戶端工具
常見的客戶端工具如PL/SQL Developer,Navicat, Toad都支持查看解釋計划。
Navicat
[SQL] DELETE PLAN_TABLE [SQL] EXPLAIN PLAN FOR SELECT * FROM EMP [SQL] SELECT LPAD(' ', LEVEL-1) || OPERATION || ' (' || OPTIONS || ')' "Operation", OBJECT_NAME "Object", OPTIMIZER "Optimizer", COST "Cost", CARDINALITY "Cardinality", BYTES "Bytes", PARTITION_START "Partition Start", PARTITION_ID "Partition ID" , ACCESS_PREDICATES "Access Predicates", FILTER_PREDICATES "Filter Predicates" FROM PLAN_TABLE START WITH ID = 0 CONNECT BY PRIOR ID=PARENT_ID 時間: 0.184s
PL/SQL Developer
二、如何讀懂執行計划
2.1執行順序的原則
執行順序的原則是:由上至下,從右向左
由上至下:在執行計划中一般含有多個節點,相同級別(或並列)的節點,靠上的優先執行,靠下的后執行
從右向左:在某個節點下還存在多個子節點,先從最靠右的子節點開始執行。
一般按縮進長度來判斷,縮進最大的最先執行,如果有2行縮進一樣,那么就先執行上面的。
圖片是Toad工具查看的執行計划。 在Toad 里面,很清楚的顯示了執行的順序。
以下面的sql為例(sakila樣例數據庫中的address city country連接查詢)
select address.address, city.city, country.country from address inner join city on address.city_id = city.city_id inner join country on city.country_id = country.country_id;
2.2 執行計划中字段解釋
ID: 一個序號,但不是執行的先后順序。執行的先后根據縮進來判斷。
Operation: 當前操作的內容。
Rows: 當前操作的Cardinality,Oracle估計當前操作的返回結果集。
Cost(CPU):Oracle 計算出來的一個數值(代價),用於說明SQL執行的代價。
Time:Oracle 估計當前操作的時間。
在看執行計划的時候,除了看執行計划本身,還需要看謂詞和統計信息。 通過整體信息來判斷SQL效率。
2.3 謂詞說明
Access :
- 通過某種方式定位了需要的數據,然后讀取出這些結果集,叫做Access。
- 表示這個謂詞條件的值將會影響數據的訪問路勁(表還是索引)。
Filter:
- 把所有的數據都訪問了,然后過濾掉不需要的數據,這種方式叫做filter 。
- 表示謂詞條件的值不會影響數據的訪問路勁,只起過濾的作用。
在謂詞中主要注意access,要考慮謂詞的條件,使用的訪問路徑是否正確。
2.4 Statistics(統計信息)說明
recursive calls |
產生的遞歸sql調用的條數。
|
Db block gets: |
從buffer cache中讀取的block的數量 |
consistent gets |
從buffer cache中讀取的undo數據的block的數量 |
physical reads |
從磁盤讀取的block的數量 |
redo size |
DML生成的redo的大小 |
bytes sent via SQL*Net to client |
數據庫服務器通過SQL*Net向查詢客戶端發送的查詢結果字節數 |
bytes received via SQL*Net from client |
通過SQL*Net接受的來自客戶端的數據字節數 |
SQL*Net roundtrips to/from client |
服務器和客戶端來回往返通信的Oracle Net messages條數 |
sorts (memory) |
在內存執行的排序量 |
sorts (disk) |
在磁盤上執行的排序量 |
rows processed |
處理的數據的行數 |
解釋:
Recursive Calls:Number of recursive calls generated at both the user and system level.
Oracle Database maintains tables used for internal processing. When it needs to change these tables, Oracle Database generates an internal SQL statement, which in turn generates a recursive call. In short, recursive calls are basically SQL performed on behalf of your SQL. So, if you had to parse the query, for example, you might have had to run some other queries to get data dictionary information. These would be recursive calls. Space management, security checks, calling PL/SQL from SQL—all incur recursive SQL calls。
當執行一條SQL語句時,產生的對其他SQL語句的調用,這些額外的語句稱之為''recursive calls''或''recursive SQL statements''. 我們做一條insert 時,沒有足夠的空間來保存row記錄,Oracle 通過Recursive Call 來動態的分配空間。
DB Block Gets:Number of times a CURRENT block was requested.
Current mode blocks are retrieved as they exist right now, not in a consistent read fashion. Normally, blocks retrieved for a query are retrieved as they existed when the query began. Current mode blocks are retrieved as they exist right now, not from a previous point in time. During a SELECT, you might see current mode retrievals due to reading the data dictionary to find the extent information for a table to do a full scan (because you need the "right now" information, not the consistent read). During a modification, you will access the blocks in current mode in order to write to them.
DB Block Gets:請求的數據塊在buffer能滿足的個數
當前模式塊意思就是在操作中正好提取的塊數目,而不是在一致性讀的情況下而產生的塊數。正常的情況下,一個查詢提取的塊是在查詢開始的那個時間點上存在的數據塊,當前塊是在這個時刻存在的數據塊,而不是在這個時間點之前或者之后的數據塊數目。
Consistent Gets: Number of times a consistent read was requested for a block.
This is how many blocks you processed in "consistent read" mode. This will include counts of blocks read from the rollback segment in order to roll back a block. This is the mode you read blocks in with a SELECT, for example. Also, when you do a searched UPDATE/DELETE, you read the blocks in consistent read mode and then get the block in current mode to actually do the modification.
(Consistent Gets: 數據請求總數在回滾段Buffer中的數據一致性讀所需要的數據塊)
這里的概念是在處理你這個操作的時候需要在一致性讀狀態上處理多少個塊,這些塊產生的主要原因是因為由於在你查詢的過程中,由於其他會話對數據塊進行操作,而對所要查詢的塊有了修改,但是由於我們的查詢是在這些修改之前調用的,所以需要對回滾段中的數據塊的前映像進行查詢,以保證數據的一致性。這樣就產 生了一致性讀。
Physical Reads:
Total number of data blocks read from disk. This number equals the value of "physical reads direct" plus all reads into buffer cache.
(Physical Reads:實例啟動后,從磁盤讀到Buffer Cache數據塊數量)
就是從磁盤上讀取數據塊的數量,其產生的主要原因是:
(1) 在數據庫高速緩存中不存在這些塊
(2) 全表掃描
(3) 磁盤排序
它們三者之間的關系大致可概括為:
邏輯讀指的是Oracle從內存讀到的數據塊數量。一般來說是'consistent gets' + 'db block gets'。當在內存中找不到所需的數據塊的話就需要從磁盤中獲取,於是就產生了'physical reads'。
Physical Reads通常是我們最關心的,如果這個值很高,說明要從磁盤請求大量的數據到Buffer Cache里,通常意味着系統里存在大量全表掃描的SQL語句,這會影響到數據庫的性能,因此盡量避免語句做全表掃描,對於全表掃描的SQL語句,建議增 加相關的索引,優化SQL語句來解決。
關於physical reads ,db block gets 和consistent gets這三個參數之間有一個換算公式:
數據緩沖區的使用命中率=1 - ( physical reads / (db block gets + consistent gets) )。
用以下語句可以查看數據緩沖區的命中率:
SQL>SELECT name, value FROM v$sysstat WHERE name IN ('db block gets', 'consistent gets','physical reads');
查詢出來的結果Buffer Cache的命中率應該在90%以上,否則需要增加數據緩沖區的大小。
清空Buffer Cache和數據字典緩存 SQL> alter system flush shared_pool; //請勿隨意在生產環境執行此語句 System altered SQL> alter system flush buffer_cache; //請勿隨意在生產環境執行此語句 System altered
bytes sent via SQL*Net to client:
Total number of bytes sent to the client from the foreground processes.
bytes received via SQL*Net from client:
Total number of bytes received from the client over Oracle Net.
SQL*Net roundtrips to/from client:
Total number of Oracle Net messages sent to and received from the client.
Oracle Net是把Oracle網絡粘合起來的粘合劑。它負責處理客戶到服務器和服務器到客戶通信,
sorts (memory): 在內存里排序。
Number of sort operations that were performed completely in memory and did not require any disk writes
You cannot do much better than memory sorts, except maybe no sorts at all. Sorting is usually caused by selection criteria specifications within table join SQL operations.
Sorts(disk): 在磁盤上排序。
Number of sort operations that required at least one disk write. Sorts that require I/O to disk are quite resource intensive. Try increasing the size of the initialization parameter SORT_AREA_SIZE.
所有的sort都是優先在memory中做的,當要排序的內容太多,在sort area中放不下的時候,會需要臨時表空間,產生sorts(disk)
rows processed
The number of rows processed
更多內容參考Oracle聯機文檔:Statistics Descriptions
2.5 動態分析
動態統計量收集是Oracle CBO優化器的一種特性。優化器生成執行計划是依據成本cost公式計算出的,如果相關數據表沒有收集過統計量,又要使用CBO的機制,就會引起動態采樣。
動態采樣(dynamic sampling)就是在生成執行計划是,以一個很小的采用率現進行統計量收集。由於采樣率低,采樣過程快但是不精確,而且采樣結果不會進入到數據字典中。
如果在執行計划中有如下提示:
Note -------------dynamic sampling used for the statement
這提示用戶CBO當前使用的技術,需要用戶在分析計划時考慮到這些因素。 當出現這個提示,說明當前表使用了動態采樣。 我們從而推斷這個表可沒有做過分析。
這里會出現兩種情況:
(1) 如果表沒有做過分析,那么CBO可以通過動態采樣的方式來獲取分析數據,也可以或者正確的執行計划。
(2) 如果表分析過,但是分析信息過舊,這時CBO就不會在使用動態采樣,而是使用這些舊的分析數據,從而可能導致錯誤的執行計划。
更多參照為准確生成執行計划更新統計信息-analyze與dbms_stats
三、JOIN方式
3.1 hash join
3.2 merge join
3.3 nested loop
參照:Nested Loops,Hash Join , Sort Merge Join