天天看点

mysql技术内幕学习笔记-查询优化器及索引(一)

1、查询优化器工作目标

        查询优化器最主要的工作目标是尽可能的使用索引,并且要使用条件最严格的索引来尽可能多、尽可能快得排除不符合索引的数据行。

例如:要查询员工表中年龄为25的男性姓名。

SELECT  name FROM staff WHERE   sex = ‘M’ AND age = 25;

  假设年龄为25的又100, 男性有600个,25岁的男性员工有10个。查询优化器会首先从age开始匹配,首先排除900人,然后再从100个25岁的员工中找出男性。如果先从sex开始匹配,那么需要从600个男性员工中查找25岁的员工,查询效率低于先排除900个年龄不是25的员工。

mysql在将一个索引项中的值与常数进行比较,优化器会假设索引中的键值是均匀的,并且会检查估算查询会用到多少个索引项。

2、索引

        2.1、 为什么要索引

  直接引用参考文献1的说法“除了词典,生活中随处可见索引的例子,如火车站的车次表、图书的目录等。它们的原理都是一样的,通过不断的缩小想要获得数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是我们总是通过同一种查找方式来锁定数据。数据库也是一样,但显然要复杂许多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?我们回想字典的例子,能不能把数据分成段,然后分段查询呢?最简单的如果1000条数据,1到100分成第一段,101到200分成第二段,201到300分成第三段......这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是1千万的记录呢,分成几段比较好?稍有算法基础的同学会想到搜索树,其平均复杂度是lgN,具有不错的查询性能。”

       2.2、 索引的数据结构

        目前大多数的数据库采用b数或者b+树。一般索引本身比较大,无法全部存储在内存中,在查询过程中就会产生I/O消耗。磁盘每次预读都会加载一个页大小的数据,数据库设计者利用这个原理将节点大小也设置为1页,那么每个节点就只需要1次I/O。高度为h的的b树,最多只需要h次I/O就可以实现查找,由于数据库中根节点常驻内存,所以只需要h-1次即可完成查找。同样,我们可以使用红黑树等其他数据结构实现索引,但是由于相同节点数红黑树等树的高度较大等原因,导致I/O较多,效率没有b数高。

      2.3 MyISAM及InnoDB索引实现

2.3.1 MyISAM索引

MyISAM索引为非聚簇索引,也就是索引和数据表不是同一文件。下图为b+数实现的MyISAM索引结构。 

mysql技术内幕学习笔记-查询优化器及索引(一)

MyISAM索引结构

         从图中可以看出,MyISAM索引的节点并没有存储真正的数据,它存储了真正数据航所在的地址,然后通过地址到数据表文件中取出数据。

2.3.2 InnoDB索引
mysql技术内幕学习笔记-查询优化器及索引(一)

InnoDB索引结构

InnoDB索引为聚簇索引,即索引和数据在同一个文件。下图为b+数实现的InnoDB索引结构。 从图中可以看出,InnoDB索引的节点存储了真正的数据,索引和数据存放在一起。

3、索引失效情况

3.1 带索引的数据列在表达式中应单独出现,不要在索引列进行运算

例如 score 列为索引列

WHERE score * 1.2 < 90    与      WHERE score < 90 / 1.2表达的意思完全相同,但是查询效率完全不同。第一个语句会全表扫描,将所有数据乘以1.2然后和90比较,无法使用索引;第二个语句将会通过索引查询score 小于 90/1.2的数据,查询效率较高。

3.2 联合索引中范围查询导致索引失效

例如联合索引(a, b, c)

WHERE a BETWEEN 1 AND 10  AND b = 100;这个语句只会使用联合索引中的a部分,b部分无法使用。

4、参考:

[1] http://tech.meituan.com/mysql-index.html  

[2] http://www.uml.org.cn/sjjm/201107145.asp#nav-2-1