实际项目中遇到的MySQL调优问题及解决方式

2023年 7月 28日 71.3k 0

一、前言

前段时间自己做项目,项目用到了MySQL技术栈,主要是用MySQL记录任务信息,后面在压测的时候,发现一个接口的QPS始终上不去,后来经过排查发现,该接口在运行的过程中使用了慢查询语句,通过排查慢查询语句,最后发现是没有对字段建立联合索引,在建立了联合索引之后,性能提升了将近两倍。此文记录自己踩过的一些坑,以及解决方案。

二、如何找到慢SQL语句

MySQL提供了慢查询日志,可以帮助我们找到慢查询语句。

2.1 什么是慢查询日志?

MySQL的慢询日志,可以记录在MySQL中,响应时间超过指定阈值的语句,比如设定阈值为3秒,那么任何SQL执行超过3秒都会被记录下来。

我们借助慢查询日志功能可以发现哪些那些执行时间特别长的询,并且有针对性地进行优化,从而提高系统的整体效率。

2.2 怎么开启慢查询日志

慢查询日志一般默认是关闭的,因为开启慢查询日志会对性能造成一定的影响,我们只需要在性能调优的时候开启就可以了,所以慢查询日志需要我们自己去开启。

执行下面命令查看是否开启慢SQL:

image.png

如果是off就是没开启,是on就是开启了。

如何通过命令开启慢查询日志?

 set global slow_query_log='ON';

2.3 如何设置慢查询日志的阈值

set global long_query_time = N;

这里的单位是秒,设置为N就是N秒,所有执行时间超过N秒的sql语句都会被记录在慢查询日志里。

2.4 如何查看慢查询日志的内容

先找到慢查询日志的位置:

show variables like '%slow_query_log_file%';

查看日志内容可以使用cat命令来查看:

cat xxx.log

三、explain分析工具的使用

explain是MySQL自带的执行分析工具,把他加在sql语句前面,就可以展示出该sql语句的具体的执行计划。

执行计划是按照多个字段来展示的,我们平时在调优的时候主要关注以下几个关键字段:type,possible keys,key,rows,filtered,extra。下面我会一一介绍这些字段的具体含义。

3.1 type字段

type字段表示当前语句所执行的类型,有如下几种类型:

  • all:全表扫描,基于表中所有的数据,逐行扫描并过滤符合条件的数据。

  • index:全索引扫描,和全表扫描类似,但这个是把索引树遍历一次,会比全表扫描要快。

  • range:基于索引字段进行范围查询,如between、、in....等操作时出现的情况。

  • index_subquery:和上面含义相同,区别:这个是基于非主键、唯一索引字段进行in操作。

  • unique_subquery:执行基于主键索引字段,进行in操作的子查询语句会出现的情况。

  • index_merge:多条件查询时,组合使用多个索引来检索数据的情况。

  • ref_or_null:基于次级(非主键)索引做条件查询时,该索引字段允许为null出现的情况。

  • fulltext:基于全文索引字段,进行查询时出现的情况。

  • ref:基于非主键或非唯一索引字段查找数据时,会出现的情况。

  • eq_ref:连表查询时,基于主键、唯一索引字段匹配数据的情况,会出现多次索引查找。

  • const:通过索引一趟查找后就能获取到数据,基于唯一、主键索引字段查询数据时的情况。

  • system:表中只有一行数据,这是const的一种特例。

  • null:表中没有数据,无需经过任何数据检索,直接返回结果。

字段很多,常用的有下面几个,性能由好到坏依次是:const->eq_ref->ref->fulltext->range->index->all

平时索引优化,最差也要优化到range,如果是index甚至是all,是不允许的,太慢了。

3.2 key字段

possible keys表示可以使用的索引,key是最终优化器选择使用的索引。注意,并不是有索引优化器就会选择使用索引,优化器会估算查询的cost,选择最低的那个查询方式。如果索引的基数太大,也有可能不会选用,因为使用二级索引意味着要回表,回表也是一个很大的开销。

默认情况下,possible_keys有值时都会从中选取一个索引,但这个选择的工作是由MySQL优化器自己决定的,如果你想让查询语句执行时走固定的索引,则可以通过force index、ignore index的方式强制指定。

3.3 rows字段

这一列代表执行时,预计会扫描的行数,这个数字是估算的,所以并不是十分精确,但也具备很大的参考价值,如果这个值很大,在执行查询语句时,其效率必然很低,所以该值越小越好。

3.4 filtered 字段

这个字段仅作参考。filter字段的含义是,存储引擎返回的数据在server层过滤后,剩下的满足查询条件的记录数量占存储引擎返回数据的百分比,因此这个值越大说明过滤掉的越少,就“越好”。但显然,filtered如果很小,只能说明被过滤的多了,并不一定说明索引不好或者引擎效率不高,所以这个值实质上参考意义不大。

3.5 extra字段

extra包含的是额外的信息,但是这个信息对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 MRR:表示执行查询时,使用了MRR机制读取数据。

  • Using index for skip scan:表示执行查询语句时,使用了索引跳跃扫描机制读取数据。

  • Using index for group-by:表示执行分组或去重工作时,可以基于某个索引处理。

  • FirstMatch:表示对子查询语句进行Semi-join优化策略。

  • No tables used:查询语句中不存在from子句时提示的信息,如desc table_name;

通常情况下当Extra中出现了Using filesort、Using temporary或Using where,则说明你写的SQL需要优化了。

四、项目中遇到的问题

本项目是一个异步任务处理框架,其中服务模块提供了一个从MySQL数据库占据任务的一个接口。占据任务的时候,是根据任务的状态和优先级先进行排序,再拉取指定数量的任务的。

任务的状态分为待执行和执行中,以及成功和失败,拉取任务的接口只能拉取待执行的任务,在本项目中,我用的是status关键字来记录任务的状态。在任务状态为待执行的任务里,我们还要对优先级排序字段进行排序,优先级越大的,越有可能被拉去到,越有可能被最先执行。

一开始的时候,我对status字段和优先级字段分别建立了索引,但是忘记了建立联合索引,这个时候hold_task接口去拉取任务的时候,就会比较慢,因为数据量还是比较大的,有数十万。

后来,压测的时候发现hold_task的QPS始终不是很高,于是就对该接口进行检查,通过慢查询日志,定位到了慢sql语句,就是select xx from table where status = “待执行” ORDERED BY order_time(伪代码) 。然后对这个sql语句使用explain关键字进行分析,发现type字段是index_merge,extra字段的值是Using where;Using index,没有走联合索引,而是索引合并,而且需要回表。

于是再回去检查MySQL表之后发现,忘记建立联合索引了,于是对status字段和优先级字段建立了联合索引,这样一来就避免了回表查询。建立联合索引之后再使用explain关键字进行分析的时候就发现,type字段变成了ref,extra字段的值变成了Using index,使用了覆盖索引,再进行压测,QPS就增大了一倍多。

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论