MySQL索引及优化

摘要

  本文主要介绍MySQL索引的概念、索引的底层数据结构,然后介绍了索引的四种分类,使读者可以理解索引的基本知识;并通过讲解索引的优缺点以及应用场景,以解决索引如何应用;最后通过介绍explain工具,以及详细阐述了SQL中索引的优化方法,以便在实际开发中掌握并优化索引的使用。

  本人还写了MySQL相关博文,有兴趣的研友可以点击如下链接,请各位研友指正并留言。
   MySQL事务与隔离级别
   MySQL的锁及其MVCC
   索引为什么选择B+Tree

1、索引介绍

1.1 索引的底层结构

  索引其实是一种数据结构,它是一种排好序的便于查找的数据结构,用Btree或B+tree实现。
  三层Btree如图所示:

  三层B+tree如图所示:

1.2 索引的分类

1.2.1 单值索引

  单值索引是指一个索引只包含表中一个字段。一张表可以有多个单值索引。
  在表test中name、age字段分别建一个单值索引

mysql> create index idx_name on test(name);
mysql> create index idx_age on test(age);  

  查看表test的表信息

mysql> desc test;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id      | int(11)     | YES  |     | NULL    |       |
| name    | varchar(10) | YES  | MUL | NULL    |       |
| age     | int(11)     | YES  | MUL | NULL    |       |
| addr    | varchar(10) | YES  |     | NULL    |       |
| collage | varchar(10) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+

  查看表test的索引信息

mysql> show index from test;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test  |          1 | idx_age  |            1 | age         | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| test  |          1 | idx_name |            1 | name        | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

  从索引信息中可知,索引idx_age、idx_name 的Seq_in_index项都为1,也就表示了两个索引没有关联关系。

  删除表test的索引

mysql> drop index idx_age on test;        
mysql> drop index idx_name on test;

1.2.2 主键索引

  如果表中设置了主键,Innodb就会自动为主键建立索引。主键索引列的值因为自身就是主键,所以具有值唯一、不允许为NULL。主键索引属于单值索引。
  注意:主键索引是InnoDB存储引擎会自动创建的索引。若有主键,则直接创建主键索引;若没有主键,则选取唯一键来创建;若没有唯一键,则自动添加6位row_id来创建主键索引。同时,主键索引是聚集类索引
  例如:为表test中的字段id添加主键

mysql>alter table test add primary key (id); 

  查看表信息

mysql> desc test;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id      | int(11)     | NO   | PRI | 0       |       |
| name    | varchar(10) | YES  |     | NULL    |       |
| age     | int(11)     | YES  |     | NULL    |       |
| addr    | varchar(10) | YES  |     | NULL    |       |
| collage | varchar(10) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+

  查看表test的索引信息

mysql> show index from test;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test  |          0 | PRIMARY  |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

  删除表test的主键

mysql> alter table test drop primary key;

1.2.3 唯一索引

  唯一索引是指索引列的值唯一,但允许为Null,如身份证信息、卡号等。与主键索引的区别就是唯一索引允许为Null。唯一索引属于单值索引。
  为表test的字段id添加唯一性约束

mysql> alter table test add unique (id);

  查看表test的表信息

mysql> desc test;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id      | int(11)     | NO   | PRI | 0       |       |
| name    | varchar(10) | YES  |     | NULL    |       |
| age     | int(11)     | YES  |     | NULL    |       |
| addr    | varchar(10) | YES  |     | NULL    |       |
| collage | varchar(10) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+

  查看表test的索引信息

mysql> show index from test;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test  |          0 | id       |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

  删除表test的唯一约束

mysql> alter table test drop key id;

1.2.4 组合索引

  组合索引就是包含了多个字段的索引。
  例如:为表test的字段name, age, addr创建组合索引:

mysql> create index idx_name_age_addr on test(name, age, addr);

  查看表test的表信息

