- 插入三条数据
INSERT INTO employees(name,age,position,hire_time) VALUES(‘LiLei’,22,’manager’,NOW());
INSERT INTO employees(name,age,position,hire_time) VALUES(‘HanMeimei’, 23,’dev’,NOW());
INSERT INTO employees(name,age,position,hire_time) VALUES(‘Lucy’,23,’dev’,NOW()); - 存储过程,插入100000条测试数据
drop procedure if exists insert_emp;
delimiter ;;
create procedure insert_emp()
begin
declare i int;
set i=1;
while(i<=100000)do
insert into employees(name,age,position) values(CONCAT(‘spy’,i),i,’dev’);
set i=i+1;
end while;
end;;
delimiter ;
call insert_emp();
首先还是来研究下sql在什么情况下会走索引
创建了一张表,employees,表结构及数据如下,大概10w条数据
建表及插入数据的存储过程如下
#创建表
CREATE TABLE employees2 (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(24) NOT NULL DEFAULT ‘’ COMMENT ‘姓名’,
age int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘年龄’,
position varchar(20) NOT NULL DEFAULT ‘’ COMMENT ‘职位’,
hire_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ‘入职时间’,
PRIMARY KEY (id),
KEY idx_name_age_position (name,age,position) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT=’员工记录表’;
插入三条数据
INSERT INTO employees(name,age,position,hire_time) VALUES(‘LiLei’,22,’manager’,NOW());
INSERT INTO employees(name,age,position,hire_time) VALUES(‘HanMeimei’, 23,’dev’,NOW());
INSERT INTO employees(name,age,position,hire_time) VALUES(‘Lucy’,23,’dev’,NOW());
存储过程,插入100000条测试数据
drop procedure if exists insert_emp;
delimiter ;;
create procedure insert_emp()
begin
declare i int;
set i=1;
while(i<=100000)do
insert into employees(name,age,position) values(CONCAT(‘spy’,i),i,’dev’);
set i=i+1;
end while;
end;;
delimiter ;
call insert_emp();
联合索引的第一个字段是范围查找时,有可能不走索引
我这里有三条SQL语句,如下
EXPLAIN SELECT FROM employees WHERE name = ‘lilei’ AND age = 22 AND position = ‘manager’ ;
EXPLAIN SELECT FROM employees WHERE name > ‘lilei’ AND age = 22 AND position = ‘manager’ ;
EXPLAIN SELECT * FROM employees WHERE name = ‘lilei’ AND age = 22 AND position > ‘manager’ ;
三条sql语句对应的执行结果分别为结果1,结果2,结果3


1. 我们可以看到,结果1和结果3,均走满了3个索引,结果2把大于号放到了字段第一位的时候,反而age和position
没有触发索引,如果第一个字段为范围的话,可能会导致后面的索引失效,但是并非绝对会走索引,通常情况mysql会
根据索引效率以及表大小来评估究竟是否会走索引,并非一定。
- 根据以上表中插入的数据,我在表中插入的name字段为leeAng(i), i = 1; i++,一直递增到100000,而我的查询条件是查询name>lilei
的字段,leeAng 和Lilei 首字母都为L ,在对“name”进行范围查询的时候,mysql在索引树上可以匹配到name = L 这个字段,然后根据
L往后面匹配,自然会走索引。如果我在表中插入其他字母开头的字段,则不会走索引。如下:
我在表中插入以spy为开头的name,然后执行
EXPLAIN SELECT FROM employees2 WHERE name > ‘lilei’ AND age = 22 AND position = ‘manager’ ;
则不会走索引,如下图所示:
可以看到type = ALL ,表示为全表检索,检索行数97554行。如果explain的结果显示,
在possible_keys字段中标识了可能会用到索引,而实际查询中在key字段中没有用到索引,那么可以使用强制索引来让SQL语句
强制使用索引,如:
EXPLAIN SELECT FROM employees2 FORCE INDEX(idx_name_age_position) WHERE name > ‘LiLei’ AND age = 22 AND position = ‘manager’ ;
这时我们观察到使用了FORCE INDEX(idx_name_age_position)后,索引生效了,检索行数48777行,但是我们来看下两种情况下的查询耗时
我现在执行sql
EXPLAIN SELECT FROM employees2 WHERE name > ‘lilei’
只对name字段进行范围查询,观察下走强制索引和不走索引会有什么区别
不走索引,检索时间为0.252s
走强制索引,检索时间0.958s
所以,针对这种情况,我们建议使用覆盖索引,即查询的字段都包括在联合索引中,这样也会走索引查询,如下
EXPLAIN SELECT name,age,position FROM employees2 WHERE name > ‘LiLei’ AND age = 22 AND position = ‘manager’ ;
*少用or或in,或用它查询时,MySQL不一定走索引
创建了employee3表,其中只有3条数据
我们拿employee2和employee3分别使用in 查询
有如下两条sql语句,看下分析器分析效果:
employee2的执行结果为走索引查询:
employee3的执行结果为全表扫描:
所以,mysql内部优化器会根据检索比例,表大小,表数据等多个因素评估是否需要走索引。
like KK% 不管表大小都会走索引
我们使用只有三条数据的employee3和有100000条数据的employee2来对比下

可以看到,无论表中的数据有多大,结果1和结果2均走满了140长度的索引
为什么like也是范围查找,而name>’lilei’也是范围查找,为什么like就一定会走索引呢?这里有个概念:
索引下推(Index Condition Pushdown,ICP), like KK%其实就是用到了索引下推优化
什么是索引下推?
对于辅助的联合索引(name,age,position),正常情况按照最左前缀原则,
SELECT * FROM employees WHERE name like ‘LiLei%’ AND age = 22 AND position =’manager’
这种情况只会走name字段索引,因为根据name字段过滤完,得到的索引行里的age和
position是无序的,无法很好的利用索引。 在MySQL5.6之前的版本,这个查询只能在联合索引里匹配到名字是 ‘LiLei’ 开头的索引,然后拿这些索引对应的主键逐个回表,到主键索 引上找出相应的记录,再比对age和position这两个字段的值是否符合。
MySQL 5.6引入了索引下推优化,可以在索引遍历过程中,对索引中包含的所有字段先做判断,过滤掉不符合条件的记录之后再回表,可 以有效的减少回表次数。使用了索引下推优化后,上面那个查询在联合索引里匹配到名字是 ‘LiLei’ 开头的索引之后,同时还会在索引里过 滤age和position这两个字段,拿着过滤完剩下的索引对应的主键id再回表查整行数据。
索引下推会减少回表次数,对于innodb引擎的表索引下推只能用于二级索引,innodb的主键索引(聚簇索引)树叶子节点上保存的是全行数据,所以这个时候索引下推并不会起到减少查询全行数据的效果。
为什么范围查找Mysql没有用索引下推优化?
估计应该是Mysql认为范围查找过滤的结果集过大,like KK% 在绝大多数情况来看,过滤后的结果集比较小,所以这里Mysql选择给 like KK% 用了索引下推优化,当然这也不是绝对的,有时like KK% 也不一定就会走索引下推。
