在上一篇文章《MySQL常见加锁场景分析》中,咱们聊到行锁是加在索引上的,可是复杂的 SQL 每每包含多个条件,涉及多个索引,找出 SQL 执行时使用了哪些索引对分析加锁场景相当重要。mysql
好比下面这样的 SQL:sql
mysql> delete from t1 where id = 1 or val = 1
其中 id 和 val 都是索引,那么执行时使用到了哪些索引,加了哪些锁呢?为此,咱们须要使用 explain 来获取 MySQL 执行这条 SQL 的执行计划。数据库
什么是执行计划呢?简单来讲,就是 SQL 在数据库中执行时的表现状况,一般用于 SQL 性能分析、优化和加锁分析等场景,执行过程会在 MySQL 查询过程当中由解析器,预处理器和查询优化器共同生成。缓存
若是能搞清楚 MySQL 是如何优化和执行查询的,不只对优化查询必定会有帮助,还能够经过分析使用到的索引来判断最终的加锁场景。服务器
下图是MySQL执行一个查询的过程。实际上每一步都比想象中的复杂,尤为优化器,更复杂也更难理解。本文只给予简单的介绍。数据结构
MySQL查询过程以下:工具
MySQL会解析查询,并建立内部数据结构(解析树),并对其进行各类优化,包括重写查询、决定表的读取顺序、选择合适的索引等。性能
用户可经过关键字提示(hint)优化器,从而影响优化器的决策过程。也能够经过 explain 了解 数据库是如何进行优化决策的,并提供一个参考基准,便于用户重构查询和数据库表的 schema、修改数据库配置等,使查询尽量高效。优化
下面,咱们依次介绍 explain 中相关输出参数,并以实际例子解释这些参数的含义。.net
查询数据的操做类型,有以下
primary 是 SQL 中包含复杂的子查询,此时最外层查询标记为该值。
derived 是 SQL 中 from 子句中包含的子查询被标记为该值,MySQL 会递归执行这些子查询,把结果放在临时表。下图展现了上述两种类型。
union 是 SQL 在出如今 union 关键字以后的第二个 select ,被标记为该值;若 union 包含在 from 的子查询中,外层select 被标记为 derived。
union result 从 union 表获取结果的 select。下图展现了 union 和 union result 的 SQL 案例。
表的链接类型,其性能由高到低排列为 system,const,eq_ref,ref,range,index 和 all。
eq_ref 惟一性索引扫描,对于每一个索引键,表中只有一条记录与之匹配,经常使用于主键或惟一索引扫描。对于每一个来自前边的表的行组合,从该表中读取一行。它是除了 const 类型外最好的链接类型。
以下图所示,对表 t1 查询的 type 是 ALL,表示全表扫描,而后 t1 中每一行数据都来跟 t2.id 这个主键索引进行对比,因此 t2 表的查询就是 eq_ref。
index 与 ALL 类型相似,惟一区别就是只遍历索引树读取索引值,比 ALL 读取全部数据行要稍微快一些,由于索引文件一般比数据文件小。这里涉及 MySQL 的索引覆盖
ALL 全表扫描,一般状况下性能不好,应该避免。
possible_key 列指出 MySQL 可能使用哪一个索引在该表中查找。若是该列为 NULL,则没有使用相关索引。须要检查 where 子句条件来建立合适的索引提升查询效率。
key 列显示 MySQL 实际决定使用的索引。若是没有选择索引,则值为 NULL。
key_len 显示 MySQL 决定使用索引的长度。若是键为 NULL,则本列也为 NULL,使用的索引长度,在保证精确度的状况下,越短越好。由于越短,索引文件越小,须要的 I/O次数也越少。
由上图能够看出,对于 select * from t2 where id = 1 or val = 1这个语句,可使用 PRIMARY 或者 idx_t2_val 索引,实际使用了 idx_t2_val 索引,索引的长度为5。
这些实际上是咱们分析加锁场景最为关心的字段,后续文章会具体讲解如何根据这些字段和其余工具一块儿判断复杂 SQL 到底加了哪些锁。
ref 列表示使用其余表的哪一个列或者常数来从表中选择行。以下图所示,从 t2 读取数据时,要判断 t2.id = t1.id,因此 ref 就是 mysql.t1.id
rows 列显示 MySQL 认为它执行查询时必须检查的行数。
filtered 列代表了 SQL 语句执行后返回结果的行数占读取行数的百分比,值越大越好。MySQL 会使用 Table Filter 来读取出来的行数据进行过滤,理论上,读取出来的行等于返回结果的行数时效率最高,过滤的比率越多,效率越低。
如上图所示,t1表中有三条数据,rows 为 3,表示全部行都要读取出来。根据 val = 3 这个 table filter 过滤,只返回一行数据,因此 filtered 比例为33.33%,
包含不适合在其余列中显示但十分重要的额外信息。常见的值以下
using index 表示 select 操做使用了覆盖索引,避免了访问表的数据行,效率不错。
using where 子句用于限制哪一行。也就是读取数据后使用了 Table Filter 进行过滤。
以下图所示,由于 id 和 val 都是有索引的,因此 select * 也是能够直接使用覆盖索引读取数据,因此 extra 中有 using index。而由于只使用 val 索引读取了3行数据,仍是经过 where 子句进行过滤,filtered为 55%,因此 extra 中使用了 using where。
using temporary 使用临时表保存中间结果,好比,MySQL 在对查询结果排序时使用临时表,经常使用于 order by 和 group by,若是出现该值,应该优化 SQL。根据个人经验,group by 一个无索引列,或者ORDER BY 或 GROUP BY 的列不是来自JOIN语句序列的第一个表,就会产生临时表。
using join buffer 使用链接缓存。以下图所示,展现了链接缓存和临时表。关于链接缓存的内容,你们能够自行查阅,后续有时间在写文章解释。
经过 explain 了解到 SQL 的执行计划后,咱们不只能够了解 SQL 执行时使用的索引,判断加锁场景,还能够针对其余信息对 SQL 进行优化分析,好比将 type 类型从 index 优化到 ref 等。