SQL中,可以在
WHERE
子句和
ORDER BY
子句中使用索引,目的是在WHERE子句中避免全表扫描,在ORDER BY子句避免使用FileSort排序。当然,某些情况下全表扫描,或者FileSort排序不一定比索引慢。但总的来说,我们还是要避免,以提高查询效率。
尽量使用Index完成
ORDER BY
排序。如果WHERE和
ORDER BY
后面是相同的列就使用单索引列;如果不同 就使用联合索引。
无法使用Index时,需要对FileSort方式进行调优。
1.2测试
删除student表和class表中已创建的索引。
#方式1: DROPINDEX idx_monitor ONclass;
DROPINDEX idx_cid ON student; DROPINDEX idx_age ON student;DROPINDEX idx_name ON student ; DROPINDEX idx_age_name_classid ON student ;DROPINDEX idx_age_classid_name ON student ;
以下是否能使用到索引,能否去掉
using filesort
过程一:
EXPLAINSELECT SQL_NO_CACHE * FROM student ORDERBY age,classid;
EXPLAINSELECT SQL_NO_CACHE * FROM student ORDERBY age,classid limit10;
过程二: order by时不limit,索引失效
#创建索引 CREATEINDEX idx_age_classid_name ON student (age,classid, NAME); #不限制,索引失效 EXPLAINSELECT SQL_NO_CACHE * FROM student ORDERBY age ,classid ;
这里优化器觉得,,还需要回表。会费时间更大,不走索引。
使用覆盖索引试试看
不用回表,优化器觉得走索引快。就使用了索引。
增加limit 条件
增加limit 减少回表的数量,优化器觉得走索引快,会使用索引
过程三: order by时顺序错误,索引失效
CREATEINDEX idx_age_classid_stuno ON student (age,classid,stuno) ;
#以下哪些索引失效?
# 不会走,最左前缀原则 EXPLAINSELECT* FROM student ORDERBY classid LIMIT10;
# 不会走,最左前缀原则 EXPLAINSELECT* FROM student ORDERBY classid,NAMELIMIT10;
# 走 EXPLAINSELECT* FROM student ORDERBY age,classid, stuno LIMIT10; # 走 EXPLAINSELECT *FROM student ORDERBY age,classid LIMIT10; # 走 EXPLAINSELECT * FROM student ORDERBY age LIMIT10;
过程四: order by时规则不一致,索引失效(顺序错,不索引; 方向反,不索引)
# age desc 方向反 索引失效 EXPLAIN SELECT * FROM student ORDER BY age DESC, classid ASC LIMIT 10;
# 没有最左前缀 索引失效 EXPLAIN SELECT * FROM student ORDER BY classid DESC, NAME DESC LIMIT 10;
# age asc 没问题 classid desc 降序, 优化器认为,文件排序比较快索引失效 # 方向反了不走索引 EXPLAIN SELECT * FROM student ORDER BY age ASC, classid DESC LIMIT 10;
# Backward index scan 走索引了,,倒着走索引 EXPLAIN SELECT * FROM student ORDER BY age DESC, classid DESC LIMIT 10;
过程五:无过滤,不索引
EXPLAINSELECT * FROM student WHERE age=45ORDERBY classid;
EXPLAINSELECT * FROM student WHERE age=45ORDERBY classid , name;
EXPLAINSELECT *FROM student WHERE classid=45orderby age;
EXPLAINSELECT * FROM student WHERE classid=45orderby age limit10;
这里第一条排序走Using filesort 很好理解
第二条为啥不是 Using filesort 呢?
这里
type = index
,
key=idx_age_classid_name
。这说明了 优化器预估对
idx_age_classid_name
索引进行完整的遍历。由于索引本身就是根据age升序存储的。。
order by 能使用索引最左前缀 - ORDER BY a - ORDER BY a, b - ORDER BY a , b, c - ORDER BY a DESC, b DESC,c DESC
# 如果WHERE使用索引的最左前缀定义为常量,则order by 能使用索引 - WHERE a = const ORDER BY b, c - WHERE a = const AND b = const ORDER BY c - WHERE a = const ORDER BY b, c - WHERE a = const AND b > const ORDER BY b , c
# 不能使用索引进行排序 - ORDER BY a ASC, b DESC, c DESC/*排序不一致*/ - WHERE g = const ORDER BY b,c/*丢失a索引*/ - WHERE a = const ORDER BY c/*丢失b索引*/ - WHERE a = const ORDER BY a, d /*d不是索引的一部分*/ - WHERE a in (...) ORDER BY b,c /*对于排序来说,多个相等条件也是范围查询*/
索引只会用到一个,没办法一个索引用来where 一个索引用来
order by
。
但是可以建立联合索引。
1.3案例实战
ORDER BY
子句,尽量使用Index方式排序,避免使用FileSort方式排序。
执行案例前先清除student上的索引,只留主键:
DROPINDEX idx_age ON student; DROPINDEX idx_age_classid_stuno ON student;DROPINDEX idx_age_classid_name ON student; #或者 call proc_drop_index( 'my_sql' , ' student' ) ;
showindexfrom student;
场景:查询年龄为30岁的,且学生编号小于101000的学生,按用户名称排序
EXPLAINSELECT SQL_NO_CACHE * FROM student WHERE age = 30AND stuno <101000ORDERBYNAME;
mysql> SELECT SQL_NO_CACHE * FROM student WHERE age = 30AND stuno <101000ORDERBYNAME; +-----+--------+--------+------+---------+ | id | stuno | name | age | classId | +-----+--------+--------+------+---------+ | 417 | 100417 | bBAYtX | 30 | 159 |