MySQL调优问题与解决方案

114 阅读6分钟

前言

在实际项目开发过程中,在MySQL进行数据查询时遇到了一些问题,在后面对查询端口进行压测后发现:由于在查询过程中对 Order By 排序字段建立联合索引,导致使用到了外部排序,性能影响很大。后续为其建立联合索引后,性能显著提高。

MySQL调优过程

SQL优化的两大目的:减少磁盘 IO减少内存 CPU 消耗

解决慢 SQL 的路径:

1、找到慢查询语句

慢查询日志默认是关闭状态的,可以通过如下命令来查看慢查询日志的开关状态并且查询慢查询日志的路径。

#查看慢查询日志开关状态
show variables like `%slow_query_log%`;

#开启慢查询日志命令
set global slow_query_log=`ON`;

设置阈值

设置阈值将 SQL 中执行时间超过阈值的语句记录下来,我们通过查看哪条语句执行时间比较长,来针对性的就慢查询语句进行优化,从而提高整体的性能。

#设置阈值
set global long_query_time=N;

2、使用 explain 分析 SQL

当我们找到慢 SQL 后,需要分析该 SQL 的执行计划,查找到该条语句是否走了索引,走的哪条索引,找到对性能产生不利的表现内容。

我们使用MySQL自带的执行分析工作 explain, 将explain 加到 sql 语句前面,就可以展现出该 sql 具体的执行细节。在调优过程中,我们主要针对几个关键字段进行分析:type, key, rows, extra

type:

type 代表的是访问类型,常见的访问类型如下:

all(性能最差):表示 MySQL需要扫描表中的所有行,即全表扫描。通常出现在没有索引的查询条件中

index:表示 MySQL扫描索引中的所有行,而不是表中的所有行。即使索引列的值覆盖查询,也需要扫描整个索引。

range:表示 MySQL会扫描表的一部分,而不是全部行。范围扫描通常出现在使用索引的范围查询中。

ref: MySQL使用非唯一索引扫描查询行。查询条件使用的索引是非唯一的。

eq_ref:表示对与每个来自于前一个表的行,MySQL 仅访问一次这个表,这种通常在连接查询中使用主键或者是唯一索引的情况下。

const:表示查询的表中最多只有一行匹配结果。通常发生在查询条件是主键或者唯一索引,并且是常量比较。

查询的效率是从上往下依次递增的,在我们的实际项目中是使用的 ref ,查询效率还是可以接受的。

key

通过观察 key 字段可以查看这条 SQL 语句实际用到的索引情况

rows

rows 字段代表 该语句在执行过程中,预计会扫描的行数, 虽然并不是很精确,但是具有很大的参考价值,查询行数较少的情况下,效率会比较高。

extra

extra 字段对于 sql 语句优化来说是非常重要的,因为它包含了很多额外的信息,我们可以通过观察这些信息,来找到 SQL 的执行过程。常用字段如下:

Using index:表示目前的查询语句,使用了索引覆盖机制拿到了数据。

Using where:表示目前的查询语句无法从索引中获取数据,需要进一步做回表去拿表数据,也就是回表。通常出现这种情况的话就需要添加合适的索引来做优化了。

Using temporary:表示MySQL在执行查询时,会创建一张临时表来处理数据,MySQL需要创建一个临时表来保存结果。经常出现在查询包含以不同方式列出列的GROUP BY和ORDER BY子句的情况。

Using filesort:表示MySQL会对结果使用一个外部索引排序,而不是从表里按索引次序读到相关内容。可能在内存或者磁盘上进行排序。 MySQL中无法利用索引完成的排序操作称为“文件排序”

Select tables optimized away:表示查询过程中,对于索引字段使用了聚合函数。

Using where;Using index:表示要返回的数据在索引中包含,但并不是索引的前导列,需要做回表获取数据。

NULL:表示查询的数据未被索引覆盖,但where条件中用到了主键,可以直接读取表数据。

Using index condition:和Using where类似,要返回的列未完全被索引覆盖,需要回表。会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。

Using join buffer (Block Nested Loop):连接查询时驱动表不能有效的通过索引加快访问速度时,会使用join-buffer来加快访问速度,在内存中完成Loop匹配。

Impossible WHEREwhere后的条件永远不可能成立时提示的信息,如where 1!=1

Impossible WHERE noticed after reading const tables:基于唯一索引查询不存在的值时出现的提示。

const row not found:表中不存在数据时会返回的提示。

distinct:去重查询时,找到某个值的第一个值时,会将查找该值的工作从去重操作中移除。

Start temporary, End temporary:表示临时表用于DuplicateWeedout半连接策略,也就是用来进行semi-join去重。

当遇到Using filesort、Using temporary或Using where时,可以考虑对SQL语句进行优化。

3、具体调优过程

在我的一个项目中,需要不断地去从 MySQL 中拉取数据,拉取数据的判断条件是 先查询任务状态是等待执行的,然后再根据优先级字段进行排序,最后拉取排序以后的前500条数据,由于涉及到排序,所以用到了order by语句。

在刚开始的设计过程中,我只是给状态字段和优先级字段分别建立了索引,但是在压测的过程中发现拉取任务的端口性能很差,然后通过慢查询语句进行定位,最后通过expain 字段对该条 SQL 语句进行解析,发现 extra 字段中包含了额外的排序操作(file sort),由于包含了额外操作就会导致查询操作非常的耗时。

在后续的优化过程中,我对任务状态字段以及优先级字段建立了联合索引,由于索引是有序的,所以就可以避免排序操作,避免file sort的过程,对于该端口的性能提升很大,查询时间从秒级别提升到了毫秒级别。

在这里做这个分享,希望对其他小白有所帮助。