MySQL性能优化篇之SQL语句优化

目录

  • 向数据库请求不需要的数据
    • 查询不需要的记录
    • 总是返回全部的列
  • MySQL扫描了额外的行
    • 扫描的行数和返回的行数
    • 行访问类型
    • 也要注意extra列的信息
    • 优化扫描行数过多的建议
  • 重构查询方式
    • 一个复杂的查询还是多个简单的查询
    • 切分查询
  • 常用的查询技巧
    • 使用内连接而不是外连接
    • 优化关联查询
    • 优化子查询
    • 优化COUNT()、MIN()和MAX()
    • 提前终止查询
    • 多个OR条件转成IN()查询
    • 排序优化
    • 优化union查询
    • 优化LIMIT分页

一般谈论性能优化是指查询性能的优化,导致查询慢的原因主要是访问的数据太多了。大部分性能低下的查询都可以通过减少访问的数据量的方法进行优化,可以从以下两个方面考虑减少访问的数据量:

  • 应用程序向MySQL请求了不必要的数据,如太多的行或列
  • MySQL服务器层在分析大量超过需要的数据行(数据能在存储引擎层的索引中完成筛选是最优的)

向数据库请求不需要的数据

查询不需要的记录

应用程序只需要数据库中前10条数据,却向数据库请求所有的数据,然后丢弃前10条之后的数据。这样会造成极大的资源浪费,要养成使用limit关键字限制返回的记录数。

总是返回全部的列

查询语句总是用select *的方式,MySQL对这种查询无法应用覆盖索引的优化,因为没有索引会将表中所有的字段都覆盖。但是这种方式也有优点,可以简化开发。

MySQL扫描了额外的行

explain关键字可以分析SQL语句是否高效,从输出的结果可以大致看出MySQL访问行的类型、数量以及一些额外的操作。

扫描的行数和返回的行数

理想情况下扫描的行数和返回的行数应该是一致的,但实际情况中这种“美事”并不多。例如在做一个关联查询时,服务器必须要扫描多行才能生成结果集中的一行。扫描的行数对返回的行数的比率通常很小,一般在1:1和10:1之间,不过有时候这个值也可能非常非常大

行访问类型

在评估查询开销的时候,需要考虑一下从表中找到某一行数据的成本。MySQL有好几种访问方式可以查找并返回一行结果。有些访问方式可能需要扫描很多行才能返回一行结果,也有些访问方式可能无须扫描就能返回结果。
在EXPLAIN语句中的type列反应了访问类型。访问类型有很多种,从全表扫描到索引扫描、范围扫描、唯一索引查询、常数引用等。这里列的这些,速度是从慢到快,扫描的行数也是从小到大。你不需要记住这些访问类型,但需要明白扫描表、扫描索引、范围访问和单值访问的概念。

也要注意extra列的信息

如extra列中的“Using Where”表示MySQL将通过WHERE条件来筛选存储引擎返回的记录。
一般MySQL能够使用如下三种方式应用WHERE条件,从好到坏依次为:

  • 在索引中使用WHERE条件来过滤不匹配的记录。这是在存储引擎层完成的。
  • 使用索引覆盖扫描(在Extra列中出现了Using index)来返回记录,直接从索引中过滤不需要的记录并返回命中的结果。这是在MySQL服务器层完成的,但无须再回表查询记录。
  • 从数据表中返回数据,然后过滤不满足条件的记录(在Extra列中出现Using Where)。这在MySQL服务器层完成,MySQL需要先从数据表读出记录然后过滤。

优化扫描行数过多的建议

如果发现查询需要扫描大量的数据但只返回少数的行,那么通常可以尝试下面的技巧去优化它:

  • 使用索引覆盖扫描,把所有需要用的列都放到索引中,这样存储引擎无须回表获取对应行就可以返回结果了(在前面的章节中我们已经讨论过了)。
  • 改变库表结构。例如使用单独的汇总表(这是我们在第4章中讨论的办法)。
  • 重写这个复杂的查询,让MySQL优化器能够以更优化的方式执行这个查询

