优化慢SQL
- 1.避免不必要的列
- 2.分页优化
- 3.索引优化
-
- 利用索引覆盖
- [避免使用or != <> 查询](#避免使用or != <> 查询)
- 避免列上的函数运算
- 使用联合索引时,注意最左匹配原则
- [4.JOIN 优化](#4.JOIN 优化)
- 5.排序优化
慢SQL的优化,主要从两个方面考虑,SQL语句本身的优化,以及数据库设计的优化。
1.避免不必要的列
SQL查询的时候,应该只查询需要的列,而不要包含额外的列,像 select * 这种写法就应该尽量避免。
这种方式会浪费CPU内存资源也会加剧网络消耗,同时也可能导致索引失效。
2.分页优化
在数据量比较大,分页比较深的情况下,需要考虑分页的优化。
sql
select * from student where age=18 order by id asc limit 10000,10
索引覆盖
sql
select id, age from student where age=18 order by id asc limit 10000,10
id偏移量
偏移量就是找到 limit 第一个参数对应的主键值,根据之歌逐渐值再去过滤并limit
sql
select * from student
where age = 18 and id >= (select id from student where age=18 limit 100000,1) limit 10;
游标分页
使用上次查询的最大值来当作这次的查询条件
需要一个列来记录上一次查询的最大值(通常是主键),并且满足查询条件时主键需要是有序的
因为本次查询需要依赖上一次查询的主键最大值,因此分页查询只能是连续的,不能进行跳页(比如查完第1页直接查第21页)
sql
select * from student where age=18 and id > 上次查询最大记录 limit 10;
延迟关联
先取出满足条件的id,然后通过id去重新关联。好处是id可以是不连续的
sql
select * from student s
inner join (select id tmp_id from student where age = 18 limit 10000,10) tmp on s.id = tmp_id
3.索引优化
利用索引覆盖
InnoDB 使用二级索引查询数据时会回表,但如果索引的叶节点中已经包含要查询的字段,那它就没必要再回表查询了,这就叫索引覆盖,可以简单的理解为查询列都是索引列。
sql
alter table test add index idx_a_b(a, b);
select a, b from test where a='北京';
避免使用or != <> 查询
在早起MySQL版本使用or查询可能会导致索引失效,高版本引入了索引合并,解决了这个问题。但在实际使用中还是规范写法,能不用就少用。
sql
id<>'abc' <===> id>'abc' or id<'abc'
-- 这是针对单一有索引的字段,对于多字段可用用union替代,那么语句就有一半可以走索引
id>'abc' or name<'abc' <===> id>'abc' union name<'abc'
避免列上的函数运算
下面的SQL语句就不会走索引
sql
select * from test where id + 1 = 50;
select * from test where month(update_time) = 7;
使用联合索引时,注意最左匹配原则
4.JOIN 优化
优化子查询
尽量使用 Join 语句来替代子查询,因为子查询时嵌套查询,而嵌套查询会新建一张临时表,而临时表的创建和销毁会占用一定的系统资源以及花费一定的时间,同时对于返回结果集比较大的子查询,其对查询性能的影响更大。
小表驱动大表
关联查询的时候,要拿小表去驱动大表,因为关联的时候,MySQL内部会遍历驱动表,再去连接被驱动表。
sql
select name from 小表 left join 大表;
适当增加冗余字段
增加冗余字段可以减少大量的连表查询,因为多张表的连表查询性能很低,所以可以适当的增加冗余字段,以减少多张表的关联查询,这是以空间换时间的优化策略
比如:员工表里有员工信息以及所属部门ID,但有大量查询需要员工信息和部门名字,这时就可以在员工表中增加部门名称字段。一个记录购物信息的表,除了单价和数量,还可以增加一个总价的字段,虽然总价依赖于单价和数量,但在查询价格时可以直接输出从而减少计算量
避免使用JOIN 关联太多的表
《阿里巴巴Java开发手册》规定不要 join 超过三张表,第一 join 太多降低查询的速度,第二 join 的buffer会占用更多的内存
5.排序优化
利用索引扫描做排序
MySQL 有两种方式生成有序结果:一是对结果集进行排序的操作,二十按照索引顺序扫描得出的结果。索引是排好序的数据结构,因此查询结果自然是有序的。
但是如果索引无法覆盖查询所需列,就会每扫描一条记录回表查询一次,这个操作是随机IO,通常会比顺序全表扫描还慢,有时会直接放弃使用索引转为全表扫描。
因此,在设计索引时,尽可能使用同一个索引既满足排序又用于查找行。
sql
-- 索引(a, b, c)
select b, c from test where a like 'abc%' order by b, c;