您的位置:首页 > 数据库 > Oracle

rowid 与 rownum 分页

2015-06-11 16:44 513 查看
转自http://www.itpub.net/thread-1603830-1-1.html,作者Kevin__Zhang

一直以来,看到的说法大都是rowid要强于rownum分页:

在索引设计的没问题的前提下,两种方式在前几页性能上接近,越向后翻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            |      |      
c1d5
|       |            |          |

|   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的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Oracle 分页性能