什么是全表扫描?全表扫描是一种“万能”的查询方式。任何对数据的访问需求,都可以通过全表扫描的方式解决。
在逻辑上,这种方式会读取表中的所有行,然后检查每一行是否满足语句的限制条件。物理上,这种方式会批量读取高水位线下的每个数据块。这里注意两点,一是批量读取,二是高水位线。批量读取的目的是为了减少I/O次数,提高系统的吞吐能力。
一个多块读操作可以使一次I/O能读取多块数据块(db_block_multiblock_read_count参数设定),而不是只读取一个数据块,这极大地减少了I/O总次数,提高了系统的吞吐量。所以利用多块读的方法可以十分高效地实现全表扫描,而且只有在全表扫描的情况下才能使用多块读操作。
高水位线,在前面已经提到过了,它标识着数据存放的最高点。常见的DELETE操作不会影响高水位线,只有使用TRUNCATE才会将高水位置为零。在10g以后的版本,可以通过shrink命令人工收缩高水位线。
一般情况下,全表扫描得到的数据库将放入缓冲区LRU链表的LRU端,也就是尽快被淘汰出的部分。因为Oracle认为全表扫描得到的数据应该是临时访问的,不应长期占用缓冲区。
在11g之后的版本,Oracle提供了一种新的方式来处理全表扫描,称为直接路径读取。这种方式的独特之处在于,数据块将不保存在缓冲区中,这将大大减少栓锁的使用,避免对缓冲区的冲击。当然这种方式也不是完全没有问题,因此很多系统从10g升级到11g的时候,要特别注意这个问题。
在优化器选择扫描方式时实际是在寻求一个平衡,即寻找表扫描和索引扫描的损益分界点。对于数据量比较少的表而言,全表扫描与索引扫描的损益分界点为15%。对于数据量比较多的表而言,全表扫描与索引扫描的损益分界点可能会小于5%。
而对于存储着海量数据的表而言,全表扫描与索引扫描的损益分界点可能是1%。这里的1%是指即使通过索引扫描来从表中读取1%的数据,也没有直接通过全表扫描读取数据有效。当然上面这些数字都经验值,实际都以成本为最终考察因素。
此外,随着磁盘技术的不断发展,特别是闪存技术的不断成熟,随机读取的开销减少了很多,换句话说索引访问的成本大大下降了,优化器会更加倾向于使用索引扫描方式。
全表扫描有哪些使用场景?下面来看看常见的使用全表扫描的场景:
大范围数据读取的情况:这里的大范围是个相对的概念。一般来说,如果访问表中的大部分数据,用表扫描效率较高;如果访问表中的小部分数据,则使用索引访问的效率较高。
这里就涉及一个“损益点”的概念,当小于损益点时,索引访问效率高;当高于损益点是,表访问效率低。这不是一个具体比例,常见的经验在1%~10%之间。具体还是取决于当时的成本评估。
从小数据表中读取数据的情况:如果访问的数据规模较小,则优化器倾向于通过全表扫描的方式访问整个表。因为全表扫描使用了多块读的机制,往往效率是很高的。当然,如何界定小表是个问题,后面会专门介绍这个问题。
按照并行处理方式读取数据的情况:在并行处理的情况下,全表扫描的执行速度会在更大程度上得到提高。
使用FULL提示的情况:这个提示告诉优化器,使用全表扫描访问表。