MySQL之查询性能优化(二)

查询性能优化

慢查询基础:优化数据访问

查询性能低下最基本的原因是访问的数据太多。某些查询可能不可避免地需要筛选大量数据,但这并不场景。大部分性能低下的查询都可以通过减少访问的数据量的方式进行优化。对于低效的查询,我们发现通过下面两个步骤来分析总是很有效:

  • 1.确认应用程序是否在检索大量超过需要的数据。这通常意味着访问了太多的行,但有时候也可能是访问了太多的列
  • 2.确认MySQL服务器是否在分析大量超过需要的数据行

是否向数据库请求了不需要的数据。

有些查询会请求超过实际需要的数据,然后这些多余的数据会被应用程序丢弃。这回给MySQL服务器带来额外的负担,并增加网络开销(如果应用服务器和数据库不在同一台主机上,网络开销就显得很明显了。即使在同一台服务器上仍然会有数据传输的开销)。另外也会消耗应用服务器的CPU和内存资源。下面是一些典型案例:

  • 1.查询不需要的记录:一个常见的错误是常常误以为MySQL会只返回需要的数据,实际上MySQL却是先返回全部结果集再进行计算。我们经常会看到一些了解其他数据库系统的人会设计出这类应用程序。这些开发者习惯适用这样的使用,先使用SELECT语句查询大量的结果,然后获取前面的N行后关闭结果集(例如在新闻网站中取出100条记录,但是只是在页面上显示前面10条)。它们认为MySQL会执行查询,并只返回它们需要的10条数据,然后停止查询。实际情况是MySQL会查询出全部的结果集,客户端的应用程序会接收全部的结果集数据,然后抛弃其中大部分数据。最简单有效的解决方法就是在这样的查询后面加上LIMIT
  • 2.多表关联时返回全部列:如果你想查询所有在电影Academy Dinosaur中出现的演员,千万不要按下面的写法编写查询:
mysql> SELECT * FROM actor-> INNER JOIN film_actor USING(actor_id)-> INNER JOIN film USING(film_id)-> WHERE film.title='Academy Dinosaur';

这将返回这三个表的全部数据列。正确的方式应该时像下面这样只取需要的列:

mysql>SELECT actor.* FROM actor .....
  • 3.总是取出全部列:每次看到SELECT * 的时候都需要用怀疑的眼光审视,是不是真的需要返回全部的列?很可能不是必需的。取出全部列,会让优化器无法完成索引覆盖扫描这类优化,还会为服务器带来额外的IO、内存和CPU的消耗。因此,一些DBA是严格禁止SELECT * 的写法的,这样做有时候还能避免某些列被修改带来的问题。当然,查询返回超过需要的数据也不总是坏事。在许多案例中,人们会说这种有点浪费数据库资源的方式可以简化开发,因为能提高相同代码片段的复用性,如果清除这样做的性能影响,那么这种做法也是值得考虑的。如果应用程序使用了某种缓存机制,或者有其他考虑,获取超过需要的数据也可能有其他好处,但不要忘记这样做的代价是什么。获取并缓存所有的列的查询相比多个独立的只获取部分列的查询可能就更有好处。
  • 4.重复查询相同的数据:如果你不太小心,很容易出现这样的错误——不断地重复执行相同的查询,然后每次都返回完全相同的数据。例如,在用户评论的地方需要查询用户头像的URL,那么用户多次评论的时候,可能就会反复查询这个数据。比较好的方案是,当初次查询的时候将这个数据缓存起来,需要的时候从缓存中取出,这样性能显然会更好。

MySQL是否扫描额外的记录

在确定查询只返回需要的数据以后,接下来应该看看查询为了返回结果是否扫描了许多的数据。对于MySQL,最简单的衡量查询开销的三个指标如下:
1.响应时间
2.扫描的行数
3.返回的行数
没有哪个指标能够完美地衡量查询的开销,但它们大致反映了MySQL在内部执行查询时需要访问多少数据,并可以大概推算出查询运行的时间。这三个指标都会记录到MySQL的慢日志中,所以检查慢日志记录是找出扫描行数过多的查询的好办法