mysql> desc test;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id      | int(11)     | NO   |     | 0       |       |
| name    | varchar(10) | YES  | MUL | NULL    |       |
| age     | int(11)     | YES  |     | NULL    |       |
| addr    | varchar(10) | YES  |     | NULL    |       |
| collage | varchar(10) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+

  查看表test的索引信息

mysql> show index from test;
+-------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name          | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test  |          1 | idx_name_age_addr |            1 | name        | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| test  |          1 | idx_name_age_addr |            2 | age         | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| test  |          1 | idx_name_age_addr |            3 | addr        | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

  从索引信息中可以知道字段name、age、addr的Key_name项都标记了组合索引idx_name_age_addr ,同时Seq_in_index 也标记了组合索引中字段的顺序。

2、索引的优缺点

  优点:

  1. 类似字典目录,提高数据检索的效率,从而减少了IO访问次数,使性能得到提升;
  2. 使用索引进行数据排序,可以降低数据排序的开销。

  缺点:

  1. 索引是一种数据结构,需要额外的占用空间;
  2. 索引降低了数据的更新效率,如insert、update、delete操作,因为MySQL除了要维护新的数据外,还要维护索引;
  3. 建表的同时要综合考虑如何建高效的索引。

3、索引的应用场景

  需要建索引的场景:

  1. 主键自动建立主键索引;
  2. 查询条件中频繁出现的字段应该创建索引;
  3. 查询时与其他表关联的字段,外键关系建立索引;
  4. 查询中排序的字段应该建立索引;
  5. 查询中分组的字段应该建立索引。

  不需要建索引的场景

  1. 频繁更新的字段不适合建索引;
  2. where中用不到的字段不适合建索引;
  3. 表中记录少不适合建索引;
  4. 经常增删改的表不适合建索引;
  5. 表中字段重复值太多,不适合建索引。

4、explain分析索引

  explain是mysql提供的一种分析SQL执行情况的工具,所以使用explain可以看出SQL执行时,索引是否用到,是否失效。

mysql> explain select * from test;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    5 | NULL  |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+

  首先介绍explain中各列所代表的意思:

  1. id 代表SQL语句的执行顺序,当有嵌套SQL时,可以根据id知道子SQL语句的执行顺序,id号越大越先执行。
  2. select_type 用于标记查询类型,如普通查询、联合查询、子查询等。
select_type的取值含义
SIMPLE简单查询,不包含子查询或union
PRIMARYSQL包含子查询,最外层SQL标识为PRIMARY
SUBQUERYSQL包含子查询,子查询被标记为SUBQUERY
DERIVED在From列表中包含的子查询被标记为DERIVED
UNION带UNION的SQL语句中,第二条select语句被标记为UNION
UNION RESULT带UNION的SQL语句的执行结果被标记为UNION RESULT
  1. type 代表SQL扫描范围的类型。
type的取值含义
system表中只有一行记录,等于系统表
const通过索引一次就找到了
eq_ref唯一性索引扫描,如主键索引、唯一索引
ref非唯一性索引扫描
range只检索给定范围的行,使用一个索引来选择行
index遍历索引树
all遍历全表
  1. possible_keys 表示预计使用到的索引键;
  2. key 表示实际使用到的索引键;
  3. key_len 索引中使用的字节数,长度越短越好
  4. ref 显示索引的哪一列被使用了
  5. rows 表示找到目标记录所需要读取的行数
  6. extra 额外信息
extra的值含义
Using filesort使用了一个外部索引排序,而不是已有的内部索引排序
Using temporary使用临时表保存中间结果,常见于排序(order by)和分组(group by)
Using index使用了覆盖索引,避免访问表的数据行,效率不错。
如果同时出现了Using where,表明索引被用来执行主键的查找,再进行了回表操作;
如果没有同时出现Using where,表明索引用来读取数据而非查找。
Using where表明使用了where过滤
Using join buffer使用了连接缓存
impossible wherewhere子句的值总是false,不能用来获取任何元素

5、查询语句的索引优化

  建立表test,并填充值

