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索引优化的主要内容,如果未能解决你的问题,请参考以下文章