MySQL索引优化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL索引优化相关的知识,希望对你有一定的参考价值。

1.应该避免的索引失效的情况

CREATE TABLE staffs(
id INT(10) PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(24) NOT NULL DEFAULT ‘‘ COMMENT 姓名,
age INT(10) NOT NULL DEFAULT 0 COMMENT 年龄,
pos VARCHAR(24) NOT NULL DEFAULT ‘‘ COMMENT 职位,
add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 入职时间
 )CHARSET utf8 COMMENT 员工记录表;
INSERT INTO staffs(NAME,age,pos,add_time) VALUES(z3,22,manager,NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES(July,23,dev,NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES(2000,23,dev,NOW());

ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(NAME,age,pos);
mysql> show index from staffs;
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table  | Non_unique | Key_name              | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| staffs |          0 | PRIMARY               |            1 | id          | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
| staffs |          1 | idx_staffs_nameAgePos |            1 | NAME        | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
| staffs |          1 | idx_staffs_nameAgePos |            2 | age         | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
| staffs |          1 | idx_staffs_nameAgePos |            3 | pos         | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.00 sec)

规则1:全值匹配我最爱

mysql> explain select * from staffs where name=July;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.01 sec)

mysql> explain select * from staffs where name=July and age=23;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref         | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 78      | const,const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name=July and age=23 and pos=dev;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref               | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 152     | const,const,const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
1 row in set (0.00 sec)

第三种效果最好~ 用到了我们创建的索引,下面再看一种情况

mysql> explain select * from staffs where age=23 and pos=dev;
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.01 sec)

mysql> explain select * from staffs where pos=dev;
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

全表扫描!!我们看到,索引竟然失效了

mysql> explain select * from staffs where name=July;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

单独使用name就不会使索引失效,由此引入规则2

规则2:最佳左前缀法则,即如果索引了多列,要遵守最佳左前缀法则,即查询必须从索引的最左前列开始,并且不跳过索引中的列~

ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(name,age,pos);

索引的最左前列是name,name相当于火车头,没有火车头,搞毛~所以索引的第一个字段不能丢失

再来看一种情况

mysql> explain select * from staffs where name=July and pos=dev;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

只有一个const,这和只用name是一样的,违背了什么原则呢?不跳过索引中的列

 

mysql> explain select * from staffs where name=July and age=23;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref         | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 78      | const,const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------+------+-----------------------+
1 row in set (0.00 sec)

 

以上两个对比一下即可~

 

规则3.不要在索引列上做任何操作(计算,函数,类型转换),会导致索引失效而转向全表扫描~

mysql> select * from staffs where name=July;
+----+------+-----+-----+---------------------+
| id | NAME | age | pos | add_time            |
+----+------+-----+-----+---------------------+
|  2 | July |  23 | dev | 2017-03-17 23:14:23 |
+----+------+-----+-----+---------------------+
1 row in set (0.00 sec)

mysql> select * from staffs where left(name,4)=July;
+----+------+-----+-----+---------------------+
| id | NAME | age | pos | add_time            |
+----+------+-----+-----+---------------------+
|  2 | July |  23 | dev | 2017-03-17 23:14:23 |
+----+------+-----+-----+---------------------+
1 row in set (0.04 sec)

mysql> explain select * from staffs where name=July;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where left(name,4)=July;
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

规则4.存储引擎不能使用索引中范围条件右边的列

mysql> explain select * from staffs where name=July and age=23 and pos=dev;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref               | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 152     | const,const,const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name=July and age>23 and pos=dev;
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
| id | select_type | table  | type  | possible_keys         | key                   | key_len | ref  | rows | Extra                 |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
|  1 | SIMPLE      | staffs | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 78      | NULL |    1 | Using index condition |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
1 row in set (0.00 sec)
age>23导致后面的pos索引失效

规则5.尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *

mysql> explain select * from staffs where name=July and age=23 and pos=dev;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref               | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 152     | const,const,const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select name,age,pos from staffs where name=July and age=23 and pos=dev;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+--------------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref               | rows | Extra                    |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+--------------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 152     | const,const,const |    1 | Using where; Using index |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------------------+------+--------------------------+
1 row in set (0.00 sec)

mysql> explain select name,age,pos from staffs where name=July and age>23 and pos=dev;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+--------------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                    |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using where; Using index |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name=July and age>23 and pos=dev;
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
| id | select_type | table  | type  | possible_keys         | key                   | key_len | ref  | rows | Extra                 |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
|  1 | SIMPLE      | staffs | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 78      | NULL |    1 | Using index condition |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
1 row in set (0.00 sec)

规则6.MySQL中在使用不等于(!=或者<>)的时候无法使用索引导致全表扫描

mysql> explain select * from staffs where name=July;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name!=July;
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys         | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name<>July;
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys         | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

规则7.is null,is not null也无法使用索引

mysql> explain select * from staffs where name is null;
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra            |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name is not null;
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys         | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

所以建表的时候字段不能为null

规则8.like以通配符开头(‘%abc‘)mysql索引失效会变成全表扫描的操作~

mysql> explain select * from staffs where name like %July%;
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from staffs where name like July%;
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
| id | select_type | table  | type  | possible_keys         | key                   | key_len | ref  | rows | Extra                 |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
|  1 | SIMPLE      | staffs | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | NULL |    1 | Using index condition |
+----+-------------+--------+-------+-----------------------+-----------------------+---------+------+------+-----------------------+
1 row in set (0.00 sec)

问题:解决like%字符串%时索引不被使用的问题~

使用覆盖索引~

规则9.字符串不加单引号索引失效

mysql> select * from staffs where name=2000;
+----+------+-----+-----+---------------------+
| id | NAME | age | pos | add_time            |
+----+------+-----+-----+---------------------+
|  3 | 2000 |  23 | dev | 2017-03-17 23:14:23 |
+----+------+-----+-----+---------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where name=2000;
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
| id | select_type | table  | type | possible_keys         | key                   | key_len | ref   | rows | Extra                 |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | staffs | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 74      | const |    1 | Using index condition |
+----+-------------+--------+------+-----------------------+-----------------------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

mysql> explain select * from staffs where name=2000;
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys         | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

底层做了隐式的数据转换,将2000转换成‘2000‘,导致索引失效

规则10.少用or,用它来连接时索引会失效

mysql> explain select * from staffs where name=July or name=z3;
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
| id | select_type | table  | type | possible_keys         | key  | key_len | ref  | rows | Extra       |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | staffs | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 | Using where |
+----+-------------+--------+------+-----------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

 

以上是关于MySQL索引优化的主要内容,如果未能解决你的问题,请参考以下文章

mysql有几种索引类型?使用索引时都有那些地方要注意?sql优化原则是啥?

MYSQL优化 学习笔记

MySql知识体系总结(SQL优化篇)

MySQL 千万 级数据量根据(索引)优化 查询 速度

MySQL 千万 级数据量根据(索引)优化 查询 速度

从数据库代码和服务器对PHP网站Mysql做性能优化