01.优化的过程
对于我们这些 MySQL 的使用者来说, MySQL 其实就是一个软件,平时用的最多的就是查询功能。DBA时不时丢过来一些慢查询语句让优化,我们如果连查询是怎么执行的都不清楚还优化个毛线,所以是时候掌握真正的技术了。我们在第一章的时候就曾说过, MySQL Server 有一个称为 查询优化器 的模块,一条查询语句进行语法解析之后就会被交给查询优化器来进行优化,优化的结果就是生成一个所谓的 执行计划 ,这个执行计划表明了应该使用哪些索引进行查询,表之间的连接顺序是啥样的,最后会按照执行计划中的步骤调用存储引擎提供的方法来真正的执行查询,并将查询结果返回给用户。
1.2 举个例子
02.查询有两种方式:
回到 MySQL 中来,我们平时所写的那些查询语句本质上只是一种声明式的语法,只是告诉 MySQL 我们要获取的数据符合哪些规则,至于 MySQL 背地里是怎么把查询结果搞出来的那是 MySQL 自己的事儿。对于单个表的查询来说,设计MySQL的大叔把查询的执行方式大致分为下边两种:
设计 MySQL 的大叔把 MySQL 执行查询语句的方式称之为 访问方法 或者 访问类型 。同一个查询语句可能可以使用多种不同的访问方法来执行,虽然最后的查询结果都是一样的,但是执行的时间可能差老鼻子远了,就像是从钟楼到大雁塔,你可以坐火箭去,也可以坐飞机去,当然也可以坐乌龟去。下边细细道来各种 访问方法 的具体内容。
2.1 const
2.1.1 主键查询
原谅我把聚簇索引对应的复杂的 B+ 树结构搞了一个极度精简版,为了突出重点,我们忽略掉了 页 的结构,直接把所有的叶子节点的记录都放在一起展示,而且记录中只展示我们关心的索引列,对于 single_table 表的聚簇索引来说,展示的就是 id 列。我们想突出的重点就是: B+ 树叶子节点中的记录是按照索引列排序的,对于的聚簇索引来说,它对应的 B+ 树叶子节点中的记录就是按照 id 列排序的。
2.1.2 唯一二级索引
B+ 树本来就是一个矮矮的大胖子,所以这样根据主键值定位一条记录的速度贼快。类似的,我们根据唯一二级索引列来定位一条记录的速度也是贼快的,比如下边这个查询:
可以看到这个查询的执行分两步,第一步先从 idx_key2 对应的 B+ 树索引中根据 key2 列与常数的等值比较条件定位到一条二级索引记录,然后再根据该记录的 id 值到聚簇索引中获取到完整的用户记录。设计 MySQL 的大叔认为通过主键或者唯一二级索引列与常数的等值比较来定位一条记录是像坐火箭一样快的,所以他们把这种通过主键或者唯一二级索引列来定位一条记录的访问方法定义为: const ,意思是常数级别的,代价是可以忽略不计的。
2.2 ref
有时候我们对某个普通的二级索引列与常数进行等值比较,比如这样:
对于这个查询,我们当然可以选择全表扫描来逐一对比搜索条件是否满足要求,我们也可以先使用二级索引找到对应记录的 id 值,然后再回表到聚簇索引中查找完整的用户记录。由于普通二级索引并不限制索引列值的唯一性,所以可能找到多条对应的记录,也就是说使用二级索引来执行查询的代价取决于等值匹配到的二级索引记录条数。
如果匹配的记录较少,则回表的代价还是比较低的,所以 MySQL 可能选择使用索引而不是全表扫描的方式来执行查询。设计 MySQL 的大叔就把这种搜索条件为二级索引列与常数等值比较,采用二级索引来执行查询的访问方法称为: ref 。我们看一下采用 ref 访问方法执行查询的图示