MySQL中优化SQL语句的方法(show status、explain分析服务器状态信息)


Posted in MySQL onApril 09, 2022

1.概述

在应用系统开发过程中,由于初期数据量小,开发人员写SQL语句时更重视功能上的实现,但是当应用系统正式上线后,随着生产数据量的急剧增长,很多SQL语句开始逐渐显露出性能问题,对生产环境的影响也越来越大,此时这些有问题的SQL语句就成为整个系统性能的瓶颈,因此我们必须要对它们进行优化,该章节将详细介绍在MySQL中优化SQL语句的方法。

2.通过show status命令了解各种SQL的执行频率

MySQL客户端连接成功后,通过show [session|global]status命令可以提供服务器状态信息,也可以在操作系统上使用mysqladmin extended-status命令获得这些消息。show [session|global] status可以根据需要加上参数“session”或者“global”来显示session级(当前连接)的统计结果和global级(自数据库上次启动至今)的统计结果。如果不写,默认使用参数是“session”。
下面的命令显示了当前session中所有统计参数的值:

-- 查看会话所有统计的值
SHOW STATUS LIKE 'Com_%';
Or
SHOW SESSION STATUS LIKE 'Com_%';

MySQL中优化SQL语句的方法(show status、explain分析服务器状态信息)

下面的命令显示了当前global中所有统计参数的值:
-- 查看全局所有统计的值

SHOW GLOBAL STATUS LIKE 'Com_%';

MySQL中优化SQL语句的方法(show status、explain分析服务器状态信息)
Com_xxx表示每个xxx语句执行的次数,我们通常比较关心的是以下几个统计参数:
●Com_select:执行SELECT操作的次数,一次查询只累加1。
●Com_insert:执行INSERT操作的次数,对于批量插入的INSERT操作,只累加一次。
●Com_update:执行UPDATE操作的次数。
●Com_delete:执行DELETE操作的次数。
上面这些参数对于所有存储引擎的表操作都会进行累计。下面这几个参数只是针对InnoDB存储引擎的,累加的算法也略有不同。
●Innodb_rows_read:SELECT查询返回的行数。
●Innodb_rows_inserted:执行INSERT操作插入的行数。
●Innodb_rows_updated:执行UPDATE操作更新的行数。
●Innodb_rows_deleted:执行DELETE操作删除的行数。
通过以上几个参数,可以很容易地了解当前数据库的应用系统是以插入更新为主还是以查询操作为主,以及各种类型的SQL大致的执行比例是多少。对于更新操作的计数,是对执行次数的计数,不论提交还是回滚都会进行累加。
对于事务型的应用,通过Com_commit和Com_rollback可以了解事务提交和回滚的情况,对于回滚操作非常频繁的数据库,可能意味着应用编写存在问题。此外,以下几个参数便于用户了解数据库的基本情况。 
●Connections:试图连接MySQL服务器的次数。
●Uptime:服务器工作时间。
●Slow_queries:慢查询的次数。

3.定位执行效率较低的SQL语句

可以通过以下两种方式定位执行效率较低的SQL语句。
通过慢查询日志定位那些执行效率较低的SQL语句,用--log-slow-queries[=file_name]选项启动时,mysqld写一个包含所有执行时间超过long_query_time秒的SQL语句的日志文件。
慢查询日志在查询结束以后才纪录,所以在应用系统反映执行效率出现问题的时候查询慢查询日志并不能定位问题,可以使用show processlist命令查看当前MySQL在进行的线程,包括线程的状态、是否锁表等,可以实时地查看SQL的执行情况,同时对一些锁表操作进行优化。

4.通过EXPLAIN分析低效SQL的执行计划

通过定位执行效率较低的SQL语句后,可以通过EXPLAIN或者DESC命令获取MySQL如何执行SELECT语句的信息,包括在SELECT语句执行过程中表如何连接和连接的顺序,比如想统计所有库存阶梯数量,需要关联goods_stock表和goods_stock_price表,并且对goods_stock_price.Qty字段做求和(sum)操作,相应 SQL 的执行计划如下:

