无论执行多少次select * from tab,它的结果总是一致的(不考虑期间表有数据变化)。
无论是请不清理内存,无论是磁盘读还是直接从内存中读取,结果总是一致。
所以为什么它的结果是“有序”的,是按什么方式“有序”。
回答上面的问题我们要了解2个概念
1.全表扫描。select * from tab是全表扫描,全表扫描是从该segment的第一个extent的第一个block开始扫描,一直扫描到表的高水位。
2.rowid。rowid是已文件号,块号,行号来标志数据行的。rowid的递增参考我之前的文章https://blog.csdn.net/qq_40687433/article/details/78466488
所以,如果是全表扫描,那么是不是一定是以rowid的顺序扫描?
3.多块读。单块读,多块读可参考我之前的文章https://blog.csdn.net/qq_40687433/article/details/82180638
多块读一次从磁盘读取多个块到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
------------------
AAAGTsAABAAAQuBAAA
1 row selected.
SQL> delete from testr;
1 row deleted.
SQL> insert into testr values(2);
1 row created.
SQL> select rowid from testr;
ROWID
------------------
AAAGTsAABAAAQuBAAB
1 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
------------------
AAAGTsAABAAAQuBAAA
1 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跳跃请参考https://blog.csdn.net/qq_40687433/article/details/88868112)
其实上面的实验并不影响我们刚才设想的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.如果说多块读这么的无序,结果为何如此的有序呢