mysql学习笔记(5)之索引选择原则


内容来源为六星教育,这里仅作为学习笔记

explain分析语句

通过对查浦酒句的分析,可以了解查询语句的执行情况,找出查询语向执行的瓶颈,从而优化查询语句。MySQL中提供了EXPLAIN语句,用来分析查询语句。

EXPLAIN语句的基本语法如下:

EXPLAIN [EXTENDED] SELECT select_ options

字段描述
idselect识别符
select_typeselect语句类型
table与查询语句相关的表
partitions表分区
typetype表示的是表的连接类型
possible_keys可能使用到的索引
key使用的索引
key_len使用的索引长度
ref使用哪个列一起进行的查询
rows查询sql语句扫描的数据量,比不会很精确 属于约等于
filtered
Extra一些额外的信息

对于以上字段信息不同值的解释:

  1. select_type

    SIMPLE:表示简单查询,其中不包括连接查询和子查询
    PRIMARY:主查询或者最外层的查询语句。
    SUBQUERY:子查询
    DERIVED:衍生查询-在select出一批自定义列的数据,概念上相当于一张表,但该表只在语句执行过程出现和
    UNION :联合查询,union 后面的那张表就会表示成它
    UNION RESULT: 联合结果

  2. type
    常用的类型有: ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好)

    ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行
    index: Full Index Scan,index与ALL区别为index类型只遍历索引树
    range:只检索给定范围的行,使用一个索引来选择行
    ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
    eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件
    const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system
    NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。

  3. Extar
    Extra列是用来说明一些额外信息的,我们可以通过这些额外信息来更准确的理解MySQL到底将如何执行给定的查询语句。

    using index:表示sql使用并且是覆盖索引的方式进行查询
    using where:根据条件进行过滤
    using where:using index;
    using index condition;使用了索引,但是需要回表进行数据查询
    using filesort:在使用索引列进行排序时(order by 索引列)
    using temporary:当sql中存在去重,排序 合并 分组
    distinct: 优化distinct,在找到第一匹配的元组后即停止找同样值的工作

联合索引结构与索引匹配原则

最左前缀匹配原则:在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

要想理解联合索引的最左匹配原则,先来理解下索引的底层原理。索引的底层是一颗B+树,那么联合索引的底层也就是一颗B+树,只不过联合索引的B+树节点中存储的是键值。由于构建一棵B+树只能根据一个值来确定索引关系,所以数据库依赖联合索引最左的字段来构建。
举例:创建一个(a,b)的联合索引,那么它的索引树就是下图的样子。
在这里插入图片描述
可以看到a的值是有顺序的,1,1,2,2,3,3,而b的值是没有顺序的1,2,1,4,1,2。但是我们又可发现a在等值的情况下,b值又是按顺序排列的,但是这种顺序是相对的。这是因为MySQL创建联合索引的规则是首先会对联合索引的最左边第一个字段排序,在第一个字段的排序基础上,然后在对第二个字段进行排序。所以b=2这种查询条件没有办法利用索引。
示例:

字段类型描述
idint(11)主键
namevarchar(10)名称
ageint(11)年龄

该表中对id列.name列.age列建立了一个联合索引 id_name_age_index,实际上相当于建立了三个索引(id)(id_name)(id_name_age)。

  1. 全值匹配查询时
mysql> explain select * from staffs where name = 'shine' and sex = 1 and age = 20;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref               | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 40      | const,const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where age = 20 and name = 'shine' and sex = 1;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref               | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 40      | const,const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where sex = 1 and age = 20 and name = 'shine';
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref               | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 40      | const,const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

通过观察上面的结果图可知,where后面的查询条件,不论是使用(name,age,sex)(age,name,sex)还是(sex,age,name)顺序,在查询时都使用到了联合索引,可能有同学会疑惑,为什么底下两个的搜索条件明明没有按照联合索引从左到右进行匹配,却也使用到了联合索引? 这是因为MySQL中有查询优化器explain,所以sql语句中字段的顺序不需要和联合索引定义的字段顺序相同,查询优化器会判断纠正这条SQL语句以什么样的顺序执行效率高,最后才能生成真正的执行计划,所以不论以何种顺序都可使用到联合索引

  1. 匹配最左边列时
mysql> explain select * from staffs where name = 'shine';
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 33      | const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

该搜索是遵循最左匹配原则的,通过key字段也可知,在搜索过程中使用到了联合索引,且使用的是联合索引中的(id)索引

mysql> explain select * from staffs where name = 'shine' and age = 20;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref         | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 38      | const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

由于name到age是从左边依次往右边匹配,这两个字段中的值都是有序的,所以也遵循最左匹配原则,通过key字段可知,在搜索过程中也使用到了联合索引,但使用的是联合索引中的(name_age)索引

mysql> explain select * from staffs where name = 'shine' and age = 20 and sex = 1;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref               | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 40      | const,const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

由于上面三个搜索都是从最左边name依次向右开始匹配的,所以都用到了name_age_sex联合索引。

那如果不是依次匹配呢?

mysql> mysql> explain select * from staffs where name = 'shine' and sex = 1;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                    |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 33      | const |    1 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

通过key字段可知,在搜索过程中也使用到了联合索引,但使用的是联合索引中的(name)索引,因为联合索引树是按照name字段创建的,但sex相对于name来说是无序的,只有name是有序的,所以他只能使用联合索引中的name索引。

