问:二级索引的非叶子结点都会存有主键ID对吗?如果在非叶子结点找到主键Id,是否不需要继续往叶子结点中找,直接回表查询呢?
答:如果索引列是唯一的,则不需要再往叶子结点找,如果不唯一,需要继续往叶子结点找
MySQL建议使用ref而不是range
mysql> SELECT COUNT() FROM t; mysql> SELECT COUNT() FROM t WHERE key1 > 'a' AND key1 < 'i'; 在执行查询时,如果需要回表操作,会访问到聚簇索引中的数据页,如果这些数据页是有序的,可以通过顺序I/O的方式快速访问到下一个数据页,减少随机I/O的开销,提高查询效率。而如果数据页是无序的,就会造成很多随机I/O,降低查询效率。
在本文中,如果使用idx_key1来执行查询1和查询2,由于查询1和查询2需要回表操作,访问到的聚簇索引记录所在的数据页是无序的,所以执行查询1的成本会比执行查询2高。因此,设计MySQL的大叔更钟情于ref而不是range,因为ref只需要回表一次,访问的聚簇索引记录所在的数据页是有序的,可以通过顺序I/O的方式快速访问到下一个数据页,提高查询效率。MySQL优化器在计算回表的成本时,对于ref和range是有明显区别的。对于range,需要扫描多少条二级索引记录,就相当于需要访问多少个页面,每访问一个页面,回表的I/O成本就加1。而对于ref,回表开销带来的I/O成本存在天花板,即定义了一个上限值,取全表记录数的十分之一和聚簇索引所占页面的3倍中较小的那个作为上限值。
在实际计算中,对于range访问方法,MySQL优化器直接认为每次回表都需要进行一次页面I/O,这是十分粗暴的,实际情况中可能会有一些页面已经在内存中,不需要进行I/O操作。但是由于目前的算法无法预测哪些页面在内存中,哪些不在,所以只能将就使用。