重构查询方式

在优化有问题的查询时,目标应该是找到一个更优的方法获得实际需要的结果——而不一定总是需要从MySQL获取一模一样的结果集。

一个复杂的查询还是多个简单的查询

在互联网发展初期,网络通信的时间成本很高,往往让数据库做更多的事,减少查询次数。现在网络带宽已经远远好于之前了,单次网络通信可以在毫秒级完成,对于复杂的查询可以拆分成多个简单的查询,在应用程序中完成业务逻辑的处理。
当然不一定所有的复杂查询都要拆分,要考虑成本。拆分成多个简单查询会增加开发的成本,这需要在实际中作出衡量。
以一个关联查询为例

mysql> SELECT * FROM tag->    JOIN tag_post ON tag_post.tag_id=tag.id->    JOIN post ON tag_post.post_id=post.id-> WHERE tag.tag='mysql';

可以分解成下面这些查询来代替:

mysql> SELECT * FROM  tag WHERE tag='mysql';
mysql> SELECT * FROM  tag_post WHERE tag_id=1234;
mysql> SELECT * FROM  post WHERE  post.id in (123,456,567,9098,8904);

这样做会有以下的优点:

  • 让缓存更高效。应用程序可以缓存tag_id和post.id对应的数据,当下一次查询时就可以访问缓存无需查数据库了。另外对MySQL查询缓存也友好,因为表结构是很少会改变的。
  • 将查询分解后,执行单个查询可以减少锁的竞争
  • 查询本身效率也可能会有所提升。使用IN()代替关联查询,可以让MySQL按照ID顺序进行查询,这可能比随机的关联要更高效。
  • 可以减少冗余记录的查询。这样做相当于在应用中实现了哈希关联,而不是使用MySQL的嵌套循环关联。

切分查询

有时候对于一个大查询我们需要“分而治之”,将大查询切分成小查询,每个查询功能完全一样,只完成一小部分,每次只返回一小部分查询结果。
删除旧的数据就是一个很好的例子。定期地清除大量数据时,如果用一个大的语句一次性完成的话,则可能需要一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。将一个大的DELETE语句切分成多个较小的查询可以尽可能小地影响MySQL性能,同时还可以减少MySQL复制的延迟。

一次删除一万行数据一般来说是一个比较高效而且对服务器影响也最小的做法(如果是事务型引擎,很多时候小事务能够更高效)。同时,需要注意的是,如果每次删除数据后,都暂停一会儿再做下一次删除,这样也可以将服务器上原本一次性的压力分散到一个很长的时间段中,就可以大大降低对服务器的影响,还可以大大减少删除时锁的持有时间。

常用的查询技巧

使用内连接而不是外连接

在联表查询时外连接会返回两张表的所有记录,未匹配的记录在其他字段会以null显示。在实际应用中往往是以其中一张表的记录为准做匹配,如果以外连接的方式实现这样的查询结果,会使MySQL执行额外的关联匹配操作,造成资源浪费。

优化关联查询

  • 确保ON或者USING子句中的列上有索引。在创建索引的时候就要考虑到关联的顺序。当表A和表B用列c关联的时候,如果优化器的关联顺序是B、A,那么就不需要在B表的对应列上建上索引。
  • 确保任何的GROUP BY和ORDER BY中的表达式只涉及到一个表中的列,这样MySQL才有可能使用索引来优化这个过程

优化子查询

关于子查询优化我们给出的最重要的优化建议就是尽可能使用关联查询代替,至少当前的MySQL版本需要这样。“尽可能使用关联”并不是绝对的,如果使用的是MySQL 5.6或更新的版本或者MariaDB,那么就可以直接忽略关于子查询的这些建议了。

优化COUNT()、MIN()和MAX()

要找到某一列的最小值,只需要查询对应B-Tree索引最左端的记录,MySQL可以直接获取索引的第一行记录。同理最大值和统计总数也是类似的。所以可以通过为对应的列建立索引实现优化。

