4.1 查询执行路径
4.2 查询状态:可用SHOW FULL PROCESSLIST命令查看查询状态 4.3 MySQL如何执行关联查询:嵌套循环 4.4 MySQL的临时表是没有索引的,在编写复杂的子查询和关联查询的时候需要注意这一点 5.MySQL查询优化器的缺陷 5.1 避免用IN(子查询) 如SELECT * FROM sakila.film WHERE file_id IN( SELECT film_id FROM sakila.film_actor WHERE actor_id = 1; ) 我们会认为上面的查询会这样执行: --SELECT GROUP_CONCAT(film_id) FROM sakila.film_actor WHERE actor_id = 1; --Result:1,23,25,106,140,166,277,361,436,499,506,509,605,635,749,832,939,970,980 SELECT * FROM sakila.film WHERE film_id IN(1,23,25,106,140,166,277,361,436,499,506,509,605,635,749,832,939,970,980) 但MySQL实际会将查询改写成下面这样的关联子查询: SELECT * FROM sakila.film WHERE EXISTS( SELECT * FROM sakila.film_actor WHERE actor_id = 1 AND film_actor.film_id = film.film_id); 性能非常差 可用如下语句改写: SELECT film.* FROM sakila.film INNER JOIN sakila.film_actor USING(film_id) WHERE actor_id = 1;‘ 另一个优化的方法是使用GROUP_CONCAT()在IN()中构造一个由逗号分隔的列表 注1:关联子查询:在关联子查询中,对于外部查询返回的每一行数据,内部查询都要执行一次。另外,在关联子查询中是信息流是双向的。外部查询的每行数据传递一个值给子查询,然后子查询为每一行数据执行一次并返回它的记录。然后,外部查询根据返回的记录做出决策。 注2:可用EXPLAIN EXTENDED来查看这个查询被改写成什么样子 注3:前面提到关联子查询效率低,可以用join代替,但其实不是所有关联子查询效率都低,应当测试 注4:可用SHOW STATUS 查看扫描的行数 5.2 如果要取出UNION ALL的两个子句的前N条可以在子句里面和外面都加上LIMIT N; 5.3 MIN()和MAX(): 看这个语句:SELECT MIN(actor_id) FROM sakila.actor WHERE first_name = 'PENELOPE'; first_name上没有索引所以会全表扫描,因为主键是按照从小到大排列,理论上扫描到第一个满足条件的记录就是了,可是MySQL却只会全表扫表,重写: SELECT actor_id FROM sakila.actor USE INDEX(PRIMARY) WHERE first_name = 'PENELOPE' LIMIT 1; 6. 优化特定类型的查询 6.1 COUNT()或COUNT(*)统计行数,COUNT(exp)统计exp有值的结果数 条件反转优化: 将SELECT COUNT(*) FROM world.City WHERE ID > 5; 优化为: SELECT (SELECT COUNT(*) FROM world.City)-COUNT(*) WHERE world.City WHERE ID < 5; 近似值优化,有些业务不需要精确值,可以用EXPLAIN出来的rows近似值代替: 6.2 优化关联查询: 确保ON或者USING子句中的列上有索引 确保GROUP BY和ORDER BY中的表达式只涉及一个表中的列,这样MySQL才能使用索引来优化这个过程 6.3 优化子查询 尽可能使用关联查询代替(MySQL5.6后可忽略这条) 6.4优化LIMIT分页 大偏移量时LIMIT效率很差,因为他会扫描很多然后丢弃他们 SELECR film_id,description FROM sakila.film ORDER BY title LIMIT 50,5 "延迟关联"优化为: SELECT film.film_id , film.description FROM sakila.film INNER JOIN( SELECT film_id FROM sakila.film ORDER BY title LIMIT 50,5 )AS lim USING(film_id) 另一种优化是,预先存储排名信息,然后优化为: SELECT film_id,description FROM sakila.film WHERE position BETWEEN 50 AND 54 ORDER BY position 6.5 优化UNION查询 MySQL总是通过创建并填充临时表的方式来执行UNION查询,因此很多优化策略在UNION中都没法很好地使用,经常需要手动的将WHERE,LIMIT,ORDER BY等子句“下推”到UNION的各个子查询中,以便优化器利用这些条件进行优化。 除非确实需要消除重复的行,否则就一定要使用UNION ALL,如果没有ALL,MySQL就会给临时表加上DISTINCT选项,代价很高 7.其他注意点: 7.1不要用前置%和前后双%,这会使索引失效 7.2 索引的最左前缀匹配 7.3 使用left join或not exists来优化not in(会使索引失效) 7.4 避免隐式转换,这会使索引失效 7.5 使用in代替or,in可利用索引 7.6 不要join太多表 7.7 程序中使用预编译语句:防止SQL注入;同步地使用执行计划;一次解析,多次利用 7.8 程序连接不同数据库使用不同的帐号,禁止跨库查询:降低业务耦合度;安全;为了以后的分库分表和数据库迁移 7.9 禁止使用不含字段列表的INSERT语句 7.10 减少同数据库的交互次数 7.11 禁止用ORDER BY rand()进行随机排序:这会把表中所有符合条件的数据装载到内存中;推荐在程序中获取随机值到数据库中取相应的数据 7.12 禁止在WHERE中对列进行函数转换和计算,这回导致索引失效