响应时间

要记住, 响应时间只是一个表面的值。这样说可能看起来和前面关于响应时间的说法有矛盾?其实并不矛盾,响应时间仍然是最重要的指标,这有一点复杂,后面细细道来。响应时间是两个部分之和:服务时间和排队时间。服务时间是指数据处理这个查询真正花了多长时间。排队时间是指服务器因为等待某些资源而没有真正执行查询的时间——可能是等待IO操作完成,也可能是等待行锁,等等。遗憾的是,我们无法把响应时间细分到上面这些部分,除非有什么办法能够逐个测量上面这些消耗,不过很难做到,一般最常见和重要的等待是IO和锁等待,但实际情况更加复杂。所以在不同类型的应用压力下,响应时间并没有什么一致的规律或者共识。诸如存储引擎的锁(表锁、行锁)、高并发资源竞争、硬件响应等诸多因素都会影响到响应时间。所以,响应时间既可能是一个问题的结果也可能是一个问题的原因,不同案例情况不同,除非我们能深入测量出每个环节。
当你看到一个查询的响应时间的时候,首先需要问问自己,这个响应时间是否是一个合理的值。实际上可以使用"快速上限估计"法来估算查询的响应时间,这是由Lahdenmaki和Mike Leach编写的Relational Database Index Design and the Optimizers一书中提到的技术。概括地说,了解这个查询需要哪些索引以及它的执行计划是什么,然后计算大概需要多少个顺序和随机IO,再用其乘以在具体硬件条件下一次IO的消耗。最后把这些消耗都加起来,就可以获得一个大概参考值来判断当前响应时间是不是一个合理得值

扫描的行数和返回的行数

分析查询时,查看该查询扫描的行数时非常有帮助的。这在一定程度上能够说明该查询找到需要的数据的效率高不高。对于找出哪些"糟糕"的查询,这个指标可能还不够完美,因为并不是所有的行的访问代价都是相同的。较短的行的访问速度更快,内存中的行也比磁盘中的行访问速度要快得多。理想情况下扫描得行数和返回的行数应该是相同的。但实际情况中这种"美事"并不多。例如在做一个管来奶查询时,服务器必须要扫描多行才能生成结果集中的一行。扫描的行数对返回的行数的比率通常很小,一般在1:1和10:1之间,不过有时候这个值也可能非常非常大

扫描的行数和访问类型

在评估查询开销的时候,需要考虑一下从表中找到某一行数据的成本。MySQL有好几种访问方式可以查找并返回一行结果。有些访问方式可能需要扫描很多行才能返回一行结果,也有些访问方式可能无须扫描就能返回结果。在EXPLAIN语句中的type列反应了访问类型。访问类型有很多种,从全表扫描到索引扫描、范围扫描、唯一索引查询、常数引用等。这里列的这些,速度时从慢到快,扫描的行数也是从多到少。你不需要记住这些访问类型,但需要明白扫描表、扫描索引、范围访问和单值访问的概念.如果查询没有办法找到合适的访问类型,那么解决的最好办法通常就是增加一个合适的索引。现在应该明白为什么索引对于查询优化如此重要了。索引让MySQL以最高效、扫描行数最少的方式找到需要的记录。例如,我们看看示例库Sakila中的一个查询案例:

mysql> SELECT * FROM film_actor WHERE film_id =1;
+----------+---------+---------------------+
| actor_id | film_id | last_update         |
+----------+---------+---------------------+
|        1 |       1 | 2006-02-15 05:05:03 |
|       10 |       1 | 2006-02-15 05:05:03 |
|       20 |       1 | 2006-02-15 05:05:03 |
|       30 |       1 | 2006-02-15 05:05:03 |
|       40 |       1 | 2006-02-15 05:05:03 |
|       53 |       1 | 2006-02-15 05:05:03 |
|      108 |       1 | 2006-02-15 05:05:03 |
|      162 |       1 | 2006-02-15 05:05:03 |
|      188 |       1 | 2006-02-15 05:05:03 |
|      198 |       1 | 2006-02-15 05:05:03 |
+----------+---------+---------------------+

