无论执行多少次select * from tab,它的结果总是一致的(不考虑期间表有数据变化)。

无论是请不清理内存,无论是磁盘读还是直接从内存中读取,结果总是一致。

所以为什么它的结果是“有序”的,是按什么方式“有序”。

 

回答上面的问题我们要了解2个概念

1.全表扫描。select * from tab是全表扫描,全表扫描是从该segment的第一个extent的第一个block开始扫描,一直扫描到表的高水位。

2.rowid。rowid是已文件号,块号,行号来标志数据行的。rowid的递增参考我之前的文章

所以,如果是全表扫描,那么是不是一定是以rowid的顺序扫描?

3.多块读。单块读,多块读可参考我之前的文章

多块读一次从磁盘读取多个块到buffer cache中,且buffer不连续,块必须是scattered(零散地)分布在内存中。

这好像会让结果趋于“无序”,因为每次都是零散的读,不连续的。

 

实验一:

1.rowid是否可用被重用

第一部分,删除一条记录后,不提交,插入一条数据,那么改数据应该算第一行,那么rowid是否重用呢?

SQL> create table testr(col1 number);
Table created.
SQL> insert into testr values(1);
1 row created.
SQL> commit;
Commit complete.
SQL> select rowid from testr;
ROWID
 ------------------
 AAAGTsAABAAAQuBAAA1 row selected.
SQL> delete from testr;
1 row deleted.
SQL> insert into testr values(2);
1 row created.
SQL>  select rowid from testr;
ROWID
 ------------------
 AAAGTsAABAAAQuBAAB1 row selected.

rowid是AAB,是第二行。其实没有提交的话,改记录并没有被标注为失效的,记录仍然存在,所以新插入数据当然不能覆盖之前的记录。

第二部分,commit后,rowid是否重用

SQL> delete from testr;
1 row deleted.
SQL> commit;
Commit complete.
SQL> insert into testr values(3);
1 row created.
SQL> commit;
Commit complete.
SQL> select rowid from testr;
ROWID
 ------------------
 AAAGTsAABAAAQuBAAA1 row selected.

新插入的数据,在这个块里面,确实属于第一行。所以rowid被重用.

番外测试:

SQL> insert  into testr values(4);
1 row created.
SQL> commit;
Commit complete.
SQL> select rowid,dbms_rowid.rowid_row_number(rowid) from testr;
ROWID              DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)
 ------------------ ----------------------------------
 AAAGTsAABAAAQuBAAA                                  0
 AAAGTsAABAAAQuBAAC                                  2

testr中只有2行,第二行不是AAB而是AAC。所以rowid中的行号并不等于记录行号(为什么会跳过1呢,先放在这,后面再做trace研究)

(关于rowid跳跃请参考)

 

其实上面的实验并不影响我们刚才设想的rowid排序的预期结果。因为rowid本身是递增的。

 

实验二:

通过创建大表,通过select * from tab对比select * from tab order by rowid是否有差异,来验证我们之前的疑问。

脚本:

[oracle@cs-db htz]$ cat rowid_test.sh
 sqlplus / as sysdba<<!
 alter system flush buffer_cache;
 set lin 200;
 set echo off;
 SET SERVEROUTPUT off;
 set feedback off;
 select rowid,object_name from tab1_rowid;
 !
 [oracle@cs-db htz]$ cat rowid_test2.sh
 sqlplus / as sysdba<<!
 alter system flush buffer_cache;
 set lin 200;
 set echo off;
 SET SERVEROUTPUT off;
 set feedback off;
 select rowid,object_name from tab1_rowid order by rowid;
 !

执行sh rowid_test.sh>aa.txt ,

然后alter system flush buffer_cache刷新buffer cache,让oracle再去走dbfile scatter read(可以开10046,确保2个查询都是走的多块读)

执行sh rowid_test2.sh >>bb.txt

执行diff aa.txt bb.txt  没有任何输出,说明结果是一致的。说明select * from tab的结果是按rowid排序的。

 

实验结果已经出来了。事实胜于雄辩,select * from tab的结果是按rowid排序的。

但是我仍然有些疑问

1.我们考虑的环境太少。从select * from tab的执行到返回结果到客户端。我们只考虑了oracle怎么把数据读到buffer cache,并没有考虑怎么把数据传递到pga,更没有考虑到pga返回到客户端进程。

2.如果说多块读这么的无序,结果为何如此的有序呢