本人多年以前的笔记,部分有摘抄,侵删
为什么有时候索引扫描比全表扫描更慢?
假设一个表有100w行数据,表的段大小为1GB。如果对表进行全表扫描,最理想的情况下,每次I/O都读取1MB数据(128个块),将1GB的表从磁盘读入buffer cache需要1024次I/O。在实际情况中,表的段 前16个extent,每个extent都只有8个块,每次I/O只能读取8个块,而不是128个块,表中有部分块会被缓存在buffer cache中,会引起I/O中断,那么将1GB的表从磁盘读入buffer cache可能需要耗费1500次物理I/O。
从表中查询5w行数据,走索引。假设一个索引叶子块能存储100行数据,那么5w行数据需要扫描500个叶子块(单块读),也就是需要500次物理I/O,然后有5w条数据需要回表,假设索引的集群因子很小(接近表的块数),假设每个数据块存储50行数据,那么回表需要耗费1000次物理I/O(单块读),也就是说从表中查询5w行数据,如果走索引,一共需要耗费大概1500次物理I/O。如果索引的集群因子较大(接近表的总行数),那么回表要耗费更多的物理I/O,可能是3000次,而不是1000次。
根据上述理论,我们知道,走索引返回的数据越多,需要耗费的I/O次数也就越多,因此,返回大量数据应该走全表扫描或者是INDEX FAST FULL SCAN,返回少量数据才走索引扫描。根据上述理论,笔者一般建议返回表中总行数5%以内的数据,走索引扫描,超过5%走全表扫描。请注意,5%只是一个参考值,适用于绝大多数场景,如有特殊情况,具体问题具体分析。
什么是集群因子(Clustering Factor)呢?集群因子是通过一个索引扫描一张表时需要访问的表的数据块的数量。集群因子的计算方法如下: (1) 扫描一个索引; (2) 比较某行的ROWID和前一行的ROWID,如果这两个ROWID不属于同一个数据块,那么集群因子增加1; (3) 整个索引扫描完毕后,就得到了该索引的集群因子。 以上面集群因子的计算方式可以看出,集群因子反映了索引范围扫描可能带来的对整个表访问过程的IO开销情况,如果集群因子接近于表存储的块数,说明这张表是按照索引字段的顺序存储的。如果集群因子接近于行的数量,那说明这张表不是按索引字段顺序存储的。在计算索引访问成本时,集群因子十分有用。集群因子乘以选择性参数就是访问索引的开销。 如果这个统计数据不能反映出索引的真实情况,那么可能会造成优化器错误地选择执行计划。另外,如果某张表上的大多数访问是按照某个索引做索引扫描,那么将该表的数据按照索引字段的顺序重新组织,可以提高该表的访问性能。