这个查询将返回10行数据,从EXPLAIN的结果可以看到,MySQL在索引idx_fk_film_id上使用了ref访问类型来执行查询:

mysql> EXPLAIN SELECT * FROM film_actor WHERE film_id=1\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: film_actorpartitions: NULLtype: ref
possible_keys: idx_fk_film_idkey: idx_fk_film_idkey_len: 2ref: constrows: 10filtered: 100.00Extra: NULL
1 row in set, 1 warning (0.00 sec)

EXPLAIN的结果也显示MySQL预估需要访问10行数据。换句话说,查询优化器认为这种访问类型可以高效地完成查询。如果没有合适地索引会怎样呢?MySQL就不得不使用一种更糟糕地访问类型,下面我们来看看如果我们删除对应的索引再来运行这个查询:

mysql> ALTER TABLE film_actor DROP FOREIGN KEY fk_film_actor_film;
Query OK, 0 rows affected (18.97 sec)
Records: 0  Duplicates: 0  Warnings: 0mysql> ALTER TABLE film_actor DROP KEY idx_fk_film_id;
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> EXPLAIN SELECT * FROM film_actor WHERE film_id=1\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: film_actorpartitions: NULLtype: ALL
possible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 5462filtered: 10.00Extra: Using where
1 row in set, 1 warning (0.00 sec)

正如我们预测的,访问类型变成了一个全表扫描(ALL),现在MySQL预估需要扫描5462条记录来完成这个查询。这里的"Using Where"表示MySQL将通过WHERE条件来筛选存储引擎返回的记录。一般MySQL能够使用如下三种方式应用WHERE条件,从好到坏依次为:

  • 1.在索引中使用WHERE条件来过滤不匹配的记录。这是在存储引擎层完成的
  • 2.使用索引覆盖扫描(在Extra列出现了Using index)来返回记录,直接从素银中过滤不需要的记录并返回命中的结果。这是在MySQL服务器层完成的,但无须再回表查询记录
  • 3.从数据表中返回数据,然后过滤不满足条件的记录(在Extra列中出现Using WHere)。这在MySQL服务器层完成,MySQL需要从数据表独处记录后过滤。
    上面这个例子说明了好的索引多么重要。好的索引可以让查询使用合适的访问了悉尼港,尽可能地只扫描需要的数据行。但也不是说增加索引就能让扫描的行数等于返回的行数。例如下面使用聚合函数COUNT()的查询:
mysql> SELECT * FROM film_actor WHERE film_id =1;

这个查询需要读取几千行数据,但是仅返回200行结果。没有什么索引能够让这样的查询减少需要扫描的行数。不幸的是,MySQL不会告诉我们生成结果实际上需要扫描多少行数据(例如关联查询结果返回的一条记录通常是由多条记录组成的)而只会告诉我们生成结果时一共扫描了多少行数据。扫描的行数中的大部分都很可能是被WHERE条件过滤掉的,对最终结果集并没有贡献。在上面的例子中,删除索引后,看到MySQL需要扫描所有记录然后根据WHERE条件过滤,最终只返回10行结果。理解一个查询需要扫描多少行和实际需要使用的行数需要先去理解这个查询背后的逻辑和思想。

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

  • 1.使用索引覆盖扫描,把所有需要用的列都放到索引中,这样存储引擎无须回表获取对应行就可以返回结果;额
  • 2.改变库表结构。例如使用单独的汇总表
  • 3.重写这个复杂的查询,让MySQL优化器能够以更优化的方式执行这个查询

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

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