CREATE TABLE `test` (
  `id` int,
  `name` varchar(10),
  `age` int ,
  `addr` varchar(10) ,
  `collage` varchar(10) ,
) 
insert test values (1,'xuebao',19,'aaa111','asd');      
insert test values (2,'tujiu',18,'bbb222','qwer');
insert test values (3,'xiezi',20,'ccc333','hjkl');
insert test values (4,'shanyin',19,'ddd444','uio');
insert test values (5,'haishi',18,'eee555','bmn');

  建组合索引idx_name_age_addr

create index idx_name_age_addr on test;

5.1 全值匹配我最爱,最左前缀要遵守

  1. select * from test;
mysql> explain select * from test;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    5 | NULL  |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+

  从上表可知,该SQL是全部扫描(type为ALL),未使用索引(possible_keys 、key为NULL)。

  1. select * from test where name=‘xuebao’ and age=19 and addr=‘aaa111’;
mysql> explain select * from test where name='xuebao' and age=19 and addr='aaa111';
+----+-------------+-------+------+-------------------+-------------------+---------+-------------------+------+-----------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref               | rows | Extra                 |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------------+------+-----------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 31      | const,const,const |    1 | Using index condition |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------------+------+-----------------------+

  从上表可知,该SQL使用到了索引(type为ref、key为idx_name_age_addr ),使用了三个索引键(ref为三个const),在索引树中遍历(Using index)。

  1. select * from test where name=‘xuebao’ and age=19;
mysql> explain select * from test where name='xuebao' and age=19;
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+-----------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref         | rows | Extra                 |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 18      | const,const |    1 | Using index condition |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+-----------------------+

  从上表可知,该SQL使用到了索引(type为ref、key为idx_name_age_addr ),使用了两个索引键(ref为两个const),在索引树中遍历(Using index)。

  1. select * from test where name=‘xuebao’ ;
mysql> explain select * from test where name='xuebao';
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref   | rows | Extra                 |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 13      | const |    1 | Using index condition |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-----------------------+

  从上表可知,该SQL使用到了索引(type为ref、key为idx_name_age_addr ),使用了一个索引键(ref为一个const),在索引树中遍历(Using index)。

  总结:在具有组合索引时,根据组合索引创建的顺序,where语句中包含索引键个数越多,查询效率越高。即全值匹配我最爱,最左前缀要遵守。

5.2 带头大哥不能死,中间兄弟不能断

  1. select * from test where age=19 and addr=‘aaa111’;
mysql> explain select * from test where age=19 and addr='aaa111';                  
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

  从上表可知,该SQL是全部扫描(type为ALL),未使用索引(possible_keys 、key为NULL),所以组合索引失效。

  1. select * from test where name=‘xuebao’ and addr=‘aaa111’;
mysql> explain select * from test where name='xuebao' and addr='aaa111';           
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref   | rows | Extra                 |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 13      | const |    1 | Using index condition |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-----------------------+

  从上表可知,该SQL使用到了索引(type为ref、key为idx_name_age_addr ),使用了一个索引键(ref为一个const),在索引树中遍历(Using index),但在where语句中却使用了两个索引键,可见最后一个索引键addr失效。

  总结:在使用组合索引时,可以使用部分索引键,但必须按照组合索引创建的顺序,按从左到右的顺序,使用部分索引键,如该例中组合索引为idx_name_age_addr,顺序为name、age、addr,所以使用部分组合索引键时,首先满足name带头大哥的存在,否则索引失效;然后选择使用name或name+age,最好不要使用name+addr,否则只有带头大哥name索引键有效。即带头大哥不能死,中间兄弟不能断。

5.3 索引列上少计算,范围之后全失效

  1. select * from test where left(name,3)=‘xue’ and age=19 and addr=‘aaa111’;
mysql> explain select * from test where left(name,3)='xue' and age=19 and addr='aaa111';                      
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

  从上表可知,该SQL是全部扫描(type为ALL),未使用索引(possible_keys 、key为NULL),所以组合索引失效。

  1. select * from test where name=‘name’ and ABS(age)=18 and addr=‘aaa111’;
