MySQL查询优化学习
学习自《高性能MySQL 第三版》和网络资源
一张图了解MySQL执行SQL语句过程:
是否向数据库请求了不需要的数据
- 查询了不需要的记录
- 多表关联时返回全部列
- 总是取出全部列
解决方案:1、使用 LIMIT;2、避免使用 *,从而有机会使用索引覆盖查询。
增加合适的索引
使用EXPLAIN分析查询,type 列反应了查询的类型。从全表扫描、索引扫描、范围扫描、唯一索引扫描、常数引用等,速度由慢到快,扫描的行数由大到小。
一般MySQL能够使用如下三种方式应用WHERE条件,从好到坏依次为:
- 在索引中使用WHERE条件来过滤不匹配的记录。这是在存储引擎层完成的。
- 使用索引覆盖扫描(在Extra列中出现了Using index)来返回记录,直接从索引中过滤不需要的记录并返回命中的结果。这是在MySQL服务器层来完成的,但无需再返回查询记录。
- 从数据表中返回数据,然后过滤不满足条件的记录(在Extra列中出现Using Where)。这在M月SQL服务器层完成,MySQL需要先从数据表读出记录然后过滤。
重构查询
1、切分查询
例如删除旧数据,定期地清除大量数据时,如果用一个大的语句一次性完成的话,则可能需要一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。将一个大的DELETE语句切分成多个较小的查询可以尽可能小地影响MySQL性能。
2、分解关联查询
将一个关联查询分解为多个小的查询有几个优点:
- 让缓存的效率更高。一是应用的缓存可以缓存部分小的查询,从而不需要每次都执行大查询;二是MySQL的查询缓存,如果关联中的某个表发生了变化,那么就无法使用查询缓存了,而拆分后,如果某个表发生改变,那么其他表的查询缓存可能仍然有效。
- 将查询分解后,执行单个查询可以减少锁的竞争。
- 在应用层做关联,可以更容易对数据库进行拆分,更容易做到高性能和可扩展。
3、查询语句优化
IN子查询优化:
UNION查询:
(SELECT first_name, last_name FROM sakila.actor ORDER BY first_name)
UNION ALL (SELECT first_name, last_name FROM sakila.actor ORDER BY last_name) LIMIT 20
重写为:
(SELECT first_name, last_name FROM sakila.actor ORDER BY first_name LIMIT 20)
UNION ALL (SELECT first_name, last_name FROM sakila.actor ORDER BY last_name LIMIT 20) LIMIT 20
可减少中间表的记录数。
索引合并优化:
考虑建立联合索引。
最大值和最小值优化:
SELECT MIN(actor_id) FROM actor WHERE first_name = 'kun';
重写为:
SELECT actor_id FROM actor USE INDEX(PRIMARY) WHERE first_name = 'kun' LIMIT 1;
利用了InnoDB的聚簇索引。
优化LIMIT分页:
SELECT film_id, description FROM film ORDER BY title LIMIT 50, 5;
“延迟关联”重写为:
SELECT film_id, description FROM film INNER JOIN ( SELECT film_id FROM film ORDER BY title LIMIT 50, 5) AS lim USING(film_id);
另外一种更好的方法是记录上次查询到的位移量,下次查询另一页时,从该位移量开始计算。
查询优化器的提示
官方手册,慢慢看吧:Chapter 8 Optimization
突然感觉看MySQL官方手册才是硬道理。