Oracle中rownum對錶的掃描方式效能上的影響深入探究
前言:
作業系統資訊:
資料庫版本:
構造測試資料:
實驗過程:
說明:由於該過程是做的全表掃描下對rownum加限定詞後,掃描表的塊是否發生減少,為了避免db_file_multiblock_read_count的影響,將其值設定為1。
上述步驟分析:
select count(*) from test where rownum<=171只讀取了前兩個塊的內容,檢視該語句執行計劃,從TABLE ACCESS FULL可以看出是全表掃描,而最後檢視x$bh中sys.test表的情況可以看出,表的所有塊也被讀進去buffer cache中,符合全表掃描。
不過這裡有一個很奇怪的現象,重新清空buffer cache之後,繼續剛剛的動作,情況就不同了。
上述步驟分析:
但從結果上看,執行計劃是全表掃描,但是讀進buffer cache的塊卻只有資訊塊138號和資料塊139、140。
說明rownum<=N(N為正整數)的時候,sql語句只要找出前N條滿足條件的結果後,便不再進行讀取,也就是“前言”中假設的那個問題的前者猜測。
只不過這裡執行計劃並不準確體現出來。
但是對比前邊實驗,為什麼一開始會全部對錶的塊進行讀取,第二次開始才讀取含有滿足條件的塊??
實驗可以發現,若是對shared pool和buffer poll同時進行清空的情況,執行select count(*) from test where rownum<=171語句之後,又變成表的所有塊也被讀進去buffer cache中的情況。
看的出來,是否清空share pool是關鍵。
懷著疑問,繼續做實驗。接下去清空share pool,做10046,執行select count(*) from test where rownum<=171。
檢視trace檔案,部分內容如下:
上述步驟分析:
看上邊trace部分可以知道,select count(*) from test where rownum<=171語句已經沒有涉及到物理io的讀取了,原因是因為SQL語句“SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),0), NVL(SUM(C2),0) FROM (SELECT /*+ NO_PARALLEL("TEST") FULL("TEST") NO_PARALLEL_INDEX("TEST") */ 1 AS C1, 1 AS C2 FROM "SYS"."TEST" "TEST") SAMPLESUB”已經把表test所有的塊讀進buffer cache裡邊了。
這個SQL語句是oracle內部的遞迴sql,去掉相關hint簡化如下:
SELECT NVL(SUM(C1),0), NVL(SUM(C2),0) FROM (SELECT 1 AS C1, 1 AS C2 FROM "SYS"."TEST" "TEST") SAMPLESUB,可以看到這裡已經對test表進行了訪問,而且從trace看是全表掃描。
因此我們知道了實際上所有的塊被讀進去buffer cache裡邊,並非select count(*) from test where rownum<=171語句引起的,也就是rownum<=N(N為正整數)的時候,sql語句只要找出前N條滿足條件的結果後,便不再進行讀取,這是oracle對rownum的優化。
但是由此引出另外的疑問,該遞迴sql的產生只有在第一次執行select count(*) from test where rownum<=171(share pool無此sql執行計劃)的時候才會產生,第二次執行做10046可以知道沒有該遞迴sql了。
問題出來總是要解決的,所幸還是可以找到相關資料的。
原因在於動態取樣,這裡引用網上對於動態取樣的一些描述:
動態取樣(Dynamic Sampling)技術的最初提出是在Oracle 9i R2,在段(表,索引,分割槽)沒有分析的情況下,為了使CBO 優化器得到足夠的資訊以保證做出正確的執行計劃而發明的一種技術,可以把它看做分析手段的一種補充。
當段物件沒有統計資訊時(即沒有做分析),動態取樣技術可以通過直接從需要分析的物件上收集資料塊(取樣)來獲得CBO需要的統計資訊。
顯然,遞迴sql是由於test表上無統計資訊而執行select count(*) from test where rownum<=171語句時後臺做動態取樣產生的。
下邊通過一些手段,在test表上沒有統計資訊的情況下,強制不做動態取樣。
從結果看,即使是清空共享池,沒有動態取樣下,是不會將所有的塊快取進資料緩衝區。
從動態取樣產生的原因上看,是因為test表沒有統計資訊,我們手動收集統計資訊,那麼即使不用hint去禁用動態取樣,也應該不會將所有的塊緩衝進去buffer cache裡邊。是否如此?
結果顯而易見,到這裡大功告成。
關於動態取樣,這裡就不深入了。
對於rownum的結論,這裡附上一個實驗資料做效能對比吧!
附上動態取樣相關資料:
動態取樣的作用
1、CBO 依賴的是充分的統計分析資訊,但是並不是每個使用者都會非常認真,及時地去對每個表做分析。為了保證執行計劃都儘可能地正確,Oracle需要使用動態取樣技術來幫助CBO 獲取儘可能多的資訊。
2、全域性臨時表。通常來講,臨時表的資料是不做分析的,因為它存放的資料是臨時性的,可能很快就釋放了,但是當一個查詢關聯到這樣的臨時表時,CBO要想獲得臨時表上的統計資訊分析資料,就只能依賴於動態取樣。
3、動態取樣除了可以在段物件沒有分析時,給CBO提供分析資料之外,還有一個獨特的能力,它可以對不同列之間的相關性做統計。這點通常發生在表設計不符合3NF的情況下,這個特性在表 符合3NF設計的 情況下少見。
儘管看到動態取樣的優點,但是它的缺點也是顯而易見,否則Oracle一定會一直使用動態取樣來取代資料分析:
1、 在OLAP或者資料倉儲環境下,SQL執行消耗的資源要遠遠大於SQL解析,那麼讓解析在消耗多一點資源做一些動態取樣分析,從而做出一個最優的執行計劃是非常值得的。 實際上在這樣的環境中,硬分析消耗的資源幾乎是可以忽略的。但是 取樣的資料塊有限,對於海量資料的表,結果難免有偏差。 所以一般在OLAP 或者資料倉儲環境中,將動態取樣的level 設定為3或者4比較好 。
2、 動態取樣需要額外的消耗資料庫資源,所以,如果 SQL被反覆執行,變數被繫結,硬分析很少,在這樣一個環境中,是不宜使用動態取樣的。 動態取樣發生在硬分析時,如果很少有硬分析發生,動態取樣的意義就不大。 當然如果沒有使用繫結變數,導致頻繁進行硬解析和動態取樣消耗過多資源也是不可行的,故OLTP系統非常不適宜使用動態取樣。
偶然發現rownum上加限定條件後,執行計劃雖然是全表掃描,但是實際上卻是隻掃描了滿足條件的行所在的資料塊,也就是沒有做全表掃描。
問題可以假設性描述為一條select語句的全表掃描,結果是1000行,加上rownum<=500之後,oracle是掃描了前500行所在的塊,還是掃描了1000行後取前500行(前者效能要更好)。
構造資料進行探究。探究過程中影響的因素有
1.引數db_file_multiblock_read_count,關於該參詳情可以讀閱http://blog.itpub.net/30174570/viewspace-2140241/。
2.動態取樣。
作業系統資訊:
-
[oracle@oracle ~]$ uname -a
-
Linux oracle.example.com 2.6.32-431.el6.x86_64 #1 SMP Sun Nov 10 22:19:54 EST 2013 x86_64 x86_64 x86_64 GNU/Linux
-
[oracle@oracle ~]$ lsb_release -a
-
LSB Version: :base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch
-
Distributor ID: RedHatEnterpriseServer
-
Description: Red Hat Enterprise Linux Server release 6.5 (Santiago)
-
Release: 6.5
- Codename: Santiago
資料庫版本:
-
SYS@proc> select * from v$version where rownum<=1;
-
-
BANNER
-
--------------------------------------------------------------------------------
- Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
構造測試資料:
-
SYS@proc> drop table test purge;
-
-
Table dropped.
-
-
SYS@proc> create table test tablespace test as select * from dba_objects where rownum<=1500;
-
-
Table created.
-
-
SYS@proc> select dbms_rowid.rowid_row_number(rowid) row# from test where rownum<=10;
-
-
ROW#
-
----------
-
0
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
-
10 rows selected.
-
-
-
SYS@proc> select dbms_rowid.rowid_block_number(rowid) block#,min(rownum),max(rownum) from test group by dbms_rowid.rowid_block_number(rowid) order by dbms_rowid.rowid_block_number(rowid);
-
-
BLOCK# MIN(ROWNUM) MAX(ROWNUM)
-
---------- ----------- -----------
-
139 1 88
-
140 89 171
-
141 172 251
-
142 252 329
-
143 330 407
-
144 408 487
-
145 488 567
-
146 568 646
-
147 647 724
-
148 725 798
-
149 799 873
-
-
BLOCK# MIN(ROWNUM) MAX(ROWNUM)
-
---------- ----------- -----------
-
150 874 946
-
151 947 1022
-
161 1023 1104
-
162 1105 1179
-
163 1180 1261
-
164 1262 1342
-
165 1343 1418
-
166 1419 1496
-
167 1497 1500
-
-
20 rows selected.
-
-
SYS@proc> select EXTENT_ID,FILE_ID,BLOCK_ID,BLOCKS from dba_extents where owner='SYS' and SEGMENT_NAME='TEST';
-
-
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
-
---------- ---------- ---------- ----------
-
0 6 136 8 --136 137 138 139 140 141 142 143
-
1 6 144 8 --144 145 146 147 148 149 150 151
-
2 6 160 8 --160 161 162 163 164 165 166 167
實驗過程:
說明:由於該過程是做的全表掃描下對rownum加限定詞後,掃描表的塊是否發生減少,為了避免db_file_multiblock_read_count的影響,將其值設定為1。
-
SYS@proc> show parameter multiblock
-
-
NAME TYPE VALUE
-
------------------------------------ ----------- ------------------------------
- db_file_multiblock_read_count integer 8
-
-
SYS@proc> alter system set db_file_multiblock_read_count=1;
-
- System altered.
-
SYS@proc> alter system flush buffer_cache;
-
-
System altered.
-
-
SYS@proc> alter system flush shared_pool;
-
-
System altered.
-
-
SYS@proc> select file#,dbablk,state from x$bh where obj=(select data_object_id from dba_objects where owner='SYS' and object_name='TEST') and state<>0 order by dbablk;
-
-
no rows selected
-
-
SYS@proc> select count(*) from test where rownum<=171; --只讀前兩個塊
-
-
COUNT(*)
-
----------
-
171
-
-
SYS@proc> set pagesize 9999
-
SYS@proc> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));
-
-
PLAN_TABLE_OUTPUT
-
------------------------------------------------------------------------------------
-
SQL_ID 5h6qpq1adpkh8, child number 0
-
-------------------------------------
-
select count(*) from test where rownum<=171
-
-
Plan hash value: 827909369
-
-
--------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-
--------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | | 25 (100)| |
-
| 1 | SORT AGGREGATE | | 1 | | |
-
|* 2 | COUNT STOPKEY | | | | |
-
| 3 | TABLE ACCESS FULL| TEST | 1500 | 25 (0) | 00:00:01 |
-
--------------------------------------------------------------------
- ...省略部分內容...
-
-
49 rows selected.
-
-
SYS@proc> select file#,dbablk,state from x$bh where obj=(select data_object_id from dba_objects where owner='SYS' and object_name='TEST') and state<>0 order by dbablk;
-
-
FILE# DBABLK STATE
-
---------- ---------- ----------
-
6 138 3
-
6 138 3
-
6 138 1
-
6 139 1
-
6 140 1
-
6 141 1
-
6 142 1
-
6 143 1
-
6 144 1
-
6 145 1
-
6 146 1
-
-
FILE# DBABLK STATE
-
---------- ---------- ----------
-
6 147 1
-
6 148 1
-
6 149 1
-
6 150 1
-
6 151 1
-
6 161 1
-
6 162 1
-
6 163 1
-
6 164 1
-
6 165 1
-
6 166 1
-
-
FILE# DBABLK STATE
-
---------- ---------- ----------
-
6 167 1
-
- 23 rows selected.
select count(*) from test where rownum<=171只讀取了前兩個塊的內容,檢視該語句執行計劃,從TABLE ACCESS FULL可以看出是全表掃描,而最後檢視x$bh中sys.test表的情況可以看出,表的所有塊也被讀進去buffer cache中,符合全表掃描。
不過這裡有一個很奇怪的現象,重新清空buffer cache之後,繼續剛剛的動作,情況就不同了。
-
SYS@proc> alter system flush buffer_cache;
-
-
System altered.
-
-
SYS@proc> select file#,dbablk,state from x$bh where obj=(select data_object_id from dba_objects where owner='SYS' and object_name='TEST') and state<>0 order by dbablk;
-
-
no rows selected
-
-
SYS@proc> select count(*) from test where rownum<=171;
-
-
COUNT(*)
-
----------
-
171
-
-
SYS@proc> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));
-
-
PLAN_TABLE_OUTPUT
-
----------------------------------------------------------------------------------
-
SQL_ID 5h6qpq1adpkh8, child number 0
-
-------------------------------------
-
select count(*) from test where rownum<=171
-
-
Plan hash value: 827909369
-
-
--------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-
--------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | | 25 (100)| |
-
| 1 | SORT AGGREGATE | | 1 | | |
-
|* 2 | COUNT STOPKEY | | | | |
-
| 3 | TABLE ACCESS FULL| TEST | 1500 | 25 (0) | 00:00:01 |
-
--------------------------------------------------------------------
-
...省略部分內容...
-
-
SYS@proc> select file#,dbablk,state from x$bh where obj=(select data_object_id from dba_objects where owner='SYS' and object_name='TEST') and state<>0 order by dbablk;
-
-
FILE# DBABLK STATE
-
---------- ---------- ----------
-
6 138 1
-
6 139 1
- 6 140 1
但從結果上看,執行計劃是全表掃描,但是讀進buffer cache的塊卻只有資訊塊138號和資料塊139、140。
說明rownum<=N(N為正整數)的時候,sql語句只要找出前N條滿足條件的結果後,便不再進行讀取,也就是“前言”中假設的那個問題的前者猜測。
只不過這裡執行計劃並不準確體現出來。
但是對比前邊實驗,為什麼一開始會全部對錶的塊進行讀取,第二次開始才讀取含有滿足條件的塊??
實驗可以發現,若是對shared pool和buffer poll同時進行清空的情況,執行select count(*) from test where rownum<=171語句之後,又變成表的所有塊也被讀進去buffer cache中的情況。
看的出來,是否清空share pool是關鍵。
懷著疑問,繼續做實驗。接下去清空share pool,做10046,執行select count(*) from test where rownum<=171。
-
SYS@proc> alter system flush shared_pool;
-
-
System altered.
-
-
SYS@proc> alter system flush buffer_cache;
-
-
System altered.
-
-
SYS@proc> select file#,dbablk,state from x$bh where obj=(select data_object_id from dba_objects where owner='SYS' and object_name='TEST') and state<>0 order by dbablk;
-
-
no rows selected
-
-
SYS@proc> alter session set events '10046 trace name context forever,level 12';
-
-
Session altered.
-
-
SYS@proc> select count(*) from test where rownum<=171;
-
-
COUNT(*)
-
----------
-
171
-
-
SYS@proc> alter session set events '10046 trace name context off';
-
-
Session altered.
-
-
SYS@proc> select file#,dbablk,state from x$bh where obj=(select data_object_id from dba_objects where owner='SYS' and object_name='TEST') and state<>0 order by dbablk;
-
-
FILE# DBABLK STATE
-
---------- ---------- ----------
-
6 138 3
-
6 138 3
-
6 138 1
-
6 139 1
-
6 140 1
-
6 141 1
-
6 142 1
-
6 143 1
-
6 144 1
-
6 145 1
-
6 146 1
-
-
FILE# DBABLK STATE
-
---------- ---------- ----------
-
6 147 1
-
6 148 1
-
6 149 1
-
6 150 1
-
6 151 1
-
6 161 1
-
6 162 1
-
6 163 1
-
6 164 1
-
6 165 1
-
6 166 1
-
-
FILE# DBABLK STATE
-
---------- ---------- ----------
-
6 167 1
-
-
23 rows selected.
-
-
SYS@proc> select value from v$diag_info where name like '%De%';
-
-
VALUE
-
--------------------------------------------------------------------
- /u01/app/oracle/diag/rdbms/proc/proc/trace/proc_ora_3950.trc
-
PARSING IN CURSOR #140571851573816 len=337 dep=1 uid=0 oct=3 lid=0 tim=1482094819882124 hv=3345277572 ad='7eceddb0' sqlid='baj7tjm3q9sn4'
-
SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),0), NVL(SUM(C2),0) FROM (SELECT /*+ NO_PARALLEL("TEST") FULL("TEST") NO_PARALLEL_INDEX("TEST") */ 1 AS C1, 1 AS C2 FROM "SYS"."TEST" "TEST") SAMPLESUB
-
END OF STMT
-
PARSE #140571851573816:c=1000,e=946,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,plh=1950795681,tim=1482094819882123
-
EXEC #140571851573816:c=0,e=13,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=1950795681,tim=1482094819882174
-
WAIT #140571851573816: nam='db file sequential read' ela= 11 file#=6 block#=138 blocks=1 obj#=89299 tim=1482094819882200
-
WAIT #140571851573816: nam='db file sequential read' ela= 11 file#=6 block#=139 blocks=1 obj#=89299 tim=1482094819882256
-
WAIT #140571851573816: nam='db file sequential read' ela= 7 file#=6 block#=140 blocks=1 obj#=89299 tim=1482094819882284
-
WAIT #140571851573816: nam='db file sequential read' ela= 6 file#=6 block#=141 blocks=1 obj#=89299 tim=1482094819882305
-
WAIT #140571851573816: nam='db file sequential read' ela= 11 file#=6 block#=142 blocks=1 obj#=89299 tim=1482094819882360
-
WAIT #140571851573816: nam='db file sequential read' ela= 7 file#=6 block#=143 blocks=1 obj#=89299 tim=1482094819882397
-
WAIT #140571851573816: nam='db file sequential read' ela= 7 file#=6 block#=144 blocks=1 obj#=89299 tim=1482094819882420
-
WAIT #140571851573816: nam='db file sequential read' ela= 5 file#=6 block#=145 blocks=1 obj#=89299 tim=1482094819882439
-
WAIT #140571851573816: nam='db file sequential read' ela= 6 file#=6 block#=146 blocks=1 obj#=89299 tim=1482094819882462
-
WAIT #140571851573816: nam='db file sequential read' ela= 10 file#=6 block#=147 blocks=1 obj#=89299 tim=1482094819882569
-
WAIT #140571851573816: nam='db file sequential read' ela= 6 file#=6 block#=148 blocks=1 obj#=89299 tim=1482094819882602
-
WAIT #140571851573816: nam='db file sequential read' ela= 9 file#=6 block#=149 blocks=1 obj#=89299 tim=1482094819882624
-
WAIT #140571851573816: nam='db file sequential read' ela= 6 file#=6 block#=150 blocks=1 obj#=89299 tim=1482094819882647
-
WAIT #140571851573816: nam='db file sequential read' ela= 5 file#=6 block#=151 blocks=1 obj#=89299 tim=1482094819882667
-
WAIT #140571851573816: nam='db file sequential read' ela= 6 file#=6 block#=161 blocks=1 obj#=89299 tim=1482094819882687
-
WAIT #140571851573816: nam='db file sequential read' ela= 6 file#=6 block#=162 blocks=1 obj#=89299 tim=1482094819882705
-
WAIT #140571851573816: nam='db file sequential read' ela= 5 file#=6 block#=163 blocks=1 obj#=89299 tim=1482094819882723
-
WAIT #140571851573816: nam='db file sequential read' ela= 5 file#=6 block#=164 blocks=1 obj#=89299 tim=1482094819882741
-
WAIT #140571851573816: nam='db file sequential read' ela= 7 file#=6 block#=165 blocks=1 obj#=89299 tim=1482094819882761
-
WAIT #140571851573816: nam='db file sequential read' ela= 56 file#=6 block#=166 blocks=1 obj#=89299 tim=1482094819882829
-
WAIT #140571851573816: nam='db file sequential read' ela= 8 file#=6 block#=167 blocks=1 obj#=89299 tim=1482094819882868
-
FETCH #140571851573816:c=1000,e=702,p=21,cr=23,cu=0,mis=0,r=1,dep=1,og=1,plh=1950795681,tim=1482094819882883
-
STAT #140571851573816 id=1 cnt=1 pid=0 pos=1 obj=0 op='SORT AGGREGATE (cr=23 pr=21 pw=0 time=701 us)'
-
STAT #140571851573816 id=2 cnt=1500 pid=1 pos=1 obj=89299 op='TABLE ACCESS FULL TEST (cr=23 pr=21 pw=0 time=335 us cost=25 size=0 card=1961)'
-
CLOSE #140571851573816:c=0,e=5,dep=1,type=1,tim=1482094819882937
-
PARSE #140571851573816:c=0,e=12,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=1950795681,tim=1482094819883042
-
EXEC #140571851573816:c=0,e=11,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=1950795681,tim=1482094819883066
-
FETCH #140571851573816:c=0,e=129,p=0,cr=23,cu=0,mis=0,r=1,dep=1,og=1,plh=1950795681,tim=1482094819883204
-
CLOSE #140571851573816:c=0,e=2,dep=1,type=3,tim=1482094819883233
-
=====================
-
PARSING IN CURSOR #140571852646608 len=44 dep=0 uid=0 oct=3 lid=0 tim=1482094819883659 hv=1297676880 ad='7ecc7130' sqlid='82jwkqt6pjykh'
-
select count(*) from test where rownum<=171
-
END OF STMT
-
PARSE #140571852646608:c=44993,e=46883,p=26,cr=68,cu=0,mis=1,r=0,dep=0,og=1,plh=827909369,tim=1482094819883659
-
EXEC #140571852646608:c=0,e=38,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=827909369,tim=1482094819883756
-
WAIT #140571852646608: nam='SQL*Net message to client' ela= 5 driver id=1650815232 #bytes=1 p3=0 obj#=89299 tim=1482094819883899
-
FETCH #140571852646608:c=0,e=72,p=0,cr=5,cu=0,mis=0,r=1,dep=0,og=1,plh=827909369,tim=1482094819884012
-
STAT #140571852646608 id=1 cnt=1 pid=0 pos=1 obj=0 op='SORT AGGREGATE (cr=5 pr=0 pw=0 time=73 us)'
-
STAT #140571852646608 id=2 cnt=171 pid=1 pos=1 obj=0 op='COUNT STOPKEY (cr=5 pr=0 pw=0 time=119 us)'
-
STAT #140571852646608 id=3 cnt=171 pid=2 pos=1 obj=89299 op='TABLE ACCESS FULL TEST (cr=5 pr=0 pw=0 time=29 us cost=25 size=0 card=1500)'
-
WAIT #140571852646608: nam='SQL*Net message from client' ela= 442 driver id=1650815232 #bytes=1 p3=0 obj#=89299 tim=1482094819884516
-
FETCH #140571852646608:c=0,e=1,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,plh=827909369,tim=1482094819884553
- WAIT #140571852646608: nam='SQL*Net message to client' ela= 1 driver id=1650815232 #bytes=1 p3=0 obj#=89299 tim=1482094819884570
看上邊trace部分可以知道,select count(*) from test where rownum<=171語句已經沒有涉及到物理io的讀取了,原因是因為SQL語句“SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),0), NVL(SUM(C2),0) FROM (SELECT /*+ NO_PARALLEL("TEST") FULL("TEST") NO_PARALLEL_INDEX("TEST") */ 1 AS C1, 1 AS C2 FROM "SYS"."TEST" "TEST") SAMPLESUB”已經把表test所有的塊讀進buffer cache裡邊了。
這個SQL語句是oracle內部的遞迴sql,去掉相關hint簡化如下:
SELECT NVL(SUM(C1),0), NVL(SUM(C2),0) FROM (SELECT 1 AS C1, 1 AS C2 FROM "SYS"."TEST" "TEST") SAMPLESUB,可以看到這裡已經對test表進行了訪問,而且從trace看是全表掃描。
因此我們知道了實際上所有的塊被讀進去buffer cache裡邊,並非select count(*) from test where rownum<=171語句引起的,也就是rownum<=N(N為正整數)的時候,sql語句只要找出前N條滿足條件的結果後,便不再進行讀取,這是oracle對rownum的優化。
但是由此引出另外的疑問,該遞迴sql的產生只有在第一次執行select count(*) from test where rownum<=171(share pool無此sql執行計劃)的時候才會產生,第二次執行做10046可以知道沒有該遞迴sql了。
問題出來總是要解決的,所幸還是可以找到相關資料的。
原因在於動態取樣,這裡引用網上對於動態取樣的一些描述:
動態取樣(Dynamic Sampling)技術的最初提出是在Oracle 9i R2,在段(表,索引,分割槽)沒有分析的情況下,為了使CBO 優化器得到足夠的資訊以保證做出正確的執行計劃而發明的一種技術,可以把它看做分析手段的一種補充。
當段物件沒有統計資訊時(即沒有做分析),動態取樣技術可以通過直接從需要分析的物件上收集資料塊(取樣)來獲得CBO需要的統計資訊。
顯然,遞迴sql是由於test表上無統計資訊而執行select count(*) from test where rownum<=171語句時後臺做動態取樣產生的。
下邊通過一些手段,在test表上沒有統計資訊的情況下,強制不做動態取樣。
-
SYS@proc> alter system flush buffer_cache;
-
-
System altered.
-
-
SYS@proc> alter system flush shared_pool;
-
-
System altered.
-
-
SYS@proc> select file#,dbablk,state from x$bh where obj=(select data_object_id from dba_objects where owner='SYS' and object_name='TEST') and state<>0 order by dbablk;
-
-
no rows selected
-
-
SYS@proc> set autotrace on
-
SYS@proc> select /*+ dynamic_sampling(test 0) */ count(*) from test where rownum<=171;
-
-
COUNT(*)
-
----------
-
171
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 827909369
-
-
--------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-
--------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | 25 (0) | 00:00:01 |
-
| 1 | SORT AGGREGATE | | 1 | | |
-
|* 2 | COUNT STOPKEY | | | | |
-
| 3 | TABLE ACCESS FULL| TEST | 1961 | 25 (0) | 00:00:01 |
-
--------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
2 - filter(ROWNUM<=171)
-
-
-
Statistics
-
----------------------------------------------------------
-
21 recursive calls
-
0 db block gets
-
23 consistent gets
-
8 physical reads
-
0 redo size
-
527 bytes sent via SQL*Net to client
-
523 bytes received via SQL*Net from client
-
2 SQL*Net roundtrips to/from client
-
3 sorts (memory)
-
0 sorts (disk)
-
1 rows processed
-
-
SYS@proc> select file#,dbablk,state from x$bh where obj=(select data_object_id from dba_objects where owner='SYS' and object_name='TEST') and state<>0 order by dbablk;
-
-
FILE# DBABLK STATE
-
---------- ---------- ----------
-
6 138 1
-
6 138 3
-
6 139 1
- 6 140 1
從動態取樣產生的原因上看,是因為test表沒有統計資訊,我們手動收集統計資訊,那麼即使不用hint去禁用動態取樣,也應該不會將所有的塊緩衝進去buffer cache裡邊。是否如此?
-
SYS@proc> analyze table test compute statistics;
-
-
Table analyzed.
-
-
SYS@proc> alter system flush buffer_cache;
-
-
System altered.
-
-
SYS@proc> alter system flush shared_pool;
-
-
System altered.
-
-
SYS@proc> select file#,dbablk,state from x$bh where obj=(select data_object_id from dba_objects where owner='SYS' and object_name='TEST') and state<>0 order by dbablk;
-
-
no rows selected
-
-
SYS@proc> select count(*) from test where rownum<=171;
-
-
COUNT(*)
-
----------
-
171
-
-
SYS@proc> select file#,dbablk,state from x$bh where obj=(select data_object_id from dba_objects where owner='SYS' and object_name='TEST') and state<>0 order by dbablk;
-
-
FILE# DBABLK STATE
-
---------- ---------- ----------
-
6 138 1
-
6 139 1
- 6 140 1
關於動態取樣,這裡就不深入了。
對於rownum的結論,這裡附上一個實驗資料做效能對比吧!
-
SYS@proc> create table t1 as select * from dba_objects;
-
-
Table created.
-
-
SYS@proc>
-
SYS@proc>
-
SYS@proc>
-
SYS@proc> analyze table t1 compute statistics;
-
-
Table analyzed.
-
-
SYS@proc> set autotrace traceonly
-
-
SYS@proc> select * from t1;
-
-
86997 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 3617692013
-
-
--------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 86997 | 8580K | 1243 (1) | 00:00:15|
-
| 1 | TABLE ACCESS FULL| T1 | 86997 | 8580K | 1243 (1) | 00:00:15|
-
--------------------------------------------------------------------------
-
-
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
6957 consistent gets
-
0 physical reads
-
0 redo size
-
10000967 bytes sent via SQL*Net to client
-
64312 bytes received via SQL*Net from client
-
5801 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
86997 rows processed
-
-
-
SYS@proc> select * from t1 where rownum<=1;
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 3836375644
-
-
-------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------
-
| 0 | SELECT STATEMENT | | 1 | 101 | 2 (0) | 00:00:01 |
-
|* 1 | COUNT STOPKEY | | | | | |
-
| 2 | TABLE ACCESS FULL| T1 | 1 | 101 | 2 (0) | 00:00:01 |
-
-------------------------------------------------------------------------
-
-
Predicate Information (identified by operation id):
-
---------------------------------------------------
-
-
1 - filter(ROWNUM<=1)
-
-
-
Statistics
-
----------------------------------------------------------
-
0 recursive calls
-
0 db block gets
-
3 consistent gets
-
0 physical reads
-
0 redo size
-
1608 bytes sent via SQL*Net to client
-
523 bytes received via SQL*Net from client
-
2 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
- 1 rows processed
附上動態取樣相關資料:
動態取樣的作用
1、CBO 依賴的是充分的統計分析資訊,但是並不是每個使用者都會非常認真,及時地去對每個表做分析。為了保證執行計劃都儘可能地正確,Oracle需要使用動態取樣技術來幫助CBO 獲取儘可能多的資訊。
2、全域性臨時表。通常來講,臨時表的資料是不做分析的,因為它存放的資料是臨時性的,可能很快就釋放了,但是當一個查詢關聯到這樣的臨時表時,CBO要想獲得臨時表上的統計資訊分析資料,就只能依賴於動態取樣。
3、動態取樣除了可以在段物件沒有分析時,給CBO提供分析資料之外,還有一個獨特的能力,它可以對不同列之間的相關性做統計。這點通常發生在表設計不符合3NF的情況下,這個特性在表 符合3NF設計的 情況下少見。
儘管看到動態取樣的優點,但是它的缺點也是顯而易見,否則Oracle一定會一直使用動態取樣來取代資料分析:
1、 在OLAP或者資料倉儲環境下,SQL執行消耗的資源要遠遠大於SQL解析,那麼讓解析在消耗多一點資源做一些動態取樣分析,從而做出一個最優的執行計劃是非常值得的。 實際上在這樣的環境中,硬分析消耗的資源幾乎是可以忽略的。但是 取樣的資料塊有限,對於海量資料的表,結果難免有偏差。 所以一般在OLAP 或者資料倉儲環境中,將動態取樣的level 設定為3或者4比較好 。
2、 動態取樣需要額外的消耗資料庫資源,所以,如果 SQL被反覆執行,變數被繫結,硬分析很少,在這樣一個環境中,是不宜使用動態取樣的。 動態取樣發生在硬分析時,如果很少有硬分析發生,動態取樣的意義就不大。 當然如果沒有使用繫結變數,導致頻繁進行硬解析和動態取樣消耗過多資源也是不可行的,故OLTP系統非常不適宜使用動態取樣。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30174570/viewspace-2140240/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【Oracle】-【ROWNUM與索引】-索引對ROWNUM檢索的影響Oracle索引
- stopkey對索引掃描的影響測試TopK索引
- 【Oracle】 索引的掃描方式Oracle索引
- Oracle中存取資料掃描Table及索引的方式(全表掃描,索引掃描等)Oracle索引
- PostgreSQL DBA(55) - MVCC#8(對全表掃描的影響)SQLMVCC#
- 【oracle】index的幾種掃描方式OracleIndex
- Oracle中ASSM模式下,全表掃描的L3塊的邏輯讀的影響OracleSSM模式
- Oracle中的rownumOracle
- INDEX建立方式對SQL的影響IndexSQL
- 存取Oracle當中掃描資料的方法Oracle
- MYSQL 中的GROUP BY 的方式 (1)(loose index scan鬆散掃描 tight index scan緊湊掃描)MySqlIndex
- 【 Oracle中rownum的用法 】Oracle
- MySQL中的全表掃描和索引樹掃描MySql索引
- 【Oracle】-【COMMIT對索引的影響】-從trace看COMMIT對索引的影響OracleMIT索引
- 深入探討!Batch 大小對訓練的影響BAT
- oracle 全表掃描,索引範圍掃描與塊的理解Oracle索引
- Oracle中的Rownum 欄位Oracle
- Oracle的執行計劃及資料存取方式 (ROWID,掃描方式等)Oracle
- [zt] SQL存取Oracle當中掃描資料的方法SQLOracle
- 影像Resize方式對深度學習模型效果的影響深度學習模型
- 修改系統時間對oracle的影響Oracle
- Oracle主鍵選擇對插入的影響Oracle
- oracle的rownumOracle
- GPFS Persistent Reserve 的設定對Oracle RAC 的影響Oracle
- 【恩墨學院】深入解讀Oracle 18c對於DBA的影響及應對措施Oracle
- 修改主機時區對Oracle的影響分析Oracle
- oracle點陣圖索引對DML操作的影響Oracle索引
- 磁碟排序對Oracle資料庫效能的影響排序Oracle資料庫
- Oracle深入Undo探究Oracle
- 小記: 關於CSS display 屬性對錶格語義的影響CSS
- 對上次的自動掃描進行改造
- 掃描器在工作中的使用
- 解讀Oracle 索引掃描Oracle索引
- Index的掃描方式:index full scan/index fast full scanIndexAST
- 新特性:/dev/shm對Oracle 11g的影響devOracle
- 【Dataguard】Oracle多租戶環境對Dataguard的影響Oracle
- 磁碟排序對Oracle資料庫效能的影響PT排序Oracle資料庫
- oracle cardinality對於執行計劃的影響Oracle