MySQL的查询性能优化——《深究MySQL》

待更新。。。。。。。

写在前面

mysql查询缓慢的原因有哪些?
1.查询语句是否请求了不必要的多余数据
2.
总结以上原因之后,优化数据库性能,就需从以下几个方面着手:
1.

- 在

说明:下面的知识为零散的记录,后期需要整理

mysql优化方面

1.in 和 exsits的区别和效率

参考:https://www.cnblogs.com/emilyyoucan/p/7833769.html
总结如下:
select a.* from A a where exists(select 1 from B b where a.id=b.id)
exists()适合B表比A表数据大的情况

select * from A where id in(select id from B)
in()适合B表比A表数据小的情况

2.not in 和not exists

如果查询语句使用了not in ,那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。

2. 非同步方法会不会受同步方法的影响?

SQL语句中or的使用方法注意事项

要建索引的MySQL字段尽量设置为NOT NULL

含有空值的列很难进行查询优化,而且表索引是不会存储NULL值的,所以如果索引的字段可以为NULL,索引的效率会下降很多。因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值。

优化特定类型的查询

1. 优化count查询

1.尽量使用count(*)。当然也可以尝试索引覆盖,此时最好是主键。

count(columnName) 虽然也可以求出行数,但是它有一个致命的问题,那就是不统计为NULL的列。所以如果用列名代替*求行数的方式可能会出现统计错误。

2.优化关联查询

1.确保在ON或USING的关联顺序中第二个表的关联列有索引

例如,当A表和B表的关联顺序是A、B,即SQL语句为select * from A left join B on A.cid = B.cid 时,需要保证在B表的cid列表创建索引,而A表的cid列有无索引对性能没有影响。

扫描二维码关注公众号,回复: 1628690 查看本文章

2. 尽量让group by 和order by 的表达式中只涉及到一个表中的列

分组的字段在一个表,排序的字段又在另外一个表中,这样就很难利用到索引。

3.优化子查询

1. 尽量使用关联查询代替子查询

子查询会创建临时表,而临时表又没有任何索引

2. 子查询优化的基本思路,就是消除嵌套层次,把子查询与父查询放在同一个层次去执行。实现步骤:

把子查询的FROM子句中的表对象,与上层的父FROM子句中的表对象做JOIN

再把子查询的WHERE子句中的条件表达式,与上层的父WHERE子句中的条件表达式用“AND”操作符连接

4.优化group by

1.不关心顺序时,尽量用order by NULL 避免group by的默认排序(从而避免了文件排序)

group by子句如果没有指定排序列,则会自动根据分组列进行默认升序排序,从而导致了文件排序。

有个一般人不会用的方式,那就是直接在 group by 字段A 后面加上ASC或DESC关键字,使得分组结果按分组列(字段A)进行升序或降序排序。

4.1优化group by with rollup

可通过explain查看执行计划,决定是否加with rollup

with rollup表示需要做超级聚合,即可统计分组结果中每组数据分组前的某些值,如我们需要统计各部门员工工资的平均值
select depId, avg(money) from user group by depId with rollup;

需要注意的是,用了with rollup后不能再使用order by关键字,但可以直接在分组字段后加asc或desc

5.优化limit

1.尽量利用索引覆盖做“延迟关联”(延迟关联也可以优化关联查询的limit子句)

在做分页查询时,如果遇到像limit 2000,20这样偏移量大的查询时,mysql会实际查询2020条数据,然后抛弃前面2000条数据,只返回最后20条。这样做的代价非常高。

limit 2000,20 这样的分而之所以会慢,是因为offset。offset会导致mysql扫描大量不需要的行然后抛弃掉。所以平时我们看到 limit 1 这种写法是与offset无关的,也就是说这种写法不会很慢。顺便提一下,limit 2000,20 是等价于 limit 20 offset 2000的

6.优化union查询

1.如果不需要对查询结果去重的话,尽量使用union all 而不是union。

union后没跟all 时,会自动给临时表加上distinct做唯一性检查,从而降低性能。

2.巧用 limit

(SELECT * from sys_user where id < 2000 order by office_id ) UNION (SELECT * from sys_user WHERE id >2000 order by office_id ) order by office_id desc limit 20;

两个括号里的子查询都会将各自的数据全部放在一个临时表中,然后从临时表中取出20条数据,这时可以将每个子查询都用上limit 20

(SELECT * from sys_user where id < 2000 order by office_id limit 20) UNION (SELECT * from sys_user WHERE id >2000 order by office_id limit 20 ) order by office_id desc limit 20;

这样一来,临时表中就只有40条数据了。需要注意的是,从临时表取数据的顺序是不确定的,所以要想按自定义的顺序取,必须在全局加上order by ,如上面SQL上的红色字。

7.优化 in 子查询

1.将in子查询用innor join改写成关联查询

IN的子查询在实际执行的时候会被mysql自动改写成较缓慢的查询(mysql有时候也会自以为是的,哈哈),如下查询

select * from film where film_id IN(select film_id from film_actor where actor_id = 1)

会被改成写:

select * from film where film_id exists (select film_id from film_actor where actor_id = 1 and film_actor.film_id = film.film_id )

从改写后的语句可以看到,子查询里面涉及到外部表 film 的film_id字段了,所以mysql会先对外部表film进行全表扫描,通过explain执行计划也可以看到。

优化后的写法为:

select * from film inner join film_actor ON film.film_id = film_actor.film_id WHERE film_actor.actor_id = 1;

8.优化 MAX()、MIN()

1. 通过order by 和limit来优化

如下,我们需要获取最大值和最小值(前提是id值有索引)

优化前:
最小值:select min(id) from actor;
最大值:select max(id) from actor;

优化后:
最小值:select id from actor order by id asc limit 1;
最大值:select id from actor order by id desc limit 1;

从explain可以看出,虽然优化前和优化后都用到了索引,但是min和max函数进行了大量的扫描,而优化后只扫描了一行。

猜你喜欢

转载自blog.csdn.net/zcl_love_wx/article/details/79981702