SQL优化汇总
今天面某家公司,然后问我SQL优化,感觉有点忘了,今天特此总结一下:
总结得是分两方面:索引优化和查询优化;
一. 索引优化:
1. 独立的列
在进行查询时,索引列不能是表达式的一部分,也不能是函数的参数,否则无法使用索引。
例如下面的查询不能使用 actor_id 列的索引:
#这是错误的 SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
优化方式:可以将表达式、函数操作移动到等号右侧。如下:
SELECT actor_id FROM sakila.actor WHERE actor_id = 5 - 1;
2. 多列索引
SELECT film_id, actor_ id FROM sakila.film_actor WHERE actor_id = 1 AND film_id = 1;
3. 索引列的顺序
索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,每个记录的区分度越高,查询效率也越高。
例如下面显示的结果中 customer_id 的选择性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。
SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity, COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity, COUNT(*) FROM payment; #结果如下 staff_id_selectivity: 0.0001 customer_id_selectivity: 0.0373 COUNT(*): 16049
4. 前缀索引
5. 覆盖索引
-
-
一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。
-
对于 InnoDB 引擎,若辅助索引能够覆盖查询,则无需访问主索引。
以下为优先使用索引,避免全表扫描:
6. mysql在使用like进行模糊查询的时候把%放后面,避免开头模糊查询
因为mysql在使用like查询的时候只有使用后面的%时,才会使用到索引。
#进行全表查询,没有用到索引 EXPLAIN SELECT * FROM `user` WHERE username LIKE '%ptd_%'; EXPLAIN SELECT * FROM `user` WHERE username LIKE '%ptd_'; #有用到索引 EXPLAIN SELECT * FROM `user` WHERE username LIKE 'ptd_%';
再比如:经常用到的查询数据库中姓张的所有人:
SELECT * FROM `user` WHERE username LIKE '张%';
7. 在表中建立索引,优先考虑where、group by使用到的字段
8. 尽量避免使用in 和not in,会导致数据库引擎放弃索引进行全表扫描。
比如:
SELECT * FROM t WHERE id IN (2,3) SELECT * FROM t1 WHERE username IN (SELECT username FROM t2)
优化方式:如果是连续数值,可以用between代替。如下:
SELECT * FROM t WHERE id BETWEEN 2 AND 3
如果是子查询,可以用exists代替。如下:
SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t2 WHERE t1.username = t2.username)
9. 尽量避免使用or,会导致数据库引擎放弃索引进行全表扫描
如:
SELECT * FROM t WHERE id = 1 OR id = 3
优化方式:可以用union代替or。如下:
SELECT * FROM t WHERE id = 1 UNION SELECT * FROM t WHERE id = 3
10. 尽量避免进行null值的判断,会导致数据库引擎放弃索引进行全表扫描
SELECT * FROM t WHERE score IS NULL
优化方式:可以给字段添加默认值0,对0值进行判断。如下:
SELECT * FROM t WHERE score = 0
11. 尽量避免在where条件中等号的左侧进行表达式、函数操作,会导致数据库引擎放弃索引进行全表扫描
同第1个,单独的列;
SELECT * FROM t2 WHERE score/10 = 9 SELECT * FROM t2 WHERE SUBSTR(username,1,2) = 'li'
优化方式:可以将表达式、函数操作移动到等号右侧。如下:
SELECT * FROM t2 WHERE score = 10*9 SELECT * FROM t2 WHERE username LIKE 'li%'
12. 当数据量大时,避免使用where 1=1的条件。通常为了方便拼装查询条件,我们会默认使用该条件,数据库引擎会放弃索引进行全表扫描
SELECT * FROM t WHERE 1=1
优化方式:用代码拼装sql时进行判断,没where加where,有where加and。
索引的缺点:在数据库进行DML操作的时候,除了维护数据表之外,还需要维护索引表,运维成本增加。
应用场景:数据量比较大,查询字段较多的情况。
索引规则:
1.选用选择性高的字段作为索引,一般unique的选择性最高;
2.复合索引:选择性越高的排在越前面。(左前缀原则);
3.如果查询条件中两个条件都是选择性高的,最好都建索引;
参考:
2. SQL优化面试
二、查询优化
1.
-
-
key : 使用的索引;
-
rows : 扫描的行数;
2.
-
只返回必要的列:最好不要使用 SELECT * 语句。
-
只返回必要的行:使用 LIMIT 语句来限制返回的数据。
-
缓存重复查询的数据:使用缓存可以避免在数据库中进行查询,特别在要查询的数据经常被重复查询时,缓存带来的查询性能提升将会是非常明显的。
-
减少服务器端扫描的行数
-
最有效的方式是使用索引来覆盖查询。
3.
将一个大连接查询分解成对每一个表进行一次单表查询,然后在应用程序中进行关联,这样做的好处有:
-
让缓存更高效:对于连接查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它表的查询缓存依然可以使用。
-
分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余记录的查询。
-
减少锁竞争;
-
在应用层进行连接,可以更容易对数据库进行拆分,从而更容易做到高性能和可伸缩。
-
查询本身效率也可能会有所提升。例如下面的例子中,使用 IN() 代替连接查询,可以让 MySQL 按照 ID 顺序进行查询,这可能比随机的连接要更高效。
SELECT * FROM tab JOIN tag_post ON tag_post.tag_id=tag.id JOIN post ON tag_post.post_id=post.id WHERE tag.tag='mysql'; SELECT * FROM tag WHERE tag='mysql'; SELECT * FROM tag_post WHERE tag_id=1234; SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);