目标SQL所需要访问的数据一般存储在表,而Oracle访问表中数据的方法有两种:一种是直接访问表;另一种是先访问索引,再回表(当然,如果目标SQL所访问的数据只通过访问相关的索引就可以得到,那么此时就不需要再回表了)。
3.1访问表的方法
Oracle数据库中直接访问表中数据的方法有两种:一种是全表扫描;另一种是ROWID扫描。
3.1.1 全表扫描
全表扫描是指Oracle在访问目标表里的数据时,会从该表所占用的第一个区(EXTENT)的第一个块(BLOCK)开始扫描,直接扫描到该表的高水位线(HWM,High Water Mark),这段范围内所有的数据块Oracle都必须读到。当然,Oracle会对这期间读到的所有数据施加目标SQL的where条件中指定的过滤条件,最后只返回那些满足过滤条件的数据。
不是说全表扫描不好,事实上Oracle在做全表扫描操作时会使用多块读,这在目标表的数据不大时执行效率是非常高的,但全表扫描最大的问题就在于走全表扫描的目标SQL执行时间会不稳定、不可控,这个执行时间一定会随着目标表数据量的递增而递增。因为随着目标表数据量的递增,它的高水位线会一直不段往上涨,所以全表扫描时所需要读取的数据块的数据也会不断增加。
在Oracle中如果对目标表不停地插入数据,当分配给该表的现有空间不足时高水位线就会向上移动,但如果你用DELETE语句从该表删除数据,则高水位线并不会随之往下移动。高水位线这种特性所带来的副作用是,即使使用DELETE删光了目标表中的所有数据,高水位线还是会在原来的位置,这意味着全表扫描该表时Oacle还是需要扫描该表高水位线下所有的数据块,此时对该表的全表扫描操作耗费的时间与之前相比并不会有明显的改观。
3.1.2 ROWID扫描
ROWID扫描是指Oracle在访问目标表里的数据时,直接通过数据所在的ROWID去定位并访问这些数据。ROWID表示的是Oracle中的数据行记录所在的物理存储地址,也就是说ROWID实际上是和Oracle数据块里的行记录一一对应的。
既然ROWID代表的就是表的数据行所在的物理存储地址,那么当Oracle知道待访问的数据行所在的ROWID后,自然就可以根据该RWOID去直接访问对应表的相关数据行,这就是ROWID扫描的含义。
从严格意义上来说,Oracle中的ROWID扫描有两层含义:一种是根据用户在SQL语句中输入的ROWID的值去直接访问对应的数据行记录;另外一种方法是先去访问相关的索引,然后根据访问索引后得到的ROWID再回表去访问对应的数据行。
对Oracle中的堆表而言,我们可以通过Oracle内置的ROWID伪列得到对应行记录所在的ROWID值,然后还可以通过DBMS_ROWID包中的相关方法将ROWID伪列的值翻译成对应数据行的实际物理存储地址。
3.2 访问索引的方法
这里提到的索引是指最常用的B*Tree索引
Oracle数据库的的B*Tree索引就好像一棵倒长的树,它包含两种类型的数据块,一种是索引分支块,另一种是索引叶子块。
索引分支块包含指向相应索引分支块/叶子块的指针和索引键值列(这里的指针是指相关分支块/叶子块的块地址RDBA。每个索引分支块都会有两种类型的指针,一种是lmc,另一种是索引分支块的索引行记录所记录的指针。lmc是Left Most Child的缩写,每个索引分支块都只有一个lmc,这个lmc指向的分支块/叶子块中的所有索引键值列中的最大值一定小于该lmc所在索引分支块的所有索引键值列中的最小值;而索引分支块的索引行记录所记录的指针所指向的分支块/叶子块的所有索引键值列中的最小值一定大于或等于该行记录的索引键值列的值)。这个索引列值不一定就是完整的被索引键值,它可能只是被索引键值的前缀,只要Oracle能通过这些前缀区分相应的索引分支块/叶子块就行,这样Oracle就能够既节省分支块的存储空间,又可以快速定位其下层的索引分支块/叶子块。索引分支块最上层的那个块就是所谓的索引根节点。在Oracle里访问B*Tree索引的操作都必须从根节点开始,即都会经历一个从根节点到分支块再到叶子块的过程。
索引叶子块包含被索引键值和用于定位该索引键值所在的数据行在表中实际物理存储位置的ROWID。对于唯一性的B*Tree索引而言,ROWID是存储在索引行的行头,所以此时Oracle并不需要额外礁该ROWID的长度。而对于非唯一性的B*Tree索引而言,ROWID被当作额外的列与被索引的键值列一起存储,所以此时Oracle既要存储ROWID,同时又要存储其长度,这意味着在同等条件下,唯一性B*Tree索引要比非唯一性B*Tree索引节省索引叶子块的存储空间。对于非唯一性索引而言,B*Tree索引的有序性体现在Oralce会按照被索引键值和相应的ROWID来联合排序。Oralce里的索引叶子块是左右互联的,即相当于有一个双向指针链表把这些索引叶子块互相连接在了一起。
3.2.1 索引唯一性扫描