第一次看CONCEPT的时候,一直不是很明白,Oracle索引组织表中逻辑ROWID的物理猜是如何实现的,而这次看的时候很自然的就想明白其中的实现。
索引组织表中逻辑ROWID的物理猜:http://yangtingkun.itpub.net/post/468/477286
通过一个具体的例子简单描述一下物理猜的过程。
可能有人对这个概念不是很理解,通过一个小例子,说明一下索引组织表如何根据第二索引来访问数据的。
SQL> CREATE TABLE T_INDEX_ORG
2 (ID NUMBER PRIMARY KEY,
3 NAME VARCHAR2(30),
4 TYPE VARCHAR2(18))
5 ORGANIZATION INDEX;
表已创建。
SQL> CREATE INDEX IND_INDEX_ORG_NAME ON T_INDEX_ORG (NAME);
索引已创建。
SQL> INSERT INTO T_INDEX_ORG VALUES (0, 'FIRST', 'TEST');
已创建 1 行。
SQL> INSERT INTO T_INDEX_ORG
2 SELECT ROWNUM, OBJECT_NAME, OBJECT_TYPE
3 FROM DBA_OBJECTS
4 WHERE ROWNUM < 1000;
已创建999行。
SQL> COMMIT;
提交完成。
建立了一个索引组织表,并插入一些记录。每条记录的插入的同时会将当时的物理位置存储在逻辑ROWID中,作为物理猜的基础。对于第一条插入的语句而言,随后的插入会导致索引组织表不断进行调整,因此在后一个插入完成时,第一次插入的记录已经不在开始的位置,这时物理猜就不会准确,而会导致额外的主键扫描。而第二次插入的记录,逻辑ROWID记录的位置就是这条记录的真实位置,因此不会导致物理猜,下面看看二者访问时逻辑读是否有差异:
SQL> SELECT * FROM T_INDEX_ORG WHERE ID = 0;
ID NAME TYPE
---------- ------------------------------ ------------------
0 FIRST TEST
SQL> SELECT * FROM T_INDEX_ORG WHERE ID = 999;
ID NAME TYPE
---------- ------------------------------ ------------------
999 V$SORT_SEGMENT SYNONYM
SQL> SET AUTOT ON
SQL> SELECT * FROM T_INDEX_ORG
2 WHERE NAME = 'FIRST';
ID NAME TYPE
---------- ------------------------------ ------------------
0 FIRST TEST
执行计划
----------------------------------------------------------
0 SELECT STATEMENT ptimizer=CHOOSE (Cost=3 Card=1 Bytes=41)
1 0 INDEX (UNIQUE SCAN) OF 'SYS_IOT_TOP_32243' (UNIQUE) (Cost=1 Card=1 Bytes=41)
2 1 INDEX (RANGE SCAN) OF 'IND_INDEX_ORG_NAME' (NON-UNIQUE) (Cost=1 Card=1)
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
484 bytes sent via SQL*Net to client
372 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> SELECT * FROM T_INDEX_ORG
2 WHERE NAME = 'V$SORT_SEGMENT';
ID NAME TYPE
---------- ------------------------------ ------------------
999 V$SORT_SEGMENT SYNONYM
执行计划
----------------------------------------------------------
0 SELECT STATEMENT ptimizer=CHOOSE (Cost=3 Card=1 Bytes=41)
1 0 INDEX (UNIQUE SCAN) OF 'SYS_IOT_TOP_32243' (UNIQUE) (Cost=1 Card=1 Bytes=41)
2 1 INDEX (RANGE SCAN) OF 'IND_INDEX_ORG_NAME' (NON-UNIQUE) (Cost=1 Card=1)
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
4 consistent gets
0 physical reads
0 redo size
498 bytes sent via SQL*Net to client
372 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
可以看到,虽然二者的执行计划完全一样,但是物理猜没有命中,导致额外的主键扫描会增加逻辑IO,这个例子就增加了2次逻辑IO。
SQL> SELECT ROWID, ID, NAME FROM T_INDEX_ORG
2 WHERE NAME IN ('FIRST', 'V$SORT_SEGMENT');
ROWID ID NAME
----------------------------------------- ---------- ------------------------------
*BAJAABwBgP4 0 FIRST
*BAJAAB4Dwgpk/g 999 V$SORT_SEGMENT
执行计划
----------------------------------------------------------
0 SELECT STATEMENT ptimizer=CHOOSE (Cost=2 Card=1 Bytes=47)
1 0 INLIST ITERATOR
2 1 INDEX (RANGE SCAN) OF 'IND_INDEX_ORG_NAME' (NON-UNIQUE) (Cost=2 Card=1 Bytes=47)
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
5 consistent gets
0 physical reads
0 redo size
557 bytes sent via SQL*Net to client
372 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2 rows processed
顺便提一句,由于索引组织表包含的ROWID信息为逻辑ROWID,而逻辑ROWID有包含了主键信息,所以上面这个包含了ROWID、主键和索引列的查询,仅通过索引扫描就可以实现,而不需要再扫描主键了。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/4227/viewspace-541559/,如需转载,请注明出处,否则将追究法律责任。