提前终止查询

在发现已经满足查询需求的时候,MySQL总是能够立刻终止查询。一个典型的例子就是当使用了LIMIT子句的时候。
除此之外,MySQL还有几类情况也会提前终止查询
发现了一个不成立的条件

mysql> EXPLAIN SELECT film.film_id FROM sakila.film WHERE film_id = -1;

explain结果

发现某些特殊的条件

mysql> SELECT film.film_id->    FROM sakila.film         ->    LEFT OUTER JOIN sakila.film_actor USING(film_id)         -> WHERE film_actor.film_id IS NULL;

这个查询将会过滤掉所有有演员的电影。每一部电影可能会有很多的演员,但是上面的查询一旦找到任何一个,就会停止并立刻判断下一部电影,因为只要有一名演员,那么WHERE条件则会过滤掉这类电影。类似这种“不同值/不存在”的优化一般可用于DISTINCT、NOT EXIST()或者LEFT JOIN类型的查询。

多个OR条件转成IN()查询

MySQL将IN()列表中的数据先进行排序,然后通过二分查找的方式来确定列表中的值是否满足条件,这是一个O(log n)复杂度的操作,等价地转换成OR查询的复杂度为O(n),对于IN()列表中有大量取值的时候,MySQL的处理速度将会更快

排序优化

无论如何排序都是一个成本很高的操作,所以从性能角度考虑,应尽可能避免排序或者尽可能避免对大量数据进行排序。在查询的结果不需要排序时可以使用order by null让MySQL不对结果集排序。
当不能使用索引生成排序结果的时候,MySQL需要自己进行排序,如果数据量小则在内存中进行,如果数据量大则需要使用磁盘,不过MySQL将这个过程统一称为文件排序(filesort),即使完全是内存排序不需要任何磁盘文件时也是如此

优化union查询

MySQL总是通过创建并填充临时表的方式来执行UNION查询。
除非确实需要服务器消除重复的行,否则就一定要使用UNION ALL,这一点很重要。如果没有ALL关键字,MySQL会给临时表加上DISTINCT选项,这会导致对整个临时表的数据做唯一性检查。这样做的代价非常高。
事实上,MySQL总是将结果放入临时表,然后再读出,再返回给客户端。

优化LIMIT分页

我们通常会使用LIMIT加上偏移量的办法实现,同时加上合适的ORDER BY子句。如果有对应的索引,通常效率会不错,否则,MySQL需要做大量的文件排序操作。
在偏移量非常大的时候,例如可能是LIMIT 1000,20这样的查询,这时MySQL需要查询10 020条记录然后只返回最后20条,前面10 000条记录都将被抛弃,这样的代价非常高。
优化此类分页查询的一个最简单的办法就是尽可能地使用索引覆盖扫描,而不是查询所有的列。然后根据需要做一次关联操作再返回所需的列。对于偏移量很大的时候,这样做的效率会提升非常大。考虑下面的查询:

mysql> SELECT film_id, description FROM sakila.film ORDER BY title LIMIT 50, 5;

如果这个表非常大,那么这个查询最好改写成下面的样子:

mysql> SELECT film.film_id, film.description-> FROM sakila.film         ->    INNER JOIN (         ->       SELECT film_id FROM sakila.film->       ORDER BY title LIMIT 50, 5         ->    ) AS lim USING(film_id);     

这里的“延迟关联”将大大提升查询效率,它让MySQL扫描尽可能少的页面,获取需要访问的记录后再根据关联列回原表查询需要的所有列。
LIMIT和OFFSET的问题,其实是OFFSET的问题,它会导致MySQL扫描大量不需要的行然后再抛弃掉。如果可以使用书签记录上次取数据的位置,那么下次就可以直接从该书签记录的位置开始扫描,这样就可以避免使用OFFSET。

mysql> SELECT * FROM sakila.rental-> WHERE rental_id < 16030         -> ORDER BY rental_id DESC LIMIT 20;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/44738.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

防火墙一些有关知识

