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 07 MySQL
MySQL pt-slave-restart工具的使用简介
Apr 07 MySQL
MySQL Innodb关键特性之插入缓冲(insert buffer)
Apr 08 MySQL
Mysql MVCC机制原理详解
Apr 20 MySQL
MySQL Router的安装部署
Apr 24 MySQL
MySQL 那些常见的错误设计规范,你都知道吗
Jul 16 MySQL
MySQL 1130异常,无法远程登录解决方案详解
Aug 23 MySQL
关于MySQL中的 like操作符详情
Nov 17 MySQL
浅谈mysql哪些情况会导致索引失效
Nov 20 MySQL
关于mysql中时间日期类型和字符串类型的选择
Nov 27 MySQL
MySQL RC事务隔离的实现
Mar 31 MySQL
MySQL性能指标TPS+QPS+IOPS压测
Aug 05 MySQL
进阶篇之linux环境下安装MySQL数据库
MySQL的存储函数与存储过程的区别解析
Apr 08 #MySQL
MySQL数据库查询进阶之多表查询详解
MySQL中一条SQL查询语句是如何执行的
解决MySQL Varchar 类型尾部空格的问题
Apr 06 #MySQL
mysql的单列多值存储实例详解
Apr 05 #MySQL
详细聊一聊mysql的树形结构存储以及查询
You might like
PHP扩展模块Pecl、Pear以及Perl的区别
2014/04/09 PHP
PHP_SELF,SCRIPT_NAME,REQUEST_URI区别
2014/12/24 PHP
php简单统计在线人数的方法
2016/05/10 PHP
PHP获取中国时间(上海时区时间)及美国时间的方法
2017/02/23 PHP
VBS通过WMI监视注册表变动的代码
2011/10/27 Javascript
Javascript图像处理—平滑处理实现原理
2012/12/28 Javascript
JS实现下拉框的动态添加(附效果)
2013/04/03 Javascript
DOM基础教程之模型中的模型节点
2015/01/19 Javascript
微信小程序 swiper组件详解及实例代码
2016/10/25 Javascript
jQuery插件MovingBoxes实现左右滑动中间放大图片效果
2017/02/28 Javascript
JavaScript实现焦点进入文本框内关闭输入法的核心代码
2017/09/20 Javascript
利用Javascript获取选择文本所在的句子详解
2017/12/03 Javascript
json.stringify()与json.parse()的区别以及用处
2021/01/25 Javascript
Python写的英文字符大小写转换代码示例
2015/03/06 Python
python实现俄罗斯方块
2018/06/26 Python
Python封装原理与实现方法详解
2018/08/28 Python
python Selenium实现付费音乐批量下载的实现方法
2019/01/24 Python
导致python中import错误的原因是什么
2020/07/01 Python
HTML5 Canvas绘制圆点虚线实例
2015/01/01 HTML / CSS
受外贸欢迎的美国主机:BlueHost
2017/05/16 全球购物
美国婴儿用品及配件购买网站:Munchkin
2019/04/03 全球购物
印尼在线购买隐形眼镜网站:Lensza.co.id
2019/04/27 全球购物
澳大利亚优惠网站:Deals.com.au
2019/07/02 全球购物
澳大利亚购买健身器材网站:Gym Direct
2019/12/19 全球购物
芭比波朗加拿大官方网站:Bobbi Brown Cosmetics CA
2020/11/05 全球购物
车间组长岗位职责
2013/12/20 职场文书
安全月活动总结
2014/05/05 职场文书
机械电子工程专业自荐书
2014/06/10 职场文书
体育专业求职信
2014/07/16 职场文书
员工保密协议书
2014/09/27 职场文书
农民工工资保障承诺书
2015/05/04 职场文书
孔子观后感
2015/06/08 职场文书
小学生节约用水倡议书
2019/08/12 职场文书
解决ObjectMapper.convertValue() 遇到的一些问题
2021/06/30 Java/Android
Java中try catch处理异常示例
2021/12/06 Java/Android
VUE使用draggable实现组件拖拽
2022/04/06 Vue.js