mysql> explain select * from test where name='name' and ABS(age)=18 and addr='aaa111'; 
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref   | rows | Extra                 |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 13      | const |    1 | Using index condition |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-----------------------+

  从上表可知,该SQL使用到了索引(type为ref、key为idx_name_age_addr ),使用了一个索引键(ref为一个const),在索引树中遍历(Using index),索引键age用于索引扫描,索引键addr失效。

3.select * from test where name=‘name’ and age>18 and addr=‘aaa111’;

mysql> explain select * from test where name='name' and age>18 and addr='aaa111';                
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys     | key               | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+
|  1 | SIMPLE      | test  | range | idx_name_age_addr | idx_name_age_addr | 18      | NULL |    1 | Using index condition |
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+

  从上表可知,该SQL使用到了索引(key为idx_name_age_addr),在给定范围类查询(type为range),在索引树中遍历(Using index),这种情况下只有索引键name被使用,addr索引键失效,而age索引键用于索引扫描。

  1. select * from test where name=‘xuebao’ and age=19 and left(addr,3)=‘aaa’;
mysql> explain select * from test where name='xuebao' and age=19 and left(addr,3)='aaa';
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+-----------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref         | rows | Extra                 |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 18      | const,const |    1 | Using index condition |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+-----------------------+

  从上表可知,该SQL使用到了索引(type为ref、key为idx_name_age_addr ),使用了两个索引键(ref为两个const),在索引树中遍历(Using index),索引键addr用于索引扫描。

  总结:索引键上不要使用函数、计算、对比,否则会使该索引用于排序,且该索引之后的索引全失效。即索引列上少计算,范围之后全失效。

5.4 Like百分写最右,覆盖索引不写星

  1. select * from test where name like “%aa” and age=19 and addr=‘aaa111’;
mysql> explain select * from test where name like "%aa" and age=19 and addr='aaa111';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

  从上表可知,该SQL是全部扫描(type为ALL),未使用索引(possible_keys 、key为NULL),所以组合索引失效。

  1. select * from test where name like “aa%” and age=19 and addr=‘aaa111’;
mysql> explain select * from test where name like "aa%" and age=19 and addr='aaa111';  
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys     | key               | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+
|  1 | SIMPLE      | test  | range | idx_name_age_addr | idx_name_age_addr | 31      | NULL |    1 | Using index condition |
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+

  从上表可知,该SQL使用到了索引(type为range、key为idx_name_age_addr ),索引键name用于索引扫描,在索引树中遍历(Using index)。

  1. select name from test where name like “%aa” and age=19 and addr=‘aaa111’;
mysql> explain select name from test where name like "%aa" and age=19 and addr='aaa111';           
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key               | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | test  | index | NULL          | idx_name_age_addr | 31      | NULL |    5 | Using where; Using index |
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
mysql> explain select age from test where name like "%aa" and age=19 and addr='aaa111';    
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key               | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | test  | index | NULL          | idx_name_age_addr | 31      | NULL |    5 | Using where; Using index |
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
mysql> explain select addr from test where name like "%aa" and age=19 and addr='aaa111';   
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key               | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | test  | index | NULL          | idx_name_age_addr | 31      | NULL |    5 | Using where; Using index |
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+

  从上表可知,该SQL使用到了索引(type为index、key为idx_name_age_addr ),使用的是覆盖索引(Using where; Using index)。

  1. select name,addr from test where name like “%aa” and age=19 and addr=‘aaa111’;