防火墙概述 防火墙核心任务 控制和防护 如何发挥功能 防火墙通过安全策略识别列两并做出相应动作。和ACL一样&#xff0c;所以防火墙本质就是ACL 防火墙分类 按物理特性划分 软件防火墙&#xff1a;电脑带的防火墙 硬件防火墙&#xff1a;设备&#xff0c;这个设备做的就…

深度学习之网络构建

目标 选择合适的神经网络 卷积神经网络&#xff08;CNN&#xff09;&#xff1a;我们处理图片、视频一般选择CNN 循环神经网络&#xff08;RNN&#xff09;&#xff1a;我们处理时序数据一般选择RNN 超参数的设置 为什么训练的模型的错误率居高不下 如何调测出最优的超参数 …

练习 5.10:检查⽤户名 按照下⾯的说明编写⼀个程序,模拟⽹站如 何确保每个⽤户的⽤户名都独⼀⽆⼆。

练习 5.10&#xff1a;检查⽤户名 按照下⾯的说明编写⼀个程序&#xff0c;模拟⽹站如 何确保每个⽤户的⽤户名都独⼀⽆⼆。 要求 创建⼀个⾄少包含 5 个⽤户名的列表&#xff0c;并将其命名为 current_users。 再创建⼀个包含 5 个⽤户名的列表&#xff0c;将其命名为 new_…

继电器实现直流电机正反转

有关继电器的使用方法&#xff0c;首先介绍了继电器的内部工作原理&#xff0c;然后介绍了两只继电器组成的正反转电路&#xff0c;以及用继电器实现直流电机正反转的具体方法&#xff0c;供大家学习参考。 继电器实现直流电机正反转 1、继电器内部原理 线圈断电时公共与常闭…

华贝甄选商业生态,成功背后的秘诀在这里

华贝甄选通过其独特的商业生态模式&#xff0c;不仅展示了对电子商城业务的深刻理解&#xff0c;更体现了对市场趋势和政策导向的敏锐把握。其S2B2C2B的用户交互模式&#xff0c;无疑是对传统商业模式的一次创新&#xff0c;它融合了供应链、企业和消费者之间的紧密联系&#x…

索引原理;为什么采用B+树?

在MySQL中&#xff0c;索引的原理是通过数据结构来快速查找数据。常见的索引数据结构有B树、B树和哈希表等。MySQL大多数存储引擎&#xff08;如InnoDB&#xff09;使用B树作为索引的数据结构。 为什么采用B树&#xff1f; 1. B树结构 B树是一种平衡树&#xff0c;它是在B树…

javaweb学习day4--《maven篇》maven的项目创建及其依赖管理详解(基于最新版本的idea)

一、前言 javaweb学习的第四天&#xff0c;不知道今天你们是否坚持下去了。今天学习到的是maven&#xff0c;温馨提示一下&#xff0c;idea中自带maven不用自行去下载了。前期的配置工作太过复杂了&#xff0c;小编感觉自己能力有限并不能将其讲的太清楚&#xff0c;还请大家在…

c小红的图上划分(牛客127)

题意&#xff1a; 有一个无向图&#xff0c;有 n 个点 m 条边&#xff0c;q 个询问&#xff0c;每次给出 L,R&#xff0c;求将图划分为至少 L 个连通块&#xff0c;最多 R个连通块的最大划分价值&#xff0c;若不可划分输出 "NO ANSWER"。 图的划分定义为将图划分为一…

Knife4j的原理及应用详解(七)

本系列文章简介&#xff1a; 在当今快速发展的软件开发领域&#xff0c;API&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&#xff09;作为不同软件应用之间通信的桥梁&#xff0c;其重要性日益凸显。随着微服务架构的兴起&#xff0c;API的数量…

每天一个数据分析题(四百二十四)- 单因素方差分析

关于单因素方差分析&#xff0c;下列说法不正确的是 A. 组间平方和只包含系统误差 B. 组内平方和只包含随机误差 C. 如果组间均方远大于组内均方&#xff0c;那么就说明不同水平之间均值存在着显著差异 D. 如果组间均方远大于组内均方&#xff0c;那么就说明分类变量对于数…