相关文章

初识Sass

1、Sass概述 Sass(Syntactically Awesome Style Sheets)是一种CSS预处理器,用于增强CSS的功能和灵活性。 定义与起源: Sass最初由Hampton Catlin设计,由Natalie Weizenbaum开发。它是对CSS3的一种扩充,允许…

家政预约小程序09小程序分享及海报分享

目录 1 设置弹窗2 制作海报总结 上一篇我们介绍了服务详情页面的开发,本篇介绍一下用户分享及海报分享的功能 1 设置弹窗 当用户点击分享按钮的时候,系统弹出弹窗界面,提供分享好友及分享海报的选项。选中页面组件,添加弹窗组件 …

nodeJs项目总结

文章目录 学习总结mongoose指令操作schema 对象将Shcema对象转化为数据模型操作数据库插入查询删除修改限制查询条数 模板引擎语法模版输出条件判断循环 子模版模版继承 项目Login主要步骤及逻辑身份认证session cookiejwt 学习总结 mongoose 指令 mongodb 数据库名 mongod…

了解 IPv4 和 IPv6 之间的区别?

在广阔的互联网环境中,设备之间的通信依赖于一组独特的协议来促进连接。在这些协议中,IPv4(互联网协议第4版)和IPv6(互联网协议第6版)是数字基础设施的支柱,能够跨网络传输数据。但是&#xff0…

基于WIN2016搭建MS2016 ALWAYS ON域控故障转移群集

基于WIN2016搭建MS2016 ALWAYS ON域控故障转移群集 一、前言1、Always On简介2、AD DC域控简介 二、部署实施1、部署环境简介2、搭建流程简介3、域控服务器安装及群集节点加域3.1、安装域控,安装同时会安装DNS系统3.2、执行安装,完成后重启服务器3.3、将…

前端需不需要控制并发请求?浏览器自带并发控制?

不知道为什么,最近大数据给我推荐了几篇前端做控制并发的文章,技术实现是没任何问题,使用到的技术核心也不错,就是应用的地方就有点问题了。 浏览器 HTTP 请求 pending 打开浏览器,network 可以看每个请求的状态&…

如何让 大模型/深度学习 更加聪明(切实有效的8个思考角度)

要让大模型更加聪明,可以采取以下方法: 增加数据量:大模型需要足够的数据来学习和理解复杂的问题。通过增加训练数据的数量,可以帮助模型更全面地学习和推理。 提高模型的复杂度:大模型往往有更多的层和参数&#xff…

「vue同一个组件,不同路由切换时界面没有更新问题」

问题&#xff1a;vue项目中不同路由切换时&#xff0c;因为引用的同一个组件&#xff0c;界面数据没有更新 一、解决方法 添加key&#xff0c;具体原理可参考vue中的diff算法 <router-view :key"$route.fullPath"></router-view>

【代码随想录算法训练营第37期 第二十八天 | LeetCode93.复原IP地址、78.子集、90.子集II】

代码随想录算法训练营第37期 第二十八天 | LeetCode93.复原IP地址、78.子集、90.子集II 一、93.复原IP地址 解题代码C&#xff1a; class Solution { private:vector<string> result;void backtracking(string& s, int startIndex, int pointNum){if(pointNum 3){…

AI大数据处理与分析实战--体育问卷分析

AI大数据处理与分析实战–体育问卷分析 前言&#xff1a;前一段时间接了一个需求&#xff0c;使用AI进行数据分析与处理&#xff0c;遂整理了一下大致过程和大致简要结果&#xff08;更详细就不方便放了&#xff09;。 文章目录 AI大数据处理与分析实战--体育问卷分析一、数据…

【电子通识】什么是电力电子

什么是电力电子 在日常生活中&#xff0c;电能变换的需求无处不在。比如给手机充电&#xff0c;充电器从插座220V交流电取电并转换为手机电池所需的5V或者其他幅度的直流电输送给手机&#xff0c;这就完成了最简单的AC-DC电能转换。除此之外&#xff0c;还有空调、电视、新能源…