EXPLAIN SELECT SUM(sp.Qty)
FROM goods_stock AS s LEFT JOIN goods_stock_price AS sp
ON s.ID=sp.GoodsStockID;

MySQL中优化SQL语句的方法(show status、explain分析服务器状态信息)

如上图所示每个列的简单解释如下:

●select_type:表示 SELECT 的类型,常见的取值有:
  ◎SIMPLE(简单表,即不使用表连接 或者子查询)。
  ◎PRIMARY(主查询,即外层的查询)、UNION(UNION 中的第二个或 者后面的查询语句)、◎SUBQUERY(子查询中的第一个SELECT)等。
●table:输出结果集的表。
●type:表示表的连接类型,性能由好到差的连接类型为:
  ◎system(表中仅有一行,即常量表)。
  ◎const(单表中最多有一个匹配行,例如primary key或者unique index)。
  ◎eq_ref(对于前面的每一行,在此表中只查询一条记录,简单来说,就是多表连接中使用primary key或者unique index)。
  ◎ref(与eq_ref类似,区别在于不是使用primary key或者unique index,而是使用普通的索引)。
  ◎ref_or_null(与ref类似,区别在于条件中包含对NULL的查询)。
  ◎index_merge(索引合并优化)。
  ◎unique_subquery(in的后面是一个查询主键字段的子查询)。
  ◎index_subquery(与unique_subquery类似,区别在于in的后面是查询非唯一索引字段的子查询)。
  ◎range(单表中的范围查询)。
  ◎index(对于前面的每一行,都通过查询索引来得到数据)。
  ◎all(对于前面的每一行,都通过全表扫描来得到数据)。
●possible_keys:表示查询时,可能使用的索引。
●key:表示实际使用的索引。
●key_len:索引字段的长度。
●rows:扫描行的数量。
●filtered:返回结果的行占需要读到的行(rows列的值)的百分比。
●Extra:执行情况的说明和描述。
  ◎Using index(此值表示mysql将使用覆盖索引,以避免访问表)。
  ◎Using where(mysql 将在存储引擎检索行后再进行过滤,许多where条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验,因此不是所有带where子句的查询都会显示“Using where”。有时“Using where”的出现就是一个暗示:查询可受益于不同的索引)。
  ◎Using temporary(mysql 对查询结果排序时会使用临时表)。
  ◎Using filesort(mysql会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。mysql有两种文件排序算法,这两种排序方式都可以在内存或者磁盘上完成,explain不会告诉你mysql将使用哪一种文件排序,也不会告诉你排序会在内存里还是磁盘上完成)。
  ◎Range checked for each record(index map: N) (没有好用的索引,新的索引将在联接的每一行上重新估算,N是显示在possible_keys列中索引的位图,并且是冗余的)。

5.确定问题并采取相应的优化措施

经过以上定位步骤,我们基本就可以分析到问题出现的原因。此时我们可以根据情况采取相应的改进措施,进行优化提高语句执行效率。
在上面的例子中,已经可以确认是goods_stock是走主键索引的,但是对goods_stock_price子表的进行了全表扫描导致效率的不理想,那么应该对goods_stock_price表的GoodsStockID字段创建索引,具体命令如下:

-- 创建索引CREATE INDEX idx_stock_price_1 ON goods_stock_price (GoodsStockID);-- 附加删除跟查询索引语句ALTER TABLE goods_stock_price DROP INDEX idx_stock_price_1;SHOW INDEX FROM goods_stock_price;

创建索引后,我们再看一下这条语句的执行计划,具体如下:

EXPLAIN SELECT SUM(sp.Qty)
FROM goods_stock AS s LEFT JOIN goods_stock_price AS sp
ON s.ID=sp.GoodsStockID;

MySQL中优化SQL语句的方法(show status、explain分析服务器状态信息)
可以发现建立索引后对goods_stock_price子表需要扫描的行数明显减少(从 3 行减少到1行),可见索引的使用可以大大提高数据库的访问速度,尤其在表很庞大的时候这种优势更为明显。

参考文献:
深入浅出MySQL大全

MySQL 相关文章推荐
mysql字符串截取函数小结
Apr 05 MySQL
mysql数据库入门第一步之创建表
May 14 MySQL
MySQL 重命名表的操作方法及注意事项
May 21 MySQL
MySQL 使用事件(Events)完成计划任务
May 24 MySQL
MySQL中in和exists区别详解
Jun 03 MySQL
MySQL中连接查询和子查询的问题
Sep 04 MySQL
SQL基础查询和LINQ集成化查询
Jan 18 MySQL
MySQL RC事务隔离的实现
Mar 31 MySQL
解决MySQL报“too many connections“错误
Apr 19 MySQL
MySQL索引 高效获取数据的数据结构
May 02 MySQL
MySQL事务的ACID特性以及并发问题方案
Jul 15 MySQL
MySQL中dd::columns表结构转table过程及应用详解
Sep 23 MySQL
进阶篇之linux环境下安装MySQL数据库
MySQL的存储函数与存储过程的区别解析
Apr 08 #MySQL
MySQL数据库查询进阶之多表查询详解
MySQL中一条SQL查询语句是如何执行的
解决MySQL Varchar 类型尾部空格的问题
Apr 06 #MySQL
mysql的单列多值存储实例详解
Apr 05 #MySQL
详细聊一聊mysql的树形结构存储以及查询
You might like
使用sockets:从新闻组中获取文章(一)
2006/10/09 PHP
php抓即时股票信息
2006/10/09 PHP
php的memcache类分享(memcache队列)
2014/03/26 PHP
ThinkPHP标签制作教程
2014/07/10 PHP
php强制下载文件函数
2016/08/24 PHP
Yii 框架使用Forms操作详解
2020/05/18 PHP
jquery 3D球状导航的文章分类
2010/07/06 Javascript
JS获取URL中的参数数据
2013/12/05 Javascript
javascript中取前n天日期的两种方法分享
2014/01/26 Javascript
js中实现多态采用和继承类似的方法
2014/08/22 Javascript
jquery用ajax方式从后台获取json数据后如何将内容填充到下拉列表
2015/08/26 Javascript
javascript中加var和不加var的区别 你真的懂吗
2016/01/06 Javascript
针对JavaScript中this指向的简单理解
2016/08/26 Javascript
Bootstrap 模态框(Modal)插件代码解析
2016/12/21 Javascript
RequireJS 依赖关系的实例(推荐)
2017/01/21 Javascript
js实现简单的获取验证码按钮效果
2017/03/03 Javascript
JS继承与闭包及JS实现继承的三种方式
2017/10/15 Javascript
详解在Angular4中使用ng2-baidu-map的方法
2019/06/19 Javascript
node后端服务保活的实现
2019/11/10 Javascript
vue flex 布局实现div均分自动换行的示例代码
2020/08/05 Javascript
[04:26]DOTA2上海特锦赛小组赛第二日 TOP10精彩集锦
2016/02/27 DOTA
python 异或加密字符串的实例
2018/10/14 Python
Python协程 yield与协程greenlet简单用法示例
2019/11/22 Python
python爬虫爬取网页数据并解析数据
2020/09/18 Python
vue.js刷新当前页面的实例讲解
2020/12/29 Python
药学专业大学生个人的自我评价
2013/11/04 职场文书
生产部厂长职位说明书
2014/03/03 职场文书
周年庆促销方案
2014/03/15 职场文书
广告创意求职信
2014/03/17 职场文书
公务员上班玩游戏检讨书
2014/09/17 职场文书
三严三实对照检查材料思想汇报
2014/09/28 职场文书
先进个人事迹材料
2014/12/29 职场文书
pytorch损失反向传播后梯度为none的问题
2021/05/12 Python
解决python3安装pandas出错的问题
2021/05/20 Python
Python使用海龟绘图实现贪吃蛇游戏
2021/06/18 Python
MySQL的存储函数与存储过程的区别解析
2022/04/08 MySQL