前言
数据库中存储的数据比作字典的话,索引就相当于是字典中的目录。如果没有索引,查找一个数据就需要从第一页开始全局检索直至找到需要的诗句,有了索引可以先在目录中根据拼音查找到该数据所在的页数,因此通过索引可以大大减少了查询时间
索引类型
Mysql中根据 索引中是否存在数据,将索引分为了两种类型的索引:聚簇索引和非聚簇索引。
聚簇索引是在索引树的叶子节点中保存了完整的数据信息,则索引称为聚簇索引。Mysql的 InnoDB引擎为主键创建的主键索引即是聚簇索引,数据文件即是索引文件。MyIsam引擎中的主键索引也是非聚簇索引。
非聚簇索引是指索引树的叶子节点中保存的不是完整的数据信息,而是指向数据的一个指针或者是地址信息等(MyIsam的主键索引是指向数据的地址信息,InnoDB 中是主键的Id)。如果需要获取全部的数据信息,需要进行一次回表的操作。
联合索引是同时对多个列创建的索引。索引树中的叶子节点会同时包含多个列的值,叶子节点之间的排序,会根据创建索引时候列的顺序排序,排序满足最左前缀规则。
最左前缀规则:例如当存在一个联合索引,包括了A、B、C三列的时候,如果查询是使用了 A、【A、B】、【A、B、C】的查询方式,则可以使用这个索引,如果是 使用了 【B、C】作为查询条件,则不满足最左前缀原则,则不会使用这个索引。
因为Mysql在创建联合索引的时候,是先去比较第一列的值,当第一列的值相同的时候,才会比较第二列的值,当第二列的值相同的时候,再去比较第三列的值,以此类推,直到最后的一列。所以,当跳过第一列的值,直接匹配第二列的时候,此时第二列的值不一定是有序的,所以无法使用。
当使用某个查询条件的时候,如果需要查询的所有字段在索引中已经存在,即可直接返回,不用再根据查询到的主键ID进行回表操作,称为覆盖索引。
如果我们为name和age 创建了一个索引。当我们查询name为指定值的数据的age 和Id的时候,就会使用覆盖索引。因为在索引数据已经存在了 name 和 age 的值(主键ID的值是每一个索引都会存在的),不会再根据id回表聚簇索引查询数据。
如果在索引中不存在查询的字段数据信息,则会在查找到满足条件的数据之后,会根据查找到数据的主键ID,回表聚簇索引查找其他的信息,最终返回数据。
唯一索引要求指定的列的值不能存在相同的值,数据库在每一次进行添加或者修改的时候,都会进行数据的检查,如果数据存在了重复的值,则会直接返回失败。
存储类型
索引有两种存储类型:B树(BTREE)索引和哈希(HASH)索引,InnoDB和MyISAM存储引擎支持BTREE索引,MEMORY引擎两种都支持,默认为BTREE
大多数的存储引擎都支持B树索引,B树通常意味着所有的值按照顺序存储,并且每个叶子节点到根的距离相同,B树索引能顾加快数据访问的速度
在引入B数之前,数据结构中比较熟悉的一种树二叉树,那么为何不用二叉树来做索引,主要有几个问题:
- 如果索引数据很多,树的层次会很高(只有左右两个子节点),数据量大时查询还是会慢
- 二叉树每个节点只存储一个记录,一次查询在树上找的时候花费磁盘IO次数较多
所以它并不适合直接拿来做索引存储,算法设计人员在二叉树的基础之上进行了变种,引入了BTREE的概念,B树与二叉树有几个区别
- 不再是二叉搜索,而是N叉搜索,树的高度会降低,查询速度变快
- 叶子节点与非叶子节点都可以存储数据,且可以存储多个数据。
- 通过中序遍历,可以访问树上所有节点
BTREE被作为实现索引的数据结构被创造出来,是因为它能够完美的利用“局部性原理”,其设计逻辑是这样的:
- 内存读写快,磁盘读写慢,而且慢很多
- 磁盘预读:磁盘读写并不是按需读取,而是按页预读,一次会读一页的数据,每次加载一些看起来是冗余的数据,如果未来要读取的数据就在这一页中,可以避免未来的磁盘读写,提高效率(通常,一页数据是4K)
- 局部性原理:软件设计要尽量遵循“数据读取集中”与“使用到一个数据,大概率会使用其附近的数据”,这样磁盘预读能充分提高磁盘IO效能
B树特征:
- 根节点至少包含两个孩子
- 树中每个结点最多含有m个孩子(m >= 2)
- 除了根节点和叶结点外,其他每个结点至少含有ceil(m/2)个孩子,ceil为向上取整
- 所有叶子结点位于同一层(高度相同)
- 假设每个非终端结点中包含有n个关键字信息,其中Ki(i = 1…n)为关键字,且按顺序升序排列关键字的个数n必须满足:[ceil(m / 2) - 1] <= n <= m - 1
- 非叶子结点的指针P[1],P[2],…,P[M];其中K[1]指向关键字小于K[1]的子树,P[M - 1] 指向关键字大于K[M - 1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树,比如看图中关键字值为8的这个结点,P1所对应的这个子树,其值均小于8
查询效率O(log n)
B+树特征
B+树是在BTree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。
B+树是B树的变体,基本定义与B树相同,其改进点在于:
- B+树仍然是N叉树,但是层级变得更小,非叶子结点的子树指针与关键字个数相同(所以相对于B树,B+树能够存储更多的关键字)
- 非叶子结点的子树指针P[i],指向关键字值[K[i], K[i+1])的子树,注意区间为左闭右开,左侧值可以取到。
- 范围查找方面,当定位min与max之后,中间叶子节点,就是结果集,不用中序回溯(范围查询在SQL中用得很多,这是B+树比B树最大的优势)
- 非叶子结点仅仅用来索引,数据都保存在叶子结点中(B+树所有的检索都是从根部开始,检索到叶子结点才能结束,而且非叶子结点不存储数据的话就能存储更多关键字)
- 所有叶子结点均有一个链指针指向下一个叶子节点,这样以来就不用中序遍历,可以直接通过next指针快速访问
索引失效
索引失效是指为数据列创建了索引,但是在使用的时候,虽然使用了索引列,但是索引的作用不会生效,最终查询的时候,不会使用索引树进行查询。
索引失效的条件
类型转换
计算
因为Mysql在比较字符串的时候,是根据字符串从左到右做比较,满足最左匹配原则,当使用"%"开头查询的时候,就会破坏最左原则,所以不能使用索引。
- 破坏最左前缀原则:联合索引查询的时候,如果不满足最左前缀原则,则不会使用索引
- 范围查询的右边索引会失效
当有A、B、C三个联合索引的时候,如果
where A = 123 and B > 20 and C = 456;
则C的索引会失效。因为 B 会返回多个值,会根据返回的多个值再去做匹配查询。因为在多个值的情况下,C的排序未必是有序的,可能是乱序的情况,无法使用索引。
or 查询会产生多条数据,这多个数据之间排序规则是不确定的。
- 使用 ≠ 做判断
- 字符串没有加单引号:会发生类型的隐式转换
- 估计全表扫描会比索引快
索引type介绍
Explain介绍
在select语句之前增加explain关键字,执行后MySQL就会返回执行计划的信息,而不是执行sql。但如果from中包含子查询,MySQL仍会执行该子查询,并把子查询的结果放入临时表中。
Explain中的列
| id |
select_type |
table |
partitions |
type |
possible_keys |
key |
key_len |
ref |
rows |
filtered |
Extra |
| 1 |
PRIMARY |
|
|
ALL |
|
|
|
|
500 |
100.00 |
Using filesort |
| 2 |
DERIVED |
|
|
ALL |
|
|
|
|
87729 |
100.00 |
Using temporary; Using filesort |
| 2 |
DERIVED |
ext |
|
ref |
IDX_HPE_CRACT_UUID |
IDX_HPE_CRACT_UUID |
203 |
t.cractUuid |
1 |
100.00 |
Using where |
| 2 |
DERIVED |
core_organization |
|
ref |
CRORG_OUTER_UUID |
CRORG_OUTER_UUID |
1023 |
risen_hnshzt_txl.ext.HPE_HZT_ORGANIZATION_CODE |
1 |
100.00 |
Using where |
| 3 |
DERIVED |
a |
|
ALL |
AK_CRACT_UUID |
|
|
|
193061 |
10.00 |
Using where; Using temporary; Using filesort |
| 3 |
DERIVED |
e |
|
ref |
PRIMARY |
PRIMARY |
146 |
risen_hnshzt_txl.a.CRACT_UUID |
4 |
100.00 |
Using where |
simple:不包含子查询和union的简单查询
primary:复杂查询中最外层的select
subquery:包含在select中的子查询(不在from的子句中)
derived:包含在from子句中的子查询。mysql会将查询结果放入一个临时表中,此临时表也叫衍生表。
union:在union中的第二个和随后的select,UNION RESULT为合并的结果
表示当前行访问的是哪张表。当from中有子查询时,table列的格式为,表示当前查询依赖id=N行的查询,所以先执行id=N行的查询,如上面select_type列图4所示。当有union查询时,UNION RESULT的table列的值为<union1,2>,1和2表示参与union的行id。
-
partitions列
查询将匹配记录的分区。 对于非分区表,该值为 NULL。
-
type列
此列表示关联类型或访问类型。也就是MySQL决定如何查找表中的行。依次从最优到最差分别为:
system > const > eq_ref > ref > range > index > all。
NULL:MySQL能在优化阶段分解查询语句,在执行阶段不用再去访问表或者索引。
system、const:MySQL对查询的某部分进行优化并把其转化成一个常量(可以通过show warnings命令查看结果)。system是const的一个特例,表示表里只有一条元组匹配时为system。
eq_ref:主键或唯一键索引被连接使用,最多只会返回一条符合条件的记录。简单的select查询不会出现这种type。
ref:相比eq_ref,不使用唯一索引,而是使用普通索引或者唯一索引的部分前缀,索引和某个值比较,会找到多个符合条件的行。
range:通常出现在范围查询中,比如in、between、大于、小于等。使用索引来检索给定范围的行。
index:扫描全索引拿到结果,一般是扫描某个二级索引,二级索引一般比较少,所以通常比ALL快一点。
ALL:全表扫描,扫描聚簇索引的所有叶子节点。
此列显示在查询中可能用到的索引。如果该列为NULL,则表示没有相关索引,可以通过检查where子句看是否可以添加一个适当的索引来提高性能。
此列显示MySQL在查询时实际用到的索引。在执行计划中可能出现possible_keys列有值,而key列为null,这种情况可能是表中数据不多,MySQL认为索引对当前查询帮助不大而选择了全表查询。如果想强制MySQL使用或忽视possible_keys列中的索引,在查询时可使用force index、ignore index。
此列显示MySQL在索引里使用的字节数,通过此列可以算出具体使用了索引中的那些列。索引最大长度为768字节,当长度过大时,MySQL会做一个类似最左前缀处理,将前半部分字符提取出做索引。当字段可以为null时,还需要1个字节去记录。
字符串:
char(n):n个数字或者字母占n个字节,汉字占3n个字节
varchar(n): n个数字或者字母占n个字节,汉字占3n+2个字节。+2字节用来存储字符串长度。
数字类型:
tinyint:1字节 smallint:2字节 int:4字节 bigint:8字节
时间类型
date:3字节 timestamp:4字节 datetime:8字节
此列显示key列记录的索引中,表查找值时使用到的列或常量。常见的有const、字段名
此列是MySQL在查询中估计要读取的行数。注意这里不是结果集的行数。
-
Extra列
此列是一些额外信息。常见的重要值如下:
Using index:使用覆盖索引(如果select后面查询的字段都可以从这个索引的树中获取,不需要通过辅助索引树找到主键,再通过主键去主键索引树里获取其它字段值,这种情况一般可以说是用到了覆盖索引)。
Using where:使用 where 语句来处理结果,并且查询的列未被索引覆盖。
Using index condition:查询的列不完全被索引覆盖,where条件中是一个查询的范围。
Using temporary:MySQL需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的。
Using filesort:将使用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序。
Select tables optimized away:使用某些聚合函数(比如 max、min)来访问存在索引的某个字段时。
<h2><a id="_0"></a>前言</h2>
<blockquote>
<p>数据库中存储的数据比作字典的话,索引就相当于是字典中的目录。如果没有索引,查找一个数据就需要从第一页开始全局检索直至找到需要的诗句,有了索引可以先在目录中根据拼音查找到该数据所在的页数,因此通过索引可以大大减少了查询时间</p>
</blockquote>
<h2><a id="_3"></a>索引类型</h2>
<p>Mysql中根据 索引中是否存在数据,将索引分为了两种类型的索引:聚簇索引和非聚簇索引。</p>
<ul>
<li>聚簇索引</li>
</ul>
<p>聚簇索引是在索引树的叶子节点中保存了完整的数据信息,则索引称为聚簇索引。Mysql的 InnoDB引擎为主键创建的主键索引即是聚簇索引,数据文件即是索引文件。MyIsam引擎中的主键索引也是非聚簇索引。</p>
<ul>
<li>非聚簇索引</li>
</ul>
<p>非聚簇索引是指索引树的叶子节点中保存的不是完整的数据信息,而是指向数据的一个指针或者是地址信息等(MyIsam的主键索引是指向数据的地址信息,InnoDB 中是主键的Id)。如果需要获取全部的数据信息,需要进行一次回表的操作。</p>
<ul>
<li>联合索引</li>
</ul>
<p>联合索引是同时对多个列创建的索引。索引树中的叶子节点会同时包含多个列的值,叶子节点之间的排序,会根据创建索引时候列的顺序排序,排序满足最左前缀规则。</p>
<p>最左前缀规则:例如当存在一个联合索引,包括了A、B、C三列的时候,如果查询是使用了 A、【A、B】、【A、B、C】的查询方式,则可以使用这个索引,如果是 使用了 【B、C】作为查询条件,则不满足最左前缀原则,则不会使用这个索引。</p>
<p>因为Mysql在创建联合索引的时候,是先去比较第一列的值,当第一列的值相同的时候,才会比较第二列的值,当第二列的值相同的时候,再去比较第三列的值,以此类推,直到最后的一列。所以,当跳过第一列的值,直接匹配第二列的时候,此时第二列的值不一定是有序的,所以无法使用。</p>
<ul>
<li>覆盖索引</li>
</ul>
<p>当使用某个查询条件的时候,如果需要查询的所有字段在索引中已经存在,即可直接返回,不用再根据查询到的主键ID进行回表操作,称为覆盖索引。</p>
<p>如果我们为name和age 创建了一个索引。当我们查询name为指定值的数据的age 和Id的时候,就会使用覆盖索引。因为在索引数据已经存在了 name 和 age 的值(主键ID的值是每一个索引都会存在的),不会再根据id回表聚簇索引查询数据。</p>
<p>如果在索引中不存在查询的字段数据信息,则会在查找到满足条件的数据之后,会根据查找到数据的主键ID,回表聚簇索引查找其他的信息,最终返回数据。</p>
<ul>
<li>唯一索引</li>
</ul>
<p>唯一索引要求指定的列的值不能存在相同的值,数据库在每一次进行添加或者修改的时候,都会进行数据的检查,如果数据存在了重复的值,则会直接返回失败。</p>
<h2><a id="_34"></a>存储类型</h2>
<p>索引有两种存储类型:B树(BTREE)索引和哈希(HASH)索引,InnoDB和MyISAM存储引擎支持BTREE索引,MEMORY引擎两种都支持,默认为BTREE</p>
<p>大多数的存储引擎都支持B树索引,B树通常意味着所有的值按照顺序存储,并且每个叶子节点到根的距离相同,B树索引能顾加快数据访问的速度</p>
<p>在引入B数之前,数据结构中比较熟悉的一种树二叉树,那么为何不用二叉树来做索引,主要有几个问题:</p>
<ul>
<li>如果索引数据很多,树的层次会很高(只有左右两个子节点),数据量大时查询还是会慢</li>
<li>二叉树每个节点只存储一个记录,一次查询在树上找的时候花费磁盘IO次数较多</li>
</ul>
<p>所以它并不适合直接拿来做索引存储,算法设计人员在二叉树的基础之上进行了变种,引入了BTREE的概念,B树与二叉树有几个区别</p>
<ul>
<li>不再是二叉搜索,而是N叉搜索,树的高度会降低,查询速度变快</li>
<li>叶子节点与非叶子节点都可以存储数据,且可以存储多个数据。</li>
<li>通过中序遍历,可以访问树上所有节点</li>
</ul>
<p>BTREE被作为实现索引的数据结构被创造出来,是因为它能够完美的利用“局部性原理”,其设计逻辑是这样的:</p>
<ul>
<li>内存读写快,磁盘读写慢,而且慢很多</li>
<li>磁盘预读:磁盘读写并不是按需读取,而是按页预读,一次会读一页的数据,每次加载一些看起来是冗余的数据,如果未来要读取的数据就在这一页中,可以避免未来的磁盘读写,提高效率(通常,一页数据是4K)</li>
<li>局部性原理:软件设计要尽量遵循“数据读取集中”与“使用到一个数据,大概率会使用其附近的数据”,这样磁盘预读能充分提高磁盘IO效能</li>
</ul>
<h3><a id="B_57"></a>B树特征:</h3>
<ul>
<li>根节点至少包含两个孩子</li>
<li>树中每个结点最多含有m个孩子(m >= 2)</li>
<li>除了根节点和叶结点外,其他每个结点至少含有ceil(m/2)个孩子,ceil为向上取整</li>
<li>所有叶子结点位于同一层(高度相同)</li>
<li>假设每个非终端结点中包含有n个关键字信息,其中Ki(i = 1…n)为关键字,且按顺序升序排列关键字的个数n必须满足:[ceil(m / 2) - 1] <= n <= m - 1</li>
<li>非叶子结点的指针P[1],P[2],…,P[M];其中K[1]指向关键字小于K[1]的子树,P[M - 1] 指向关键字大于K[M - 1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树,比如看图中关键字值为8的这个结点,P1所对应的这个子树,其值均小于8<br />
<strong>查询效率O(log n)</strong></li>
</ul>
<h3><a id="B_67"></a>B+树特征</h3>
<p>B+树是在BTree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。</p>
<p>B+树是B树的变体,基本定义与B树相同,其改进点在于:</p>
<ul>
<li>B+树仍然是N叉树,但是层级变得更小,非叶子结点的子树指针与关键字个数相同(所以相对于B树,B+树能够存储更多的关键字)</li>
<li>非叶子结点的子树指针P[i],指向关键字值[K[i], K[i+1])的子树,注意区间为左闭右开,左侧值可以取到。</li>
<li>范围查找方面,当定位min与max之后,中间叶子节点,就是结果集,不用中序回溯(范围查询在SQL中用得很多,这是B+树比B树最大的优势)</li>
<li>非叶子结点仅仅用来索引,数据都保存在叶子结点中(B+树所有的检索都是从根部开始,检索到叶子结点才能结束,而且非叶子结点不存储数据的话就能存储更多关键字)</li>
<li>所有叶子结点均有一个链指针指向下一个叶子节点,这样以来就不用中序遍历,可以直接通过next指针快速访问</li>
</ul>
<h2><a id="_78"></a>索引失效</h2>
<p>索引失效是指为数据列创建了索引,但是在使用的时候,虽然使用了索引列,但是索引的作用不会生效,最终查询的时候,不会使用索引树进行查询。</p>
<h3><a id="_81"></a>索引失效的条件</h3>
<ul>
<li>索引列上的操作</li>
</ul>
<p>类型转换</p>
<p>计算</p>
<ul>
<li>like查询以"%"开头</li>
</ul>
<p>因为Mysql在比较字符串的时候,是根据字符串从左到右做比较,满足最左匹配原则,当使用"%"开头查询的时候,就会破坏最左原则,所以不能使用索引。</p>
<ul>
<li>破坏最左前缀原则:联合索引查询的时候,如果不满足最左前缀原则,则不会使用索引</li>
<li>范围查询的右边索引会失效</li>
</ul>
<p>当有A、B、C三个联合索引的时候,如果</p>
<pre><code class="lang-">where A = 123 and B > 20 and C = 456;
</code></pre>
<p>则C的索引会失效。因为 B 会返回多个值,会根据返回的多个值再去做匹配查询。因为在多个值的情况下,C的排序未必是有序的,可能是乱序的情况,无法使用索引。</p>
<ul>
<li>or 查询</li>
</ul>
<p>or 查询会产生多条数据,这多个数据之间排序规则是不确定的。</p>
<ul>
<li>使用 ≠ 做判断</li>
<li>字符串没有加单引号:会发生类型的隐式转换</li>
<li>估计全表扫描会比索引快</li>
</ul>
<h2><a id="type_107"></a>索引type介绍</h2>
<h3><a id="Explain_108"></a>Explain介绍</h3>
<p>在select语句之前增加explain关键字,执行后MySQL就会返回执行计划的信息,而不是执行sql。但如果from中包含子查询,MySQL仍会执行该子查询,并把子查询的结果放入临时表中。</p>
<h3><a id="Explain_111"></a>Explain中的列</h3>
<table>
<thead>
<tr>
<th>id</th>
<th>select_type</th>
<th>table</th>
<th>partitions</th>
<th>type</th>
<th>possible_keys</th>
<th>key</th>
<th>key_len</th>
<th>ref</th>
<th>rows</th>
<th>filtered</th>
<th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>PRIMARY</td>
<td><derived2></td>
<td></td>
<td>ALL</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>500</td>
<td>100.00</td>
<td>Using filesort</td>
</tr>
<tr>
<td>2</td>
<td>DERIVED</td>
<td><derived3></td>
<td></td>
<td>ALL</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>87729</td>
<td>100.00</td>
<td>Using temporary; Using filesort</td>
</tr>
<tr>
<td>2</td>
<td>DERIVED</td>
<td>ext</td>
<td></td>
<td>ref</td>
<td>IDX_HPE_CRACT_UUID</td>
<td>IDX_HPE_CRACT_UUID</td>
<td>203</td>
<td>t.cractUuid</td>
<td>1</td>
<td>100.00</td>
<td>Using where</td>
</tr>
<tr>
<td>2</td>
<td>DERIVED</td>
<td>core_organization</td>
<td></td>
<td>ref</td>
<td>CRORG_OUTER_UUID</td>
<td>CRORG_OUTER_UUID</td>
<td>1023</td>
<td>risen_hnshzt_txl.ext.HPE_HZT_ORGANIZATION_CODE</td>
<td>1</td>
<td>100.00</td>
<td>Using where</td>
</tr>
<tr>
<td>3</td>
<td>DERIVED</td>
<td>a</td>
<td></td>
<td>ALL</td>
<td>AK_CRACT_UUID</td>
<td></td>
<td></td>
<td></td>
<td>193061</td>
<td>10.00</td>
<td>Using where; Using temporary; Using filesort</td>
</tr>
<tr>
<td>3</td>
<td>DERIVED</td>
<td>e</td>
<td></td>
<td>ref</td>
<td>PRIMARY</td>
<td>PRIMARY</td>
<td>146</td>
<td>risen_hnshzt_txl.a.CRACT_UUID</td>
<td>4</td>
<td>100.00</td>
<td>Using where</td>
</tr>
</tbody>
</table>
<ul>
<li>
<p>id列</p>
<p>id列的编号是select的序列号,有几个select就有几个id,并且id是按照select出现的顺序增长的,id列的值越大优先级越高,id相同则是按照执行计划列从上往下执行,id为空则是最后执行。</p>
</li>
<li>
<p>select_type列</p>
<p>表示对应行是简单查询还是复杂查询。</p>
</li>
</ul>
<p>simple:不包含子查询和union的简单查询</p>
<p>primary:复杂查询中最外层的select</p>
<p>subquery:包含在select中的子查询(不在from的子句中)</p>
<p>derived:包含在from子句中的子查询。mysql会将查询结果放入一个临时表中,此临时表也叫衍生表。<br />
union:在union中的第二个和随后的select,UNION RESULT为合并的结果</p>
<ul>
<li>table列</li>
</ul>
<p>表示当前行访问的是哪张表。当from中有子查询时,table列的格式为<derivedN>,表示当前查询依赖id=N行的查询,所以先执行id=N行的查询,如上面select_type列图4所示。当有union查询时,UNION RESULT的table列的值为<union1,2>,1和2表示参与union的行id。</p>
<ul>
<li>
<p>partitions列</p>
<p>查询将匹配记录的分区。 对于非分区表,该值为 NULL。</p>
</li>
<li>
<p><strong>type列</strong></p>
<p>此列表示关联类型或访问类型。也就是MySQL决定如何查找表中的行。依次从最优到最差分别为:</p>
<p><strong>system > const > eq_ref > ref > range > index > all。</strong></p>
</li>
</ul>
<p><strong>NULL</strong>:MySQL能在优化阶段分解查询语句,在执行阶段不用再去访问表或者索引。</p>
<p><strong>system、const</strong>:MySQL对查询的某部分进行优化并把其转化成一个常量(可以通过show warnings命令查看结果)。system是const的一个特例,表示表里只有一条元组匹配时为system。</p>
<p><strong>eq_ref</strong>:主键或唯一键索引被连接使用,最多只会返回一条符合条件的记录。简单的select查询不会出现这种type。</p>
<p><strong>ref</strong>:相比eq_ref,不使用唯一索引,而是使用普通索引或者唯一索引的部分前缀,索引和某个值比较,会找到多个符合条件的行。</p>
<p><strong>range</strong>:通常出现在范围查询中,比如in、between、大于、小于等。使用索引来检索给定范围的行。</p>
<p><strong>index</strong>:扫描全索引拿到结果,一般是扫描某个二级索引,二级索引一般比较少,所以通常比ALL快一点。</p>
<p><strong>ALL</strong>:全表扫描,扫描聚簇索引的所有叶子节点。</p>
<ul>
<li>possible_keys列</li>
</ul>
<p>此列显示在查询中可能用到的索引。如果该列为NULL,则表示没有相关索引,可以通过检查where子句看是否可以添加一个适当的索引来提高性能。</p>
<ul>
<li>key列</li>
</ul>
<p>此列显示MySQL在查询时实际用到的索引。在执行计划中可能出现possible_keys列有值,而key列为null,这种情况可能是表中数据不多,MySQL认为索引对当前查询帮助不大而选择了全表查询。如果想强制MySQL使用或忽视possible_keys列中的索引,在查询时可使用force index、ignore index。</p>
<ul>
<li>key_len列</li>
</ul>
<p>此列显示MySQL在索引里使用的字节数,通过此列可以算出具体使用了索引中的那些列。索引最大长度为768字节,当长度过大时,MySQL会做一个类似最左前缀处理,将前半部分字符提取出做索引。当字段可以为null时,还需要1个字节去记录。</p>
<ul>
<li>key_len计算规则:</li>
</ul>
<p><strong>字符串</strong>:</p>
<p>char(n):n个数字或者字母占n个字节,汉字占3n个字节</p>
<p>varchar(n): n个数字或者字母占n个字节,汉字占3n+2个字节。+2字节用来存储字符串长度。</p>
<p><strong>数字类型</strong>:</p>
<p>tinyint:1字节 smallint:2字节 int:4字节 bigint:8字节</p>
<p><strong>时间类型</strong></p>
<p>date:3字节 timestamp:4字节 datetime:8字节</p>
<ul>
<li>ref列</li>
</ul>
<p>此列显示key列记录的索引中,表查找值时使用到的列或常量。常见的有const、字段名</p>
<ul>
<li>rows列</li>
</ul>
<p>此列是MySQL在查询中估计要读取的行数。注意这里不是结果集的行数。</p>
<ul>
<li>
<p>Extra列</p>
<p>此列是一些额外信息。常见的重要值如下:</p>
</li>
</ul>
<p><strong>Using index</strong>:使用覆盖索引(如果select后面查询的字段都可以从这个索引的树中获取,不需要通过辅助索引树找到主键,再通过主键去主键索引树里获取其它字段值,这种情况一般可以说是用到了覆盖索引)。</p>
<p><strong>Using where</strong>:使用 where 语句来处理结果,并且查询的列未被索引覆盖。</p>
<p><strong>Using index condition</strong>:查询的列不完全被索引覆盖,where条件中是一个查询的范围。</p>
<p><strong>Using temporary</strong>:MySQL需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的。</p>
<p><strong>Using filesort</strong>:将使用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序。</p>
<p><strong>Select tables optimized away</strong>:使用某些聚合函数(比如 max、min)来访问存在索引的某个字段时。</p>