33 | MySQL调优之SQL语句:如何写出高性能SQL语句?
33 | MySQL调优之SQL语句:如何写出高性能SQL语句?
讲述:李良
时长13:57大小12.76M
慢 SQL 语句的几种常见诱因
1. 无索引、索引失效导致慢查询
2. 锁等待
3. 不恰当的 SQL 语句
优化 SQL 语句的步骤
1. 通过 EXPLAIN 分析 SQL 执行计划
2. 通过 Show Profile 分析 SQL 执行性能
常用的 SQL 优化
1. 优化分页查询
2. 优化 SELECT COUNT(*)
3. 优化 SELECT *
总结
思考题
赞 18
提建议
精选留言(45)
- 张学磊2019-08-06status和create_time单独建索引,在查询时只会遍历status索引对数据进行过滤,不会用到create_time列索引,将符合条件的数据返回到server层,在server对数据通过快排算法进行排序,Extra列会出现file sort;应该利用索引的有序性,在status和create_time列建立联合索引,这样根据status过滤后的数据就是按照create_time排好序的,避免在server层排序
作者回复: 非常准确!
共 13 条评论92 - 大俊stan2019-09-08count(*) 的速度是最快的innodb自己有优化共 5 条评论20
- Kian.Lee2019-08-08我在实际项目中使用“select order_no from order where status =1 order by id desc ” 代替此功能,id为bigint ,也少维护一个索引(create_time)😁共 4 条评论16
- QQ怪2019-08-06对staus和create_time建立联合索引
作者回复: 对的,为了避免文件排序的发生。因为查询时我们只能用到status索引,如果要对create_time进行排序,则需要使用文件排序filesort。 filesort是通过相应的排序算法将取得的数据在内存中进行排序,如果内存不够则会使用磁盘文件作为辅助。虽然在一些场景中,filesort并不是特别消耗性能,但是我们可以避免filesort就尽量避免。
共 2 条评论16 - Jian2019-08-12因为好久没有做SQL相关的开发了,所以开始没有特别明白【利用子查询优化分页查询】这里面的意思。我来说下自己的想法,请您检证。我看到您贴的截图中,优化后的sql语句,扫描的行数(rows列)分别是90409和10001,多余前一个较慢的查询,可见扫描行数,不是这个性能的主要原因。我推测这个是由于limit [m],n的实现方法导致的,即MySql会把m+n的数据都取出来,然后返回n个数据给用户。如果用第二种SQL语句,子查询只是获得一个id,虽然扫描了很多行,但都是在索引上进行的,切不需要回表获取内容。外查询是根据id获取数据20条内容,速度自然就会快了。我认为这里性能提高的原因还是居于索引的恰当使用。展开
作者回复: 可以这么理解
共 3 条评论15 - 尔冬橙2020-02-26老师,分页查询优化那一块,单从扫描函数看,采用子查询和不采用子查询扫描总行数是差不多的,而不采用子查询,第一个主查询就是返回10020条记录,采用子查询只返回20条记录?我理解是结果集有多少行就是返回多少记录。是因为子查询不返回记录么,它不也扫描了10000行么?
作者回复: 数据库需要查询 10020 条记录,最后返回 20 条记录,也就是说将会有 10000 条记录被查询出来没有被使用到。子查询是select id from `demo`.`order` order by order_no limit 10000, 1虽然会扫描10000条数据,但并不会查询出10000条数据,最终查询出来的是一个id值,而这个id值又是主键。
12 - 钱2019-09-14课后思考及问题 1:按照效率排序的话,count(字段)<count(主键 id)<count(1)≈count(*),所以我建议你,尽量使用 count(*)。——MySQL,丁奇老师的结论 如果对一张大表经常做 SELECT COUNT(*) 操作,这肯定是不明智的。——刘老师的结论 同样的问题,不同的老师给出了截然相反的结论,我希望有一天两位老师可以讨论一下为什么? 刘老师具体没讲COUNT(*)的原理,我倾向于认为丁奇老师的结论是正确的。展开
作者回复: 我与丁奇结论没有相反,丁奇是建议你在这些count类型中选择使用count(*),而我是建议不要在大表中做count(*)。丁奇老师讲解了COUNT(*)的原理,我相信他也会建议你不要在一个大表中频繁COUNT(*)。
共 4 条评论7 - 起点2020-08-23sql慢通常是两种情况 1.sql没有使用到索引。 2.因为sql语句使用了粗粒度的锁,导致大量的锁等待,所以也会让sql变慢。 怎样对慢sql进行分析 1.使用explain分析sql的索引使用情况。 2.使用show profile 指令分析sql的资源损耗情况 常用的sql优化 1.对于limit 的优化。 案例中优化的逻辑(子查询使用了覆盖索引,jion的sql需要把匹配的数据都拿到,再从数据中拿到10000到10010条的数据。子查询的案例中是先获取到符合条件的数据id条件,然后从主查询中查询过滤直接得到符合条件的20条数据) 2.对于count的优化。 可用explain获取近似值。 可冗余一个统计字段 思考题的sql问题在于 status和create_time分别是两个索引,而sql查询的时候只能用到一个索引,所以思考题的sql肯定会用create_time的索引(因为status列的离散性不高),而通过crete_time索引是无法获取到status的值的,所以又必须通过聚集索引获得status的值,然后再对status进行过滤,获得最终结果。 思考题可通过组合索引优化,把status和create_time建立为一个组合索引,这个时候就只需要通过这个组合索引就能过滤掉所有条件,而且索引中已经包含了需要查询显示的列,这里又会使用覆盖索引,索引无须查询聚集索引进行回表操作。 完全理解老师的优化案例还需要一定基础知识才行。参考下面这篇文章可帮大家补充一些sql优化的基础原则。 sql优化原则 https://mp.weixin.qq.com/s/u3FOsTroEo6eqKIrfoBBSQ展开5
- 迎风劲草2019-08-06创建 status create_time order_no 联合索引,避免回表
作者回复: 建立联合索引没错,还有就是避免文件排序的问题。
共 3 条评论5 - mumu2019-11-04select * from `demo`.`order` where id> (select id from `demo`.`order` order by order_no limit 10000, 1) limit 20;,老师您好,我不懂这样写为什么是正确的,为什么id>子查询结果的20条就是按order_no排序所需的20条?
作者回复: 这里我们就默认orderno是递增的,而且是随着id自增长递增
共 2 条评论4 - 张三丰2019-10-04就算使用了联合索引,也避免不了排序吧,因为题目要求的是降序,联合索引是保证第一个索引有序的前提下再保证第二个索引有序,那么这个有序是升序,如果没记错的话。
作者回复: 只要有序的,就不会再重复排序了,只是一个升降序的问题了
共 2 条评论4 - 尔冬橙2019-09-26为什么分页查询优化那块,主查询扫描这么多行?
作者回复: 使用limit 10000,1,所以需要顺序扫描到10001行,所以我们尽量使用主键递增的方式,直接将主查询换成select * from `demo`.`order` where id> 10000 limit 20,避免扫描带来的性能问题。
3 - Geek_002ff72019-08-09真实情况一般不会在status上单独建索引,因为status大部分都是重复值,数据库一般走全表扫描了,感觉漏讲了索引失效的情况
作者回复: 下一讲则会讲到
3 - 石妖2020-05-27思考题中,由于status区分度较低,无论是否有索引,以status为条件进行查询大概率是进行全表扫描,而且联合索引有一定的局限性(最左匹配),所以我觉得没有必要在status上添加索引。而create_time因为是排序字段,可以利用索引有序性优化查询速度。所以,我建议是只在create_time添加索引即可2
- IT橘子2020-03-08老师,常用的SQL优化-1.优化分页查询中利用子查询优化分页查询成立的条件是不是order_no建立了唯一索引,即order_no与id(主键)一一对应,并且在order_no的索引上,id是严格单调递增的?
作者回复: 是的,id是自增长的
2 - 风轻扬2019-09-20select * from `demo`.`order` where id> (select id from `demo`.`order` order by order_no limit 10000, 1) limit 20; 老师,这个优化后的查询,您是先查询出来的第10001条数据的id,然后 id 大于此值。获取20条数据。这样获取的值不对啊,我试了。这样获取的是10002到10021的数据了,不是10001到10020的数据。子查询获取的值减一就对了
作者回复: >=即可,强调优化思路,具体的细节老师这边没有把控好,多多包涵
共 2 条评论2 - 张德2019-08-28没看别人评论 我来说一下 直接查状态为1的订单 索引的区分度太低
作者回复: 答案已给出
1 - 拒绝2019-08-09感觉要建立联合索引,但不知具体原因
作者回复: 为了避免文件排序的发生。因为查询时我们只能用到status索引,如果要对create_time进行排序,则需要使用文件排序filesort。
1 - LW2019-08-06order_no创建主键,status+create_time创建联合索引
作者回复: 对的
共 2 条评论1 - nimil2019-08-06select * from table limit 1 这种sql语句会走主键索引么,我看explain里边没有任何索引记录
作者回复: 不会,没有使用到索引。
共 2 条评论1