【C++】入门(二):引用、内联、auto

书接上回&#xff1a;【C】入门&#xff08;一&#xff09;&#xff1a;命名空间、缺省参数、函数重载 文章目录 六、引用引用的概念引用的使用场景1. 引用做参数作用1&#xff1a;输出型参数作用2&#xff1a;对象比较大&#xff0c;减少拷贝&#xff0c;提高效率 2. 引用作为…

MySQL—约束—外键约束(基础)

一、引言 概念&#xff1a;外键用来让两张表的数据之间建立连接&#xff0c;从而保证数据的一致性和完整性。 举个例子&#xff1a; 提示说明&#xff1a;&#xff08;有两张表&#xff09; &#xff08;1&#xff09;员工表&#xff1a;emp id&#xff1a;主键、姓名、年龄、…

git 提交

当你不小心在master上改了代码&#xff0c;并且add&#xff0c; commit 之后&#xff0c;想push&#xff0c;发现根本push不了&#xff08;本来也不能直接将master代码push到远端&#xff09;&#xff0c;而且每次pull远程master的时候都要和本地的master进行merge &#xff0c…

机器人系统ros2-开发学习实践16-RViz 用户指南

RViz 是 ROS&#xff08;Robot Operating System&#xff09;中的一个强大的 3D 可视化工具&#xff0c;用于可视化机器人模型、传感器数据、路径规划等。以下是RViz用户指南&#xff0c;帮助你了解如何使用RViz来进行机器人开发和调试。 启动可视化工具 ros2 run rviz2 rviz2…

css-Ant-Menu 导航菜单更改为左侧列表行选中

1.Ant-Menu导航菜单 导航菜单是一个网站的灵魂&#xff0c;用户依赖导航在各个页面中进行跳转。一般分为顶部导航和侧边导航&#xff0c;顶部导航提供全局性的类目和功能&#xff0c;侧边导航提供多级结构来收纳和排列网站架构。 2.具体代码 html <!-- 左侧切换 --><…

15、matlab绘图汇总(图例、标题、坐标轴、线条格式、颜色和散点格式设置)

1、plot()函数默认格式画图 代码: x=0:0.1:20;%绘图默认格式 y=sin(x); plot(x,y) 2、X轴和Y轴显示范围/axis()函数 代码: x=0:0.1:20;%绘图默认格式 y=sin(x); plot(x,y) axis([0 21 -1.1 1.1])%设置范围 3、网格显示/grid on函数 代码: x=0:0.1:20;%绘图默认格式 …

CXL (1)

为什么有CXL CXL说到底 是为了打破内存墙而生的 CXL全称是Compute Express Link&#xff0c; 可以用来连接CPU&#xff0c;以及其他任何计算单元&#xff0c;比如GPU。 CXL和PCIe跑在一样的physical layer上&#xff0c;与PCIe不一样的是&#xff0c;CXL允许CPU和连接的设备共…

Python | R 潜在混合模型

&#x1f4dc;用例 &#x1f4dc;Python | MATLAB | R 心理认知数学图形模型推断 | &#x1f4dc;信用卡消费高斯混合模型 | &#x1f4dc;必修课学业成绩分布异常背景混合模型潜在类别分析 ✒️潜在混合模型 本质上&#xff0c;混合模型&#xff08;或混合分布&#xff09;…

(文章复现)基于主从博弈的售电商多元零售套餐设计与多级市场购电策略

参考文献&#xff1a; [1]潘虹锦,高红均,杨艳红,等.基于主从博弈的售电商多元零售套餐设计与多级市场购电策略[J].中国电机工程学报,2022,42(13):4785-4800. 1.摘要 随着电力市场改革的发展&#xff0c;如何制定吸引用户选择的多类型零售套餐成为提升售电商利润的研究重点。为…