mysql> explain select name,addr from test where name like "%aa" and age=19 and addr='aaa111';
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key               | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | test  | index | NULL          | idx_name_age_addr | 31      | NULL |    5 | Using where; Using index |
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
mysql> explain select age,addr from test where name like "%aa" and age=19 and addr='aaa111';    
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key               | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | test  | index | NULL          | idx_name_age_addr | 31      | NULL |    5 | Using where; Using index |
+----+-------------+-------+-------+---------------+-------------------+---------+------+------+--------------------------+

  从上表可知,该SQL使用到了索引(type为index、key为idx_name_age_addr ),使用的是覆盖索引(Using where; Using index)。同时从上面例子可以得知,覆盖索引只要覆盖到组合索引中任何一个或多个索引键即可。

  总结:在使用like语句时,‘%’尽量写在右边,where语句中不能使用到索引时,尽可能在select中选择索引键字段,从而使用到覆盖索引,覆盖索引意味着在索引树中就可以查询到所需数据,而不用回表操作。即Like百分写最右,覆盖索引不写星。

5.5 不等空值还有or,索引失效要少用

  1. select * from test where name is not null and age=18 and addr=‘aaa111’;
mysql> explain select * from test where name is not null and age=18 and addr='aaa111';
+----+-------------+-------+------+-------------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys     | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+-------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | test  | ALL  | idx_name_age_addr | NULL | NULL    | NULL |    5 | Using where |
+----+-------------+-------+------+-------------------+------+---------+------+------+-------------+

  从上表可知,该SQL是全部扫描(type为ALL),未使用索引(possible_keys 、key为NULL),所以组合索引失效。由此可见is not null用于带头大哥name会使组合索引全失效。

  1. select * from test where name=‘name’ and age is not null and addr=‘aaa111’;
mysql> explain select * from test where name='name' and age is not null and addr='aaa111';        
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys     | key               | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+
|  1 | SIMPLE      | test  | range | idx_name_age_addr | idx_name_age_addr | 18      | NULL |    1 | Using index condition |
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+

  从上表可知,该SQL使用到了索引(type为range、key为idx_name_age_addr ),索引键age用于索引扫描,在索引树中遍历(Using index)。

  1. select * from test where name=‘name’ and age=18 and addr is not null;
mysql> explain select * from test where name='name' and age=18 and addr is not null;
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys     | key               | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+
|  1 | SIMPLE      | test  | range | idx_name_age_addr | idx_name_age_addr | 31      | NULL |    1 | Using index condition |
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+

  从上表可知,该SQL使用到了索引(type为range、key为idx_name_age_addr ),索引键addr用于索引扫描,在索引树中遍历(Using index)。

5.6 varchar数引不能丢,SQL高级也不难

  1. select * from test where name=‘xuebao’ and age=18 and addr=111; 111没有引号
mysql> explain select * from test where name='xuebao' and age=18 and addr=111;
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+-----------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref         | rows | Extra                 |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 18      | const,const |    1 | Using index condition |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+-----------------------+

  从上表可知,该SQL使用到了索引(type为ref 、key为idx_name_age_addr ),索引键(ref为两个const),索引键addr用于索引扫描,在索引树中遍历(Using index)。

  1. select * from test where name=‘xuebao’ and age=18 and addr=‘111’; 111带引号
mysql> explain select * from test where name='xuebao' and age=18 and addr='111';
+----+-------------+-------+------+-------------------+-------------------+---------+-------------------+------+-----------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref               | rows | Extra                 |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------------+------+-----------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 31      | const,const,const |    1 | Using index condition |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------------+------+-----------------------+

  从上表可知,该SQL使用到了索引(type为ref 、key为idx_name_age_addr ),索引键(ref为三个const),全值匹配型。

  总结:由此可见,varchar型字段在匹配数字字符时一定要带引号,否则该索引键及后续索引键都会失效。即varchar数引不能丢,SQL高级也不难。

6、排序语句的索引优化

6.1 where使用有效索引,oder by能使用索引最左前缀