mysql> explain select * from staffs where age = 20;
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_name_age_sex | 40      | NULL |    5 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

通过观察发现上面key字段发现在搜索中也使用了name_age_sex索引,可能许多同学就会疑惑它并没有遵守最左匹配原则,按道理会索引失效,为什么也使用到了联合索引?因为没有从id开始匹配,且name单独来说是无序的,所以它确实不遵循最左匹配原则,然而从type字段可知,它虽然使用了联合索引,但是它是对整个索引树进行了扫描,正好匹配到该索引,与最左匹配原则无关,一般只要是某联合索引的一部分,但又不遵循最左匹配原则时,都可能会采用index类型的方式扫描,但它的效率远不如最做匹配原则的查询效率高,index类型类型的扫描方式是从索引第一个字段一个一个的查找,直到找到符合的某个索引,与all不同的是,index是对所有索引树进行扫描,而all是对整个磁盘的数据进行全表扫描。

mysql> explain select * from staffs where sex = 1;
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_name_age_sex | 40      | NULL |    5 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where age = 20 and sex = 1;
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_name_age_sex | 40      | NULL |    5 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

这两个结果跟上面的是同样的道理,由于它们都没有从最左边开始匹配,所以没有用到联合索引,使用的都是index全索引扫描。

  1. 匹配列前缀
    对于模糊匹配的查询,如果是前缀匹配用的是索引,中坠和后缀用的是全表扫描
mysql> explain select * from staffs where name like "shie%";
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys    | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | range | idx_name_age_sex | idx_name_age_sex | 33      | NULL |    1 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where name like "%ara%";
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_name_age_sex | 40      | NULL |    5 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where name like "%ara";
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_name_age_sex | 40      | NULL |    5 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

总结:只要是按照了索引创建顺序来编写where条件,那么就可以使用到这个索引,并且大几率是覆盖索引,同时也符合最左匹配原则。
而最左匹配原则与条件中的字段顺序无关。只需要按照索引创建顺序最左字段存在即可。

mysql对于索引优先考虑的对象

一般我们在开发项目时对于一些业务难免会写出一些条件+分组/排序的sql语句,而通常也是这些sql会导致mysql的性能变差,这个时候我们想到使用mysql的索引来进行优化,但是mysql的索引对于条件,分组,排序都存在的情况下是如何去选择索引的呢?

下面我们根据上面的问题来进行一些测试以及分析。

条件与分组排序共存的情况下

mysql> explain select sex,age from staffs where name = "shine" group by sex order by age;
+----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------------------------------------------+
| id | select_type | table  | partitions | type | possible_keys            | key              | key_len | ref   | rows | filtered | Extra                                                     |
+----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------------------------------------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex,idx_sex | idx_name_age_sex | 33      | const |    1 |   100.00 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

当sql中where条件,分组,排序同时存在时,MySQL的优化器会优先选择条件来确定使用的索引,因为where可以减少更多的sql扫描,而排序和分组往往进行的是全表扫描。

条件与排序共存

mysql> explain select sex,age from staffs where name = "shine" order by age;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                    |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 33      | const |    1 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

原因:所有的排序都是在条件过滤之后才执行的,所以如果条件过滤了大部分数据的话,几百几千条数据进行排序其实并不是很消耗性能,即使索引优化了排序但实际提升性能很有限。

分组排序共存

mysql> explain select sex,age from staffs group by sex order by age;
+----+-------------+--------+------------+-------+--------------------------+---------+---------+------+------+----------+---------------------------------+
| id | select_type | table  | partitions | type  | possible_keys            | key     | key_len | ref  | rows | filtered | Extra                           |
+----+-------------+--------+------------+-------+--------------------------+---------+---------+------+------+----------+---------------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | idx_name_age_sex,idx_sex | idx_sex | 2       | NULL |    5 |   100.00 | Using temporary; Using filesort |
+----+-------------+--------+------------+-------+--------------------------+---------+---------+------+------+----------+---------------------------------+
1 row in set, 1 warning (0.00 sec)

对于分组和排序共存的情况下,mysql会优先根据分组去选择索引,那是因为sql需要先将要查询的数据进行分组,随后才会进行数据的排序。

简单来说 mysql的优化器在选择索引时的策略:where > group by > order by
优化方案:将条件字段以及分组和排序字段,都建立在索引中

mysql索引的挑选原则

注:字段一般是推荐重复比较少的字段影响到数据的检索,如果是项目需求(可建立联合索引)

  1. 唯一字段可以建立单索引 where id = 4 非唯一字段可以考虑建立联合索引
  2. 索引的个数与包含的字段,最佳是不超出6个,勉强可以是10个 =》 垂直分表
  3. 索引的使用遵循最左匹配原则其次就是覆盖索引
  4. 索引的选择字段尽量要小一些 int varchar(10) char(5) es 存储文本路径
  5. 避免<,<= ,> ,>= , % ,between 之前的条件 等范围查询
  6. 尽量多使用explain进行分析
  7. 优先考虑建立联合索引,索引的字段不要包含null 或者是 ‘’;
  8. 建立的索引- 优先考虑 建立 联合索引
  9. 索引字段不要有 null, 不是 ‘’

联合索引:是我们实际创建的索引
覆盖索引:一般是查询的性能信息 在索引列都是sql需要查询的数据

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值