一條SQL語句的執行計劃變化探究
最近有個同事碰到一個問題,想讓我給點思路。我大體瞭解了一下,是一個系統目前在做壓力測試,但是經業務反饋發現某個環節的處理時間有些長,排查了一圈,最後這件事情就落在了DB這邊,希望DB能夠給點意見,是否存在一些效能瓶頸。
我們從開發同學那裡得到的一個基本的SQL語句,根據關鍵字從v$sql中做了提取,發現對應的SQL語句的執行時間還是OK的。
得到的SQL語句如下:
SQL_ID SQL_FULLTEXT
------------- ----------------------------------------------------------------------------------------------------
6h4w0u8stp3z0 select APP_ID,GOODS_ID,ORDER_ID,ORDER_STATUS,GOODS_REGISTER_ID,GOODS_NUMBER,GROUP_ID,GOODS_PRICE,US
ER_ID,ROLE_ID,ROLE_NAME,CHANNEL_ID,PUSH_NUM,PUSH_INFO from TB_ORDE
R WHERE ORDER_ID=:1 AND USER_ID=:2 AND SUBSTR(CHANNEL_ID,0,4)=:3
這樣一個語句,如此來看效能上應該是沒有多少改進的空間了。我檢視了資料庫層面的相關統計資訊,發現DB time極低,比如Elapsed time為60分鐘,DB time就不到1分鐘,
類似下面這樣的輸出。
Snap Id Snap Time Sessions Curs/Sess
--------- ------------------- -------- ---------
Begin Snap: 3295 24-Aug-16 13:00:17 38 .9
End Snap: 3296 24-Aug-16 14:00:20 44 .9
Elapsed: 60.05 (mins)
DB Time: 0.03 (mins)
如此之低的情況下,很難和效能問題聯絡起來。透過得到的資料情況分析,細化到ASH報告也沒有發現任何異常,所以我們可以說DB層面沒有效能瓶頸,這個問題還需要進一步的確認。
當然交代完這件事情,主要任務就完成了。就簡單再看了看這個問題。
執行計劃的情況如下,看到這樣的執行計劃似乎也沒有任何可挑剔的。
謂詞資訊如下:
看到這裡我開始有一些疑惑,作為一個訂單表,訂單號應該是作為主鍵的,看到索引的情況,發現確實是。
表結構如下所示,在分析之前還是需要說明這些基本情況的。
那麼問題就來了,按道理是需要走唯一性索引代價最低,為什麼執行計劃缺走了另外一個索引,由期望中的唯一性索引掃描變為了範圍索引掃描,這是疑點1.
解答這個問題的過程中發現,其實會引出更多的問題,原本的問題只是開始,因為後面要走的路還有很多。
對於這個問題,我們得求助於10053事件,這個診斷事件能夠從根本上解釋清楚這個原因來。
當然開啟10053,我開啟了1級,日誌量相對要更多一些。
開啟10053事件
ALTER SESSION SET EVENTS='10053 trace name context forever, level 1';
執行SQL語句
結束10053事件
ALTER SESSION SET EVENTS '10053 trace name context off';
其中需要說明一點的是,如果採用如下的這種方式開啟診斷事件,是不行的。
alter session set current_schema=ordermob;
select 。。。。
from OP_ORDER WHERE ORDER_ID='160824165342672424' AND USER_ID='15000501196112' AND SUBSTR(CHANNEL_ID,0,4)=5046 ;
可以使用如下的方式來代替。
select 。。。
from ordermob.OP_ORDER WHERE ORDER_ID='160824165342672424' AND USER_ID='15000501196112' AND SUBSTR(CHANNEL_ID,0,4)=5046 ;
10053事件中查詢轉換結果如下:
Final query after transformations:******* UNPARSED QUERY IS *******
SELECT "OP_ORDER"."APP_ID" "APP_ID",。。。。 FROM "ORDERMOB"."TB_ORDER" "OP_ORDER" WHERE "OP_ORDER"."ORDER_ID"='160824165342672424' AND "OP_ORDER"."USER_ID"='15000501196112' AND TO_NUMBER(SUBSTR(TO_CHAR("OP_ORDER"."CHANNEL_ID"),0,4))=5046
kkoqbc: optimizing query block SEL$1 (#0)
統計資訊的一些明細資訊如下:比如CLUF是叢集因子。
***************************************
BASE STATISTICAL INFORMATION
***********************
Table Stats::
Table: OP_ORDER Alias: OP_ORDER
#Rows: 2143 #Blks: 50 AvgRowLen: 157.00 ChainCnt: 0.00
Index Stats::
Index: IDX_OP_ORDER_PUSH_STATE Col#: 25
LVLS: 1 #LB: 6 #DK: 1 LB/K: 6.00 DB/K: 50.00 CLUF: 50.00
Index: IND_ORDER_USERID Col#: 15
LVLS: 1 #LB: 10 #DK: 230 LB/K: 1.00 DB/K: 2.00 CLUF: 625.00
Index: SYS_C0011155 Col#: 4
LVLS: 1 #LB: 9 #DK: 2143 LB/K: 1.00 DB/K: 1.00 CLUF: 1271.00
而下面的這段內容就讓人更加疑惑了。Density代表列的密度,可以看到Density的值ORDER_ID對應的為0.000467,而USER_ID對應的為0.000233,
表中目前存在2000多條記錄,在Oracle中,表裡沒有直方圖資訊的時候,是按照1/NDV的形式來計算的。而這裡OERDER_ID對應的值,其實就是1/2143得到的值就是0.000467
而user_id的值如果按照1/NDV的形式的話,應該是0,0043478這樣的值,很顯然這裡不是這個值。
***************************************
1-ROW TABLES: OP_ORDER[OP_ORDER]#0
Access path analysis for OP_ORDER
***************************************
SINGLE TABLE ACCESS PATH
Single Table Cardinality Estimation for OP_ORDER[OP_ORDER]
Column (#4): ORDER_ID(
AvgLen: 19 NDV: 2143 Nulls: 0 Density: 0.000467
Column (#15):
NewDensity:0.000233, OldDensity:0.000233 BktCnt:2143, PopBktCnt:2084, PopValCnt:171, NDV:230
Column (#15): USER_ID(
AvgLen: 15 NDV: 230 Nulls: 0 Density: 0.000233
Histogram: Freq #Bkts: 230 UncompBkts: 2143 EndPtVals: 230
這是為什麼呢,我們來看看直方圖的資訊。可以看到ORDER_ID列是沒有直方圖資訊的,而USER_ID列卻含有。
SQL> SELECT COLUMN_NAME,NUM_DISTINCT,NUM_BUCKETS,HISTOGRAM
FROM DBA_TAB_COL_STATISTICS WHERE OWNER='ORDERMOB' AND TABLE_NAME='OP_ORDER'
COLUMN_NAME NUM_DISTINCT NUM_BUCKETS HISTOGRAM
------------------------------ ------------ ----------- ---------------
APP_ID 1 1 FREQUENCY
CHANNEL_ID 6 6 FREQUENCY
ACCOUNT 230 1 NONE
ORDER_ID 2143 1 NONE
GOODS_ID 28 1 NONE
USER_ID 230 230 FREQUENCY
CREATE_DATE 2010 254 HEIGHT BALANCED
這裡需要提一下直方圖分為兩種,一種是頻率直方圖,顯示為:FREQUENCY,另外一種是高度平衡直方圖,顯示為:HEIGHT BALANCED
高度均衡直方圖適用於 資料分佈不均勻 ,由於列中資料很多,這時資料比較密集,不利於分析和評估,這時直方圖需要均衡化。
頻率直方圖適用於資料分佈很均勻的情況。當然如果資料很平均,其實也沒有太大的意義,直方圖本身就是適用於對應列中資料分佈比較傾斜的列(不均勻)
那麼問題似乎有了一些眉目,我們知道在Oracle中收集統計資訊的時候是推薦使用FOR ALL COLUMNS SIZE AUTO的選項的。
收集統計資訊的語句大概是這樣的形式。
exec dbms_stats.gather_table_stats(tabname => 'OP_ORDER',ownname => 'ORDERMOB',method_opt => 'FOR ALL COLUMNS SIZE AUTO');
所以我大膽做了一個測試,那就是取消直方圖的資訊。
exec dbms_stats.gather_table_stats(tabname => 'OP_ORDER',ownname => 'ORDERMOB',method_opt => 'FOR ALL COLUMNS SIZE 1');
再次檢視執行計劃,發現就會採用唯一性索引掃描了,達到了我們預期的效果。
當然問題來了,這個是為什麼呢,收集統計資訊中的auto選項是什麼含義呢。為什麼兩個資料型別一樣的(varchar2(64))的列,境遇卻大大不同。且聽我下回慢慢道來。
我們從開發同學那裡得到的一個基本的SQL語句,根據關鍵字從v$sql中做了提取,發現對應的SQL語句的執行時間還是OK的。
得到的SQL語句如下:
SQL_ID SQL_FULLTEXT
------------- ----------------------------------------------------------------------------------------------------
6h4w0u8stp3z0 select APP_ID,GOODS_ID,ORDER_ID,ORDER_STATUS,GOODS_REGISTER_ID,GOODS_NUMBER,GROUP_ID,GOODS_PRICE,US
ER_ID,ROLE_ID,ROLE_NAME,CHANNEL_ID,PUSH_NUM,PUSH_INFO from TB_ORDE
R WHERE ORDER_ID=:1 AND USER_ID=:2 AND SUBSTR(CHANNEL_ID,0,4)=:3
這樣一個語句,如此來看效能上應該是沒有多少改進的空間了。我檢視了資料庫層面的相關統計資訊,發現DB time極低,比如Elapsed time為60分鐘,DB time就不到1分鐘,
類似下面這樣的輸出。
Snap Id Snap Time Sessions Curs/Sess
--------- ------------------- -------- ---------
Begin Snap: 3295 24-Aug-16 13:00:17 38 .9
End Snap: 3296 24-Aug-16 14:00:20 44 .9
Elapsed: 60.05 (mins)
DB Time: 0.03 (mins)
如此之低的情況下,很難和效能問題聯絡起來。透過得到的資料情況分析,細化到ASH報告也沒有發現任何異常,所以我們可以說DB層面沒有效能瓶頸,這個問題還需要進一步的確認。
當然交代完這件事情,主要任務就完成了。就簡單再看了看這個問題。
執行計劃的情況如下,看到這樣的執行計劃似乎也沒有任何可挑剔的。
謂詞資訊如下:
看到這裡我開始有一些疑惑,作為一個訂單表,訂單號應該是作為主鍵的,看到索引的情況,發現確實是。
表結構如下所示,在分析之前還是需要說明這些基本情況的。
那麼問題就來了,按道理是需要走唯一性索引代價最低,為什麼執行計劃缺走了另外一個索引,由期望中的唯一性索引掃描變為了範圍索引掃描,這是疑點1.
解答這個問題的過程中發現,其實會引出更多的問題,原本的問題只是開始,因為後面要走的路還有很多。
對於這個問題,我們得求助於10053事件,這個診斷事件能夠從根本上解釋清楚這個原因來。
當然開啟10053,我開啟了1級,日誌量相對要更多一些。
開啟10053事件
ALTER SESSION SET EVENTS='10053 trace name context forever, level 1';
執行SQL語句
結束10053事件
ALTER SESSION SET EVENTS '10053 trace name context off';
其中需要說明一點的是,如果採用如下的這種方式開啟診斷事件,是不行的。
alter session set current_schema=ordermob;
select 。。。。
from OP_ORDER WHERE ORDER_ID='160824165342672424' AND USER_ID='15000501196112' AND SUBSTR(CHANNEL_ID,0,4)=5046 ;
可以使用如下的方式來代替。
select 。。。
from ordermob.OP_ORDER WHERE ORDER_ID='160824165342672424' AND USER_ID='15000501196112' AND SUBSTR(CHANNEL_ID,0,4)=5046 ;
10053事件中查詢轉換結果如下:
Final query after transformations:******* UNPARSED QUERY IS *******
SELECT "OP_ORDER"."APP_ID" "APP_ID",。。。。 FROM "ORDERMOB"."TB_ORDER" "OP_ORDER" WHERE "OP_ORDER"."ORDER_ID"='160824165342672424' AND "OP_ORDER"."USER_ID"='15000501196112' AND TO_NUMBER(SUBSTR(TO_CHAR("OP_ORDER"."CHANNEL_ID"),0,4))=5046
kkoqbc: optimizing query block SEL$1 (#0)
統計資訊的一些明細資訊如下:比如CLUF是叢集因子。
***************************************
BASE STATISTICAL INFORMATION
***********************
Table Stats::
Table: OP_ORDER Alias: OP_ORDER
#Rows: 2143 #Blks: 50 AvgRowLen: 157.00 ChainCnt: 0.00
Index Stats::
Index: IDX_OP_ORDER_PUSH_STATE Col#: 25
LVLS: 1 #LB: 6 #DK: 1 LB/K: 6.00 DB/K: 50.00 CLUF: 50.00
Index: IND_ORDER_USERID Col#: 15
LVLS: 1 #LB: 10 #DK: 230 LB/K: 1.00 DB/K: 2.00 CLUF: 625.00
Index: SYS_C0011155 Col#: 4
LVLS: 1 #LB: 9 #DK: 2143 LB/K: 1.00 DB/K: 1.00 CLUF: 1271.00
而下面的這段內容就讓人更加疑惑了。Density代表列的密度,可以看到Density的值ORDER_ID對應的為0.000467,而USER_ID對應的為0.000233,
表中目前存在2000多條記錄,在Oracle中,表裡沒有直方圖資訊的時候,是按照1/NDV的形式來計算的。而這裡OERDER_ID對應的值,其實就是1/2143得到的值就是0.000467
而user_id的值如果按照1/NDV的形式的話,應該是0,0043478這樣的值,很顯然這裡不是這個值。
***************************************
1-ROW TABLES: OP_ORDER[OP_ORDER]#0
Access path analysis for OP_ORDER
***************************************
SINGLE TABLE ACCESS PATH
Single Table Cardinality Estimation for OP_ORDER[OP_ORDER]
Column (#4): ORDER_ID(
AvgLen: 19 NDV: 2143 Nulls: 0 Density: 0.000467
Column (#15):
NewDensity:0.000233, OldDensity:0.000233 BktCnt:2143, PopBktCnt:2084, PopValCnt:171, NDV:230
Column (#15): USER_ID(
AvgLen: 15 NDV: 230 Nulls: 0 Density: 0.000233
Histogram: Freq #Bkts: 230 UncompBkts: 2143 EndPtVals: 230
這是為什麼呢,我們來看看直方圖的資訊。可以看到ORDER_ID列是沒有直方圖資訊的,而USER_ID列卻含有。
SQL> SELECT COLUMN_NAME,NUM_DISTINCT,NUM_BUCKETS,HISTOGRAM
FROM DBA_TAB_COL_STATISTICS WHERE OWNER='ORDERMOB' AND TABLE_NAME='OP_ORDER'
COLUMN_NAME NUM_DISTINCT NUM_BUCKETS HISTOGRAM
------------------------------ ------------ ----------- ---------------
APP_ID 1 1 FREQUENCY
CHANNEL_ID 6 6 FREQUENCY
ACCOUNT 230 1 NONE
ORDER_ID 2143 1 NONE
GOODS_ID 28 1 NONE
USER_ID 230 230 FREQUENCY
CREATE_DATE 2010 254 HEIGHT BALANCED
這裡需要提一下直方圖分為兩種,一種是頻率直方圖,顯示為:FREQUENCY,另外一種是高度平衡直方圖,顯示為:HEIGHT BALANCED
高度均衡直方圖適用於 資料分佈不均勻 ,由於列中資料很多,這時資料比較密集,不利於分析和評估,這時直方圖需要均衡化。
頻率直方圖適用於資料分佈很均勻的情況。當然如果資料很平均,其實也沒有太大的意義,直方圖本身就是適用於對應列中資料分佈比較傾斜的列(不均勻)
那麼問題似乎有了一些眉目,我們知道在Oracle中收集統計資訊的時候是推薦使用FOR ALL COLUMNS SIZE AUTO的選項的。
收集統計資訊的語句大概是這樣的形式。
exec dbms_stats.gather_table_stats(tabname => 'OP_ORDER',ownname => 'ORDERMOB',method_opt => 'FOR ALL COLUMNS SIZE AUTO');
所以我大膽做了一個測試,那就是取消直方圖的資訊。
exec dbms_stats.gather_table_stats(tabname => 'OP_ORDER',ownname => 'ORDERMOB',method_opt => 'FOR ALL COLUMNS SIZE 1');
再次檢視執行計劃,發現就會採用唯一性索引掃描了,達到了我們預期的效果。
當然問題來了,這個是為什麼呢,收集統計資訊中的auto選項是什麼含義呢。為什麼兩個資料型別一樣的(varchar2(64))的列,境遇卻大大不同。且聽我下回慢慢道來。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23718752/viewspace-2124094/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一條SQL語句的執行計劃變化探究(r10筆記第9天)SQL筆記
- 通過分析SQL語句的執行計劃優化SQL語句SQL優化
- 清除SQL語句的執行計劃SQL
- 通過分析SQL語句的執行計劃優化SQL 二SQL優化
- 一條sql語句的執行過程SQL
- spark sql語句效能最佳化及執行計劃SparkSQL
- 通過分析SQL語句的執行計劃優化SQL(總結)SQL優化
- 一條update SQL語句是如何執行的SQL
- 一條SQL更新語句是如何執行的SQL
- 一條SQL更新語句是如何執行的?SQL
- 解決: 執行計劃變了,一條語句要跑20小時候.
- 透過分析SQL語句的執行計劃最佳化SQL(總結)SQL
- 一條更新的SQL語句是如何執行的?SQL
- [20120104]穩定一條sql語句的執行計劃.txtSQL
- 根據SQL Id獲得SQL語句的執行計劃SQL
- 一條 SQL 查詢語句是如何執行的?SQL
- 一條SQL語句在MySQL中如何執行的MySql
- 一條sql語句的優化SQL優化
- [轉]透過分析SQL語句的執行計劃最佳化SQL(總結)SQL
- MyBatis 一次執行多條SQL語句MyBatisSQL
- 獲得目標SQL語句執行計劃的方法SQL
- 一條sql語句在mysql中是如何執行的MySql
- 一條 SQL 語句在 MySQL 中是如何執行的?MySql
- 11g改變了DELETE語句的執行計劃delete
- 一條更新語句的執行流程
- MySQL日誌(一條sql更新語句是如何執行的)MySql
- 獲取oracle sql語句詳細些執行計劃OracleSQL
- 一條SQL語句的優化過程SQL優化
- 執行一條sql語句都經歷了什麼?SQL
- 執行一條 SQL 語句,期間發生了什麼?SQL
- 一條簡單的sql語句執行15天的原因分析SQL
- Mybatis原始碼分析(五)探究SQL語句的執行過程MyBatis原始碼SQL
- 獲取oracle正在處於等待狀態的sql語句的執行計劃的語句OracleSQL
- 一條查詢語句的執行流程
- sql執行計劃是否改變SQL
- MySQL系列之一條SQL查詢語句的執行過程MySql
- PostgreSQL執行計劃變化SQL
- 使用dbms_xplan包來獲得sql語句的執行計劃SQL