mysql> explain select * from test where name='asdf' order by age;
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+------------------------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref   | rows | Extra                              |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+------------------------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 13      | const |    1 | Using index condition; Using where |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+------------------------------------+
mysql> explain select * from test where name='asdf' and age=19 order by addr;
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+------------------------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref         | rows | Extra                              |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+------------------------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 18      | const,const |    1 | Using index condition; Using where |
+----+-------------+-------+------+-------------------+-------------------+---------+-------------+------+------------------------------------+
mysql> explain select * from test where name='asdf' order by age, addr; 
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+------------------------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref   | rows | Extra                              |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+------------------------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 13      | const |    1 | Using index condition; Using where |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+------------------------------------+
mysql> explain select * from test where name='asdf' and age>18 order by age, addr;
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys     | key               | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+
|  1 | SIMPLE      | test  | range | idx_name_age_addr | idx_name_age_addr | 18      | NULL |    1 | Using index condition |
+----+-------------+-------+-------+-------------------+-------------------+---------+------+------+-----------------------+

  总结:order by中可以使用索引的前提是where语句中有有效索引,同时order by 在有效索引的基础上继续满足最左前缀规则即可使用索引。

6.2 不能使用索引进行排序

  1. select * from test order by name ASC,age DESC, addr DESC;
mysql> explain select * from test order by name ASC,age DESC, addr DESC;      
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+

  从上表可知,该SQL是全部扫描(type为ALL),未使用索引(possible_keys 、key为NULL),因为order by中索引键的升降序不一致。同时extra出现了Using filesort,表示使用了外部索引,也就是在查询之前mysql自己建了一套索引再进行查询动作,由此可见效率低,耗资源。

  1. select * from test where id=2 order by age, addr;
mysql> explain select * from test where id=2 order by age, addr;                      
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                       |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using where; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+

  从上表可知,该SQL是全部扫描(type为ALL),未使用索引(possible_keys 、key为NULL),因为where中没有有效索引。同时extra出现了Using filesort,表示使用了外部索引,也就是在查询之前mysql自己建了一套索引再进行查询动作,由此可见效率低,耗资源。

  1. select * from test where name=‘asdf’ order by addr;
mysql> explain select * from test where name='asdf' order by addr;  
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref   | rows | Extra                                              |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+----------------------------------------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 13      | const |    1 | Using index condition; Using where; Using filesort |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+----------------------------------------------------+

  从上表可知,该SQL使用了索引(type为ref ,possible_keys 、key为idx_name_age_addr ),但extra出现了Using filesort,表示使用了外部索引,也就是在查询之前mysql自己建了一套索引再进行查询动作,由此可见效率低,耗资源。因为order by没有基于where的有效索引键满足最左前缀原则

  1. select * from test where name=‘asdf’ order by age,collage;
mysql> explain select * from test where name='asdf' order by age,collage;
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys     | key               | key_len | ref   | rows | Extra                                              |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+----------------------------------------------------+
|  1 | SIMPLE      | test  | ref  | idx_name_age_addr | idx_name_age_addr | 13      | const |    1 | Using index condition; Using where; Using filesort |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+----------------------------------------------------+

  从上表可知,该SQL使用了索引(type为ref ,possible_keys 、key为idx_name_age_addr ),但extra出现了Using filesort,表示使用了外部索引,也就是在查询之前mysql自己建了一套索引再进行查询动作,由此可见效率低,耗资源。因为order by中collage不是索引的一部分

  1. select * from test where name in (‘xuebao’, ‘tujiu’, ‘sanyin’) order by age,addr;
mysql> explain select * from test where name in ('xuebao', 'tujiu', 'sanyin') order by age,addr;
+----+-------------+-------+------+-------------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys     | key  | key_len | ref  | rows | Extra                       |
+----+-------------+-------+------+-------------------+------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | test  | ALL  | idx_name_age_addr | NULL | NULL    | NULL |    5 | Using where; Using filesort |
+----+-------------+-------+------+-------------------+------+---------+------+------+-----------------------------+

  从上表可知,该SQL是全部扫描(type为ALL),未使用索引(possible_keys 、key为NULL),同时extra出现了Using filesort,表示使用了外部索引,也就是在查询之前mysql自己建了一套索引再进行查询动作,由此可见效率低,耗资源。因为对排序来说,多个相等条件也是范围查询

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值