在索引設計的沒問題的前提下,兩種方式在前幾頁性能上接近,越向後翻rowid的效果越明顯。
注意紅字部分,紅字部分一定成立嗎?實驗說明一切。
我們先來看看11g的情況,建立實驗環境:
11gR2 >create table test(id number,status VARCHAR2(7),type VARCHAR2(19),created date);
Table created.
11gR2 >insert into test select OBJECT_ID,STATUS,OBJECT_TYPE,CREATED from dba_objects;
12926 rows created.
11gR2 >alter table test modify created not null;
Table altered.
11gR2 >create index test_ind1 on test(CREATED);
Index created.
11gR2 >ANALYZE TABLE TEST compute statistics;
Table analyzed.
測試11g的rownum分頁:
11gR2 >select *
from (
select rownum rn,t.*
from
(select id,status,type,created from test order by created) t
where rownum<1000)
where rn >900;
99 rows selected.
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 999 | 50949 | 9 (0)| 00:00:01 |
|* 1 | VIEW | | 999 | 50949 | 9 (0)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | VIEW | | 999 | 37962 | 9 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| TEST | 12926 | 277K| 9 (0)| 00:00:01 |
| 5 | INDEX FULL SCAN | TEST_IND1 | 999 | | 4 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
22 consistent gets
0 physical reads
0 redo size
3703 bytes sent via SQL*Net to client
590 bytes received via SQL*Net from client
8 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
99 rows processed
下面測試rowid分頁
11gR2 >select /*+ ordered use_nl(p s) */ *
from (
select rownum rn,rd
from (select rowid rd from test order by created)
t where rownum<1000) p,
test s
where rn>900 and p.rd=s.rowid; 2 3 4 5 6 7
99 rows selected.
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 999 | 52947 | 1003 (0)| 00:00:13 |
| 1 | NESTED LOOPS | | 999 | 52947 | 1003 (0)| 00:00:13 |
|* 2 | VIEW | | 999 | 24975 | 4 (0)| 00:00:01 |
|* 3 | COUNT STOPKEY | | | | | |
| 4 | VIEW | | 999 | 11988 | 4 (0)| 00:00:01 |
| 5 | INDEX FULL SCAN | TEST_IND1 | 12926 | 239K| 4 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY USER ROWID| TEST | 1 | 28 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
19 consistent gets
0 physical reads
0 redo size
5450 bytes sent via SQL*Net to client
590 bytes received via SQL*Net from client
8 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
99 rows processed
我們看可以看到,當讀900-1000行時,rowid分頁(19邏輯讀)確實是優於rownum分頁(22邏輯讀)。因爲rowid方式減少了前900行的回表。
我們來看看10g的情況。
在10g建立同樣的TEST表和索引,然後:
測試10g的rownum分頁:
10gR2 >select *
from (
select rownum rn,t.*
from
(select id,status,type,created from test order by created) t
where rownum<1000)
where rn >900;
99 rows selected.
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 999 | 50949 | 17 (0)| 00:00:01 |
|* 1 | VIEW | | 999 | 50949 | 17 (0)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | VIEW | | 999 | 37962 | 17 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| TEST | 45620 | 1113K| 17 (0)| 00:00:01 |
| 5 | INDEX FULL SCAN | TEST_IND1 | 999 | | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
19 consistent gets
0 physical reads
0 redo size
3842 bytes sent via SQL*Net to client
558 bytes received via SQL*Net from client
8 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
99 rows processed
19個邏輯讀,和11g是很接近的,是個正常值,也符合我們的預期。
繼續測試10g的rowid分頁:
swp1 >select /*+ ordered use_nl(p s) */ *
from (
select rownum rn,rd
from (select rowid rd from test order by created)
t where rownum<1000) p,
test s
where rn>900 and p.rd=s.rowid;
2 3 4 5 6 7
99 rows selected.
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 999 | 55944 | 1061 (1)| 00:00:15 |
| 1 | NESTED LOOPS | | 999 | 55944 | 1061 (1)| 00:00:15 |
|* 2 | VIEW | | 999 | 24975 | 62 (2)| 00:00:01 |
|* 3 | COUNT STOPKEY | | | | | |
| 4 | VIEW | | 45620 | 534K| 62 (2)| 00:00:01 |
| 5 | INDEX FULL SCAN | TEST_IND1 | 45620 | 846K| 62 (2)| 00:00:01 |
| 6 | TABLE ACCESS BY USER ROWID| TEST | 1 | 31 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
109 consistent gets
1 physical reads
0 redo size
5585 bytes sent via SQL*Net to client
558 bytes received via SQL*Net from client
8 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
99 rows processed
怎麼邏輯讀變成了109? 執行計劃是和11g一樣的。爲什麼會有這麼大的差別?
rowid分頁的cost一下變成了rownum方式的好幾倍。
爲什麼?
下面的實驗揭露了原因,也爲我們揭露了11g一個不爲人知的新特性:
11gR2 >select rowid,id from test where rowid in ('AAADSHAABAAAH3hAAA','AAADSHAABAAAH3hAAB','AAADSHAABAAAH3hAAC','AAADSHAABAAAH3hAAD','AAADSHAABAAAH3hAAE','AAADSHAABAAAH3hAAF','AAADSHAABAAAH3hAAG','AAADSHAABAAAH3hAAH','AAADSHAABAAAH3hAAI','AAADSHAABAAAH3hAAJ');
ROWID ID
------------------ ----------
AAADSHAABAAAH3hAAA 20
AAADSHAABAAAH3hAAB 46
AAADSHAABAAAH3hAAC 28
AAADSHAABAAAH3hAAD 15
AAADSHAABAAAH3hAAE 29
AAADSHAABAAAH3hAAF 3
AAADSHAABAAAH3hAAG 25
AAADSHAABAAAH3hAAH 41
AAADSHAABAAAH3hAAI 54
AAADSHAABAAAH3hAAJ 40
10 rows selected.
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 16 | 1 (0)| 00:00:01 |
| 1 | INLIST ITERATOR | | | | | |
| 2 | TABLE ACCESS BY USER ROWID| TEST | 1 | 16 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
2 consistent gets
0 physical reads
0 redo size
875 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10 rows processed
10gR2 >select rowid,id from test where rowid in ('AAAzo5AABAAAKnWAAA','AAAzo5AABAAAKnWAAB','AAAzo5AABAAAKnWAAC','AAAzo5AABAAAKnWAAD','AAAzo5AABAAAKnWAAE','AAAzo5AABAAAKnWAAF','AAAzo5AABAAAKnWAAG','AAAzo5AABAAAKnWAAH','AAAzo5AABAAAKnWAAI','AAAzo5AABAAAKnWAAJ');
ROWID ID
------------------ ----------
AAAzo5AABAAAKnWAAA 30
AAAzo5AABAAAKnWAAB 8
AAAzo5AABAAAKnWAAC 14
AAAzo5AABAAAKnWAAD 34
AAAzo5AABAAAKnWAAE 45
AAAzo5AABAAAKnWAAF 39
AAAzo5AABAAAKnWAAG 47
AAAzo5AABAAAKnWAAH 51
AAAzo5AABAAAKnWAAI 11
AAAzo5AABAAAKnWAAJ 48
10 rows selected.
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 16 | 1 (0)| 00:00:01 |
| 1 | INLIST ITERATOR | | | | | |
| 2 | TABLE ACCESS BY USER ROWID| TEST | 1 | 16 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
10 consistent gets
0 physical reads
0 redo size
861 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10 rows processed
我們看到,同樣用rowid讀取同一個block的10行,在11g中僅僅耗費了2個讀,而在10g中耗費10個讀。
更進一步的測試也確認了10g中,每一個rowid都會產生一個邏輯讀,即使這些rowid來自一個block,也不能重用。
而11g引入了對rowid讀的新特性, 那就是,如果rowid是來自一個block,那麼是可以重用的。
總結:
在11g中,得益於這個11g不爲人知的新特性,總體來說,rowid分頁要優於rownum。
可是在10g中,相當一部分情況下rowid方式性能是遠遠不如rownum方式的。只有在後翻的頁數非常大,並且頁的大小很小,譬如10000-10010的情況下,rowid的分頁纔會優於rownum方式。
(PS:之前發了疑問在管理版,貌似不是很多人關注,重新整理了實驗發在開發版,求討論和斧正。)
ROWID ID
------------------ ----------
AAAzo5AABAAAKnWAAA 30
AAAzo5AABAAAKnWAAB 8
AAAzo5AABAAAKnWAAC 14
AAAzo5AABAAAKnWAAD 34
AAAzo5AABAAAKnWAAE 45
AAAzo5AABAAAKnWAAF 39
AAAzo5AABAAAKnWAAG 47
AAAzo5AABAAAKnWAAH 51
AAAzo5AABAAAKnWAAI 11
AAAzo5AABAAAKnWAAJ 48
從rowid的名字就可以看出出來,一個rowid是這樣組成的:
OOOOOOFFFBBBBBBRRR,佔用10個字節(32bit+10bit rfile#+22bit+16bit)。其中,O是對象ID,F是文件ID,B是塊ID,R是行ID
所以我們可以看到以上的那些rowid,前面的對象號,文件號,塊號都是一樣的。只有行號有區別。
所以說是一個block的。