使用Java实现高性能的图像处理服务

使用Java实现高性能的图像处理服务 大家好&#xff0c;我是微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 图像处理在现代应用中是一项常见而重要的任务&#xff0c;涵盖了从简单的格式转换到复杂的计算机视觉算法的各种需…

如何将本地仓库中的文件推送到远程git服务器

第一步&#xff1a;进入所在项目&#xff0c;右击打开"Git Bash Here" 第二步&#xff1a;git config --global user.email "18351810763163.com" // 输入你的名称 第三步&#xff1a;git config --global user.name "chenliang-sam&quo…

Java使用 BufferedImage生成验证码图片简单使用Java生成验证码 使用Java生成验证码图片 java生成验证码图片

Java使用 BufferedImage生成验证码图片简单使用Java生成验证码 使用Java生成验证码图片 java生成验证码图片 1、工具类2、调用 1、工具类 import org.apache.xmlbeans.impl.util.Base64; import org.springframework.web.context.WebApplicationContext; import org.springfra…

如何将一个2D数组切分成多个块

要将一个2D数组切分成多个块&#xff0c;可以考虑使用以下几种方法&#xff0c;具体取决于如何定义块的划分规则和需求。如果你希望将2D数组均匀地切分成固定大小的小块&#xff0c;可以使用简单的循环和切片操作。 1、问题背景 Python 中, 如果有一个 raw 数据文件&#xff0…

SSM社区物业管理系统-计算机毕业设计源码91276

摘要 随着城市化进程的加快&#xff0c;居民社区的规模和数量不断增长&#xff0c;传统的人工管理方式已经无法满足管理需求。借助信息技术和互联网应用&#xff0c;社区物业管理系统可以实现物业管理信息的集中化、自动化和便捷化&#xff0c;提供全方位的管理和服务支持。社区…

使用kubeadm重置k8s集群

1.Master 节点和Work节点都需要执行&#xff1a;kubeadm reset kubeadm reset -hAvailable Commands:alpha Kubeadm experimental sub-commandscompletion Output shell completion code for the specified shell (bash or zsh)config Manage configuration for a…

智慧水利的变革之路:如何通过大数据、物联网和人工智能构建高效、智能、可持续的水利管理新模式

目录 一、引言&#xff1a;智慧水利的时代背景与意义 二、大数据&#xff1a;水利管理的数据基石 &#xff08;一&#xff09;数据收集与整合 &#xff08;二&#xff09;数据分析与挖掘 三、物联网&#xff1a;水利管理的感知神经 &#xff08;一&#xff09;智能感知与监…

ONLYOFFICE 8.1版本版本桌面编辑器测评

ONLYOFFICE官网链接&#xff1a;ONLYOFFICE - 企业在线办公应用软件 | ONLYOFFICE ONLYOFFICE在线办公套件&#xff1a;在线办公套件 | ONLYOFFICE ONLYOFFICE在线PDF编辑器、阅读器和转换器&#xff1a;在线PDF查看器和转换器 | ONLYOFFICE ONLYOFFICE 8.1版本桌面编辑器是…

Java面试之缓存中间件常见面试题

1. 请简述什么是缓存以及为什么需要使用缓存&#xff1f; 缓存是存储常用或临时数据的组件&#xff0c;用于提高数据访问速度。在Web应用中&#xff0c;缓存能够减少数据库访问次数&#xff0c;提高系统响应速度和吞吐量&#xff0c;从而提升用户体验和系统稳定性。 2. 列举一…

开源可视化Flutter图表库:Graphic

Graphic&#xff1a;用Graphic绘制数据的无限可能- 精选真开源&#xff0c;释放新价值。 概览 Graphic&#xff0c;这个基于Flutter的图表库&#xff0c;以其源自《The Grammar of Graphics》的灵感&#xff0c;为数据可视化提供了一种全新的方法。它不仅仅是一个工具&#xf…