mysql中Query语句优化基本思路和原则

tech2024-07-30  58

参考:https://phpmianshi.com/?id=151

基本思路和原则

 

在分析如何优化 MySQL Query 之前,我们需要先了解一下 Query 语句优化的基本思路和原则。一般来说,Query 语句的优化思路和原则主要体现在以下几个方面: 

 

1. 优化更需要优化的 Query; 

2. 定位优化对象的性能瓶颈; 

3. 明确的优化目标; 

4. 从 Explain 入手; 

5. 多使用 profile 

6. 永远用小结果集驱动大的结果集; 

7. 尽可能在索引中完成排序; 

8. 只取出自己需要的 Columns; 

9. 仅仅使用最有效的过滤条件; 

10. 尽可能避免复杂的 Join 和子查询;

 

上面所列的几点信息,前面 4 点可以理解为 Query优化的一个基本思路,后面部分则是我们优化中的基本原则。

 

优化更需要优化的 Query

 

那什么样的 Query更需要优化呢?对于这个问题我们需要从对整个系统的影响来考虑。什么 Query 的优化能给系统整体带来更大的收益,就更需要优化。一般来说,高并发低消耗(相对)的 Query 对整个系统的影响远比低并发高消耗的 Query大。

 

我们从对整个系统的影响来分析。一个频繁执行的高并发 Query 的危险性比一个低并发的 Query 要大很多。当一个低并发的 Query 走错执行计划,所带来的影响主要只是该 Query 的请求者的 体验会变差,对整体系统的影响并不会特别的突出,至少还属于可控范围。但是,如果我们一个高并发 的 Query 走错了执行计划,那所带来的后果很可能就是灾难性的,很多时候可能连自救的机会都不给你 就会让整个系统 Crash 掉。

 

定位优化对象的性能瓶颈

 

MySQL 的 Query Profiler 是一个使用非常方便的 Query 诊断分析工具,通过该工具可以获取一条Query 在整个执行过程中多种资源的消耗情况,如 CPU,IO,IPC,SWAP 等,以及发生的 PAGE FAULTS,CONTEXT SWITCHE 等等,同时还能得到该 Query 执行过程中 MySQL 所调用的各个函数在源文件中的位置。

 

 

明确的优化目标

 

一般来说,我们首先需要清楚的 了解数据库目前的整体状态,同时也要清楚的知道数据库中与该 Query 相关的数据库对象的各种信息, 而且还要了解该 Query 在整个应用系统中所实现的功能。了解了数据库整体状态,我们就能知道数据库 所能承受的最大压力,也就清楚了我们能够接受的最悲观情况。把握了该 Query 相关数据库对象的信 息,我们就应该知道实现该 Query 的消耗最理想情况下需要消耗多少资源,最糟糕又需要消耗多少资 源。最后,通过该 Query 所实现的功能点在整个应用系统中的重要地位,我们可以大概的分析出该 Query 可以占用的系统资源比例,而且我们也能够知道该 Query 的效率给客户带来的体验影响到底有多大

 

从 Explain 入手

 

Explain 用来获取一个 Query 在当前状态的数据库中的执行计划。在优化动手之前,我们必须要根据优化目标在自己头脑中有一个清晰的目标执行计划。只有这样, 优化的目标才有意义。一个优秀的 SQL 调优人员(或者成为 SQL Performance Tuner),在优化任何一 个 SQL 语句之前,都应该在自己头脑中已经先有一个预定的执行计划,然后通过不断的调整尝试,再借 助 Explain 来验证调整的结果是否满足自己预定的执行计划。对于不符合预期的执行计划需要不断分析 Query 的写法和数据库对象的信息,继续调整尝试,直至得到预期的结果。

当然,人无完人,并不一定每次自己预设的执行计划都肯定是最优的,在不断调整测试的过程中, 如果发现 MySQL Optimizer 所选择的执行计划的实际执行效果确实比自己预设的要好,我们当然还是应 该选择使用 MySQL optimizer 所生成的执行计划。

 

永远用小结果集驱动大的结果集

 

很多人喜欢在优化 SQL 的时候说用小表驱动大表,个人认为这样的说法不太严谨。为什么?因 为大表经过 WHERE 条件过滤之后所返回的结果集并不一定就比小表所返回的结果集大,可能反而更小。 在这种情况下如果仍然采用小表驱动大表,就会得到相反的性能效果。

 

其实这样的结果也非常容易理解,在 MySQL 中的 Join,只有 Nested Loop 一种 Join 方式,也就是 MySQL 的 Join 都是通过嵌套循环来实现的。驱动结果集越大,所需要循环的此时就越多,那么被驱动表 的访问次数自然也就越多,而每次访问被驱动表,即使需要的逻辑 IO 很少,循环次数多了,总量自然也 不可能很小,而且每次循环都不能避免的需要消耗 CPU ,所以 CPU 运算量也会跟着增加。所以,如果 我们仅仅以表的大小来作为驱动表的判断依据,假若小表过滤后所剩下的结果集比大表多很多,结果就 是需要的嵌套循环中带来更多的循环次数,反之,所需要的循环次数就会更少,总体 IO 量和 CPU 运算 量也会少。而且,就算是非 Nested Loop 的 Join 算法,如 Oracle 中的 Hash Join,同样是小结果集 驱动大的结果集是最优的选择

 

只取出自己需要的 Columns

 

对于任何 Query,返回的数据都是需要通过网络数据包传回给客户端,如果取出的 Column 越多, 需要传输的数据量自然会越大,不论是从网络带宽方面考虑还是从网络传输的缓冲区来看,都是一个浪费。

 

仅仅使用最有效的过滤条件

很多人在优化 Query 语句的时候很容易进入一个误区,那就是觉得 WHERE 子句中的过滤条件越多 越好,实际上这并不是一个非常正确的选择。其实我们分析 Query 语句的性能优劣最关键的就是要让他选择一条最佳的数据访问路径

 

尽可能避免复杂的 Join 和子查询

 

我们的 Query 语句所涉及到的表越多,所需要锁定的资源就越多。也就是说,越复杂的 Join 语句,所需要锁定的资源也就越多,所阻塞的其他线程也就越多。相反,如果我们将比较复杂的 Query 语句分拆成多个较为简单的 Query语句分步执行,每次锁定的资源也就会少很多,所阻塞的其他线程也要少一些

 

可能很多读者会有疑问,将复杂 Join 语句分拆成多个简单的 Query 语句之后,那不是我们的网络 交互就会更多了吗?网络延时方面的总体消耗也就更大了啊,完成整个查询的时间不是反而更长了吗? 是的,这种情况是可能存在,但也并不是肯定就会如此。我们可以再分析一下,一个复杂的 Join Query 语句在执行的时候,所需要锁定的资源比较多,可能被别人阻塞的概率也就更大,如果是一个简单的 Query,由于需要锁定的资源较少,被阻塞的概率也会小很多。所以 较为复杂的 Join Query 也有可能 在执行之前被阻塞而浪费更多的时间。而且,我们的数据库所服务的并不是单单这一个 Query 请求,还 有很多很多其他的请求,在高并发的系统中,牺牲单个 Query 的短暂响应时间而提高整体处理能力也是 非常值得的。优化本身就是一门平衡与取舍的艺术,只有懂得取舍,平衡整体,才能让系统更优。

最新回复(0)