当以name,age,position设置为联合索引。
eg:
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei';
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22;
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22 AND position ='manager';
如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。
EXPLAIN SELECT * FROM employees WHERE name = 'Bill' and age = 31;
EXPLAIN SELECT * FROM employees WHERE age = 30 AND position = 'dev';
EXPLAIN SELECT * FROM employees WHERE position = 'manager';
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei';
EXPLAIN SELECT * FROM employees WHERE left(name,3) = 'LiLei';
给hire_time增加一个普通索引:
ALTER TABLE `employees` ADD INDEX `idx_hire_time` (`hire_time`) USING BTREE ;
EXPLAIN select * from employees where date(hire_time) ='2018‐09‐30';
还原最初索引状态
ALTER TABLE `employees` DROP INDEX `idx_hire_time`;
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22 AND position ='manage r';
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age > 22 AND position ='manage r';
EXPLAIN SELECT name,age FROM employees WHERE name= 'LiLei' AND age = 23 AND position ='manager';
EXPLAIN SELECT * FROM employees WHERE name != 'LiLei';
EXPLAIN SELECT * FROM employees WHERE name is null
EXPLAIN SELECT * FROM employees WHERE name like '%Lei'
问题:解决like'%字符串%'索引不被使用的方法?
a)使用覆盖索引,查询字段必须是建立覆盖索引字段
EXPLAIN SELECT name,age,position FROM employees WHERE name like '%Lei%';
b)如果不能使用覆盖索引则可能需要借助搜索引擎
EXPLAIN SELECT * FROM employees WHERE name = '1000';
EXPLAIN SELECT * FROM employees WHERE name = 1000;
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' or name = 'HanMeimei';
给年龄添加单值索引
ALTER TABLE `employees` ADD INDEX `idx_age` (`age`) USING BTREE ;
explain select * from employees where age >=1 and age <=2000;
没走索引原因:mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引。比如这个例子,可能是 由于单次数据量查询过大导致优化器最终选择不走索引
优化方法:可以将大的范围拆分成多个小范围
explain select * from employees where age >=1 and age <=1000;
explain select * from employees where age >=1001 and age <=2000;
还是以name,age,position设置为联合索引。
1.联合索引第一个字段用范围不会走索引
EXPLAIN SELECT * FROM employees WHERE name > 'LiLei' AND age = 22 AND position ='manager';
2.强制走索引
KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE //添加索引
关掉查询缓存:
set global query_cache_size=0
set global query_cache_type=0
1.EXPLAIN SELECT * FROM employees WHERE name > 'LiLei'
强制走索引:
2.EXPLAIN SELECT * FROM employees force index(idx_name_age_position) WHERE name > 'LiLei'
3、覆盖索引优化 :
1比2执行的时间更短,效率快并没走索引;如果想优化的话,则添加覆盖索引。
EXPLAIN SELECT name,age,position FROM employees WHERE name > 'LiLei' //这样走的是覆盖索引
4、in和or在表数据量比较大的情况会走索引,在表记录不多的情况下会选择全表扫描
EXPLAIN SELECT * FROM employees WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager';
5、like KK% 一般情况都会走索引,like KK%其实就是用到了索引下推优化
EXPLAIN SELECT * FROM employees WHERE name like 'LiLei%' AND age = 22 AND position ='manager';
对于辅助的联合索引(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% 也不一定就会走索引下推。
Mysql如何选择合适的索引
mysql> EXPLAIN select * from employees where name > 'a'; //没有走索引
mysql> EXPLAIN select * from employees where name > 'zzz' ; //部分走索引
mysql最终如何选择索引,我们可以用trace工具来一查究竟,开启trace工具会影响mysql性能,所以只能临时分析sql使用,用完之后立即关闭
trace工具用法:
mysql> set session optimizer_trace="enabled=on",end_markers_in_json=on; ‐‐开启trace
mysql> set session optimizer_trace="enabled=off"; ‐‐关闭trace
mysql> select * from employees where name > 'a' order by position;
mysql> SELECT * FROM information_schema.OPTIMIZER_TRACE;
常见sql深入优化
Order by与Group by优化
ALTER TABLE `employees` ADD INDEX `idx_name_age_position` (`name`,`age`,`position`) USING BTREE //添加索引
KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE //添加索引后,表语句中的展示
1.利用最左前缀法则:中间字段不能断,因此查询用到了name索引,从key_len=74也能看出,age索引列用在排序过程中,因为Extra字段里没有using filesort
2.EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' order by position ; //不走索引
从explain的执行结果来看:key_len=74,查询使用了name索引,由于用了position进行排序,跳过了age,出现了Using filesort。
3.EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' order by age,position;//走了索引.
查找只用到索引name,age和position用于排序,无Using filesort。
4.EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' order by position,age; //走了部分索引,但也有Using filesort。
和Case 3中explain的执行结果一样,但是出现了Using filesort,因为索引的创建顺序为
name,age,position,但是排序的时候age和position颠倒位置了。
5.EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' and age=18 order by position,age;
部分走索引 ,与Case 4对比,在Extra中并未出现Using filesort,因为age为常量,在排序中被优化,所以索引未颠倒,不会出现Using filesort。
6.EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' order by age asc,positione desc; 部分走索引,
7.EXPLAIN SELECT * FROM employees WHERE name in('LiLei','zhuge') order by age,position;不走索引。对于排序来说,多个相等条件也是范围查询
8. EXPLAIN SELECT * FROM employees WHERE name >'a' order by name;
不走索引,有Using filesort,但是可以用覆盖索引优化
9. EXPLAIN SELECT name,age,position FROM employees WHERE name >'a' order by name;
走索引,有Using where也有Usingindex
1、MySQL支持两种方式的排序filesort和index,Using index是指MySQL扫描索引本身完成排序。index效率高,filesort效率低。
2、order by满足两种情况会使用Using index。
1) order by语句使用索引最左前列。
2) 使用where子句与order by子句条件列组合满足索引最左前列。
3、尽量在索引列上完成排序,遵循索引建立(索引创建的顺序)时的最左前缀法则。
4、如果order by的条件不在索引列上,就会产生Using filesort。
5、能用覆盖索引尽量用覆盖索引
6、group by与order by很类似,其实质是先排序后分组,遵照索引创建顺序的最左前缀法则。对于group by的优化如果不需要排序的可以加上order by null禁止排序。注意,where高于having,能写在where中的限定条件就不要去having限定了。
单路排序:是一次性取出满足条件的所有字段,然后在sort buffer中进行排序;用trace工具可以看到sort_mode信息显示
双路排序:(又叫回表排序模式):是首先根据相应的条件取出相应的排序字段和可以直接定位行数据的行ID,然后在sort buffer中进行排序,排序完后需要再次取出其他需要的字段;用trace工具可以看到sort_mode信息里显示
mysql通过比较系统max_length_for_sort_data(默认1024字节)的大小和需要查询的字段总大小来判断使用哪种排序模式。
如果字段的总长度小于max_length_for_sort_data,使用 单路排序
如果字段的总长度大于max_length_for_sort_data,使用 双路排序、
验证单路排序还是双路排序
EXPLAIN SELECT * FROM employees WHERE name = 'zhuge' order by position; //没有走索引,而是文件排序
我们先看单路排序的详细过程:
1. 从索引name找到第一个满足 name = ‘zhuge’ 条件的主键 id
2. 根据主键 id 取出整行,取出所有字段的值,存入 sort_buffer 中
3. 从索引name找到下一个满足 name = ‘zhuge’ 条件的主键 id
4. 重复步骤 2、3 直到不满足 name = ‘zhuge’
5. 对 sort_buffer 中的数据按照字段 position 进行排序
6. 返回结果给客户端
我们再看下双路排序的详细过程:
1. 从索引 name 找到第一个满足 name = ‘zhuge’ 的主键id
2. 根据主键 id 取出整行,把排序字段 position 和主键 id 这两个字段放到 sort buffer 中
3.从索引name找到下一个满足 name = ‘zhuge’ 条件的主键 id
4. 重复 3、4 直到不满足 name = ‘zhuge’
5. 对 sort_buffer 中的字段 position 和主键 id 按照字段 position 进行排序
6. 遍历排序好的 id 和字段 position,按照 id 的值回到原表中取出 所有字段的值返回给客户端
其实对比两个排序模式,单路排序会把所有需要查询的字段都放到 sort buffer 中,而双路排序只会把主键和需要排序的字段放到 sort buffer 中进行排序,然后再通过主键回到原表查询需要的字段。
注意,如果全部使用sort_buffer内存排序一般情况下效率会高于磁盘文件排序,但不能因为这个就随便增大sort_buffer(默认1M),mysql很多参数设置都是做过优化的,不要轻易调整。
1. 代码先行,索引后上
一般应该等到主体业务功能开发完毕,把涉及到该表相关sql都要拿出来分析之后再建立 索引
2. 联合索引尽量覆盖条件
比如可以设计一个或者两三个联合索引(尽量少建单值索引),让每一个联合索引都尽量去包含sql语句里的where,order by,group by的字段,还要确保这些联合索引的字段顺序尽量满足sql查询的最左前缀原则
3. 不要在小基数字段上建立索引
性别字段, 其值不是男就是女,那么该字段的基数就是2;小基数字段建立索引的话,还不如全表扫描了。
4. 长字符串我们可以采用前缀索引
尽量对字段类型较小的列设计索引,比如说什么tinyint之类的,因为字段类型较小的话,占用磁盘空间也会 比较小,此时你在搜索的时候性能也会比较好一点。
但很多时候你就是要针对varchar(255)这种字段建立索引,哪怕多占用一些磁盘空间也是有必要的。 对于这种varchar(255)的大字段可能会比较占用磁盘空间,可以稍微优化下,比如针对这个字段的前20个 字符建立索引,就是说,对这个字段里的每个值的前20个字符放在索引树里,类似于 KEY index(name(20),age,position),但这种情况就不能满足order by
5. where 与order by 冲突时优先where
在where和order by出现索引设计冲突时,到底是针对where去设计索引,还是针对order by设计索引?到 底是让where去用上索引,还是让order by用上索引?
一般这种时候往往都是让where条件去使用索引来快速筛选出来一部分指定的数据,接着再进行排序。 因为大多数情况基于索引进行where筛选往往可以最快速度筛选出你要的少部分数据,然后做排序的成本可能会小很多。
6. 基于慢sql查询做优化
可以根据监控后台的一些慢sql,针对这些慢sql查询做特定的索引优化。
比如设置了set global long_query_time=4 ,执行时间超过4秒,则判定为慢sql;
注意:使用命令 set global long_query_time=4修改后,需要重新连接或新开一个会话才能看到修改值。开启慢sql查询也会影响性能,所以一般在从库里开启慢sql查询
尽量利用一两个复杂的多字段联合索引,抗下你80%以上的查询,然后用一两个辅助索引尽量抗下剩余的一些非典型查询,保证这种大数据量表的查询尽可能多的都能充分利用索引,这样就能保证你的查询速度和性能了!
但是如果联合索引建的太多,但是插入,更新,删除的操作会比较慢。