MySQL 性能分析备忘录

嘉兴ing

[TOC]

最后修改时间: 2019年10月21日15:08:59

救急

show full processlist

查看当前线程处理情况, 确认当前有哪些语句在执行, 执行情况如何.

特别注意执行时间长的, 如果确定有问题, 那么可以使用 kill {id} 干掉该连接

show full processlist 等价于以下语句

select id, db, user, host, command, time, state, info
from information_schema.processlist
order by time desc;

注意:

  • show processlist时, root账号可以看到所有账号的连接, 如果是普通账号则只能看到自己的连接.
  • 如果显示长度太长看的很乱, 可以使用 \G, 即 show full processlist\G , 来将显示结果纵向输出, 方便查看.
扩展.

\g 等价于分号

\G 是将显示的表格以纵向输出, 方便查看.

Slow Query Log 慢查询日志

慢查询日志用于记录执行时间超过指定阀值的SQL命令.

确认开启情况

mysql> show variables like 'slow_query_log%';
+---------------------+------------------------------------------------+
| Variable_name       | Value                                          |
+---------------------+------------------------------------------------+
| slow_query_log      | ON                                             |
| slow_query_log_file | C:\laragon\data\mysql\DESKTOP-C1GGBS1-slow.log |
+---------------------+------------------------------------------------+

mysql> show variables like 'long_query_time';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 1.000000 |
+-----------------+----------+

开启方式

配置文件

############### 慢查询日志 ################
# 打开慢查询日志
slow_query_log=1
# 日志记录位置
log_output=file
# 慢查询日志记录文件
slow_query_log_file=/var/run/mysqld/mysqld-slow.log
# 慢查询时间阀值
long_query_time=10

命令方式(mysqld实例重启后失效)

-- 必须全局开启慢查询日志记录
set global slow_query_log=1;

-- 设置慢查询时间阀值
set global long_query_time=1;

如果想要分析一些语句的执行, 则可以考虑将当前Session的慢查询时间阀值设为0

set long_query_time=0;

注意

当数据库被拖垮时(高负载), 任何简单的语句都可能执行超时, 此时的慢查询日志能提供的帮助就有限了.

慢查询日志分析工具

mysqldumpslow

在实际生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活,MySQL提供了日志分析工具mysqldumpslow

# 分析慢日志
mysqldumpslow -a -n 50 -s c /var/run/mysqld/mysqld-slow.log

# 参数说明
--verbose    版本
--debug      调试
--help       帮助
 
-v           版本
-d           调试模式
-s ORDER     排序方式, 默认是 'at'
             what to sort by (al, at, ar, c, l, r, t), 'at' is default
              al: average lock time 平均锁定时间
              ar: average rows sent 平均返回记录数
              at: average query time 平均查询时间
               c: count 访问计数
               l: lock time 锁定时间
               r: rows sent 返回记录
               t: query time 查询时间
-r           反转顺序,默认文件倒序拍。reverse the sort order (largest last instead of first)
-t NUM       显示前N条
-a           不要将SQL中数字转换成N,字符串转换成S。don't abstract all numbers to N and strings to 'S'
-n NUM       abstract numbers with at least n digits within names
-g PATTERN   正则匹配;grep: only consider stmts that include this string
-h HOSTNAME  mysql机器名或者IP;hostname of db server for *-slow.log filename (can be wildcard),
             default is '*', i.e. match all
-i NAME      name of server instance (if using mysql.server startup script)
-l           总时间中不减去锁定时间;don't subtract lock time from total time

示例

得到返回记录集最多的10个SQL。
mysqldumpslow -s r -t 10 /database/mysql/mysql06_slow.log
 
得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 /database/mysql/mysql06_slow.log

得到按照时间排序的前10条里面含有左连接的查询语句。
mysqldumpslow -s t -t 10 -g “left join” /database/mysql/mysql06_slow.log
 
另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现刷屏的情况。
mysqldumpslow -s r -t 20 /mysqldata/mysql/mysql06-slow.log | more

SQL Explain

对于慢查询日志中执行慢的语句分析其 SQL语句的执行计划

EXPLAIN 可以帮助了解:

  • 数据表的读取顺序
  • SELECT子句的类型
  • 数据表的访问类型
  • 可使用的索引 possible_keys
  • 实际使用的索引 key
  • 使用的索引长度 ken_len
  • 上一个表的连接匹配条件
  • 被优化器查询的行的数量
  • 额外的信息(如使用使用外部排序, 是否使用临时表)

举例

1559124811057.png

EXPLAIN 结果列分析

<u>字段 id</u>

SQL 执行顺序是根据

  • id从大到小执行
  • id相同时按照顺序从上往下执行.

<u>字段 select_type(查询类型)</u>

查询类型 说明
SIMPLE 简单查询
不包含UNION查询或子查询
PRIMARY 最外层查询
查询中若 包含任何复杂的子部分,最外层查询则被标记为PRIMARY
SUBQUERY 子查询
在 SELECT 或 WHERE 中包含了子查询
DEPENDENT SUBQUERY !!! 子查询, 但依赖于外层查询的结果
注意确认, 避免大表驱动小表
DERIVED 子查询
在 FROM 列表中包含的子查询被标记为 DERIVED(衍生)
UNION 联合
UNION 若第二个SELECT出现在UNION之后,则被标记为UNION
UNION RESULT 使用联合的结果
从UNION表获取结果的SELECT
关于UNION, 网上有写以下这段, 但我个人不理解

UNION 若第二个SELECT出现在UNION之后,则被标记为UNION:若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED


<u>字段 table(数据表)</u>

访问的数据表


<u>字段 partitions(分区)</u>

匹配的分区


<u>字段 type(访问方式)</u>

查询时的访问方式, 性能:all < index < range < index_merge < ref < eq_ref < system/const

一般来说至少需要保证访问方式是 range, 最好是 ref 级别.

访问方式 说明
ALL 全表扫描,对于数据表从头到尾找一遍
select * from tb1;
特别的:如果有limit限制,则找到之后就不在继续向下扫描
select * from tb1 where email = 'seven@live.com'
select * from tb1 where email = 'seven@live.com' limit 1;
虽然上述两个语句都会进行全表扫描,第二句使用了limit,则找到一个后就不再继续扫描。
INDEX 全索引扫描,对索引从头到尾找一遍
因为非主键索引树比较小, 所以会比 ALL 更快
RANGE 对索引列进行范围查找
通常是在索引树上快速定位到某一索引项, 再向左/右遍历.
INDEX_MERGE 合并索引,使用多个单列索引搜索, 最后结果取交集或并集
比如使用了UNION 且单独用到了两个索引.
REF 使用索引快速定位(根据索引查找一个或多个值), 该索引是 普通索引唯一索引的部分前缀
EQ_REF 使用主键索引或唯一索引快速定位
通常出现在多表的join查询, 连接时使用primary key 或 unique 索引(都只能匹配到一行记录)
CONST 通过主键或唯一索引精确查找到一行
常量
表最多有一个匹配行(主键或唯一索引),因为仅有一行,在这行的列值可被优化器剩余部分认为是常数,const表很快,因为它们只读取一次
SYSTEM 系统
表仅有一行, 这是const联接类型的一个特例, 可以忽略这种情况。

Q. ALL 和 INDEX 的区别

A. 两个都是全索引扫描, 不同的是 ALL 是对主键索引扫描, INDEX 是对非主键索引扫描.

这里要理解, 所谓的全表扫描指的是对主键索引扫描.

Q. EQ_REFCONST 的区别

A. 相同点都是使用主键/唯一索引精确查找到行记录. 不同点在于:

  • CONST 查询条件通常是 索引列 = 具体常量值
  • EQ_REF 通常是在多表关联查询时作为连接条件使用.

<u>字段 possible_keys(候选索引)</u>


<u>字段 key(实际使用的索引)</u>

如果是合并索引(INDEX_MERGE), 则此处可能存在超过1个的key


<u>字段 key_len(使用索引的实际长度)</u>

该字段可以评估组合索引是否完全被使用或仅仅是最左前缀被用到.

该字段显示的值为索引字段的最大可能长度, 并非实际使用长度.(即 key_len 是根据表定义计算, 而非表内检索)

计算规则

字段类型 计算方式
字符串 char(n) n字节长度
varchar(n) 若是utf8编码, 则是 n3 + 2 字节
如果是 utf8mb4 编码, 则是 4
n + 2 字节.
数值 tinyint 1字节
smallint 2字节
mediumint 3字节
int 4字节
bigint 8字节
时间 date 3字节
timestamp 4字节
datetime 8字节

如果对应索引字段允许为 null, 则还要额外消耗1个字节来存储 NULL.


<u>字段 ref</u>

表示索引的查找条件, 可能是常量(const) 或 联合查询中另一张表的某个字段.


<u>字段 row(扫描行数)</u>

估算的需要扫描的行数, 注意是估算的.

在某些情况下若索引统计信息偏差较大, 则此处的预估扫描行数也会过大, 导致影响查询计划的选择.

可以使用 SHOW INDEX FROM 表名 来查看索引统计信息

可以使用 ANYLYSIS TABLE 表名来重新统计索引信息.


<u>字段 filtered</u>


<u>字段 Extra(额外信息)</u>

该列包含MySQL解决查询的详细信息

说明
Using filesort mysql无法依靠索引直接获取有序记录, 而是对结果进行额外排序.
mysql有两种文件排序算法,这两种排序方式都可以在内存或者磁盘上完成
explain不会告诉你mysql将使用哪一种文件排序
也不会告诉你排序会在内存里还是磁盘上完成。
Using index 使用覆盖索引,以避免访问表。不要把覆盖索引和index访问类型弄混了。
Using index condition 索引下推优化, 5.6新增特性
Using temporary 意味着mysql在对查询结果排序时会使用一个临时表
Using where 使用了 where 过滤
这意味着mysql服务器将在存储引擎检索行后再进行过滤
许多where条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验
因此不是所有带where子句的查询都会显示“Using where”。
有时“Using where”的出现就是一个暗示:查询可受益于不同的索引。
Range checked for each record(index map: N) 这个意味着没有好用的索引,新的索引将在联接的每一行上重新估算,N是显示在possible_keys列中索引的位图,并且是冗余的。
using join buffer 在表联结时, 使用了连接缓存

Profiling

写在最前: SHOW PROFILE 命令将被弃用, 注意, 仅仅是 SHOW PROFILE命令弃用.

替代方案是从 information_schema 中的profiling数据表进行查看, 可参数链接:

SHOW PROFILE 命令方式(旧)

查看语句执行的时间在各个步骤的开销

show profile 分析SQL性能工具(检测数据存在于临时表中)

  • 在会话级别开启profile SET profiling=1;
  • 发送sql
  • 查看profile的资源开销结果

    • show profiles 查看所有的分析结果(会有一个数量上限)
    • show profile 查看最后一条执行语句的分析结果
    • show profile for query <id> 查看指定执行语句的详细分析结果
    • show profile cpu, block io for query <id> 查看详细信息, 且包含 cpu, block.io 执行时间
  • 关闭profile

这一部分更详细的可以参考如:

information_schema .profiling

待用到时补充.

阅读 646

尤嘉兴ing
个人专栏

PHPer@厦门

195 声望
9 粉丝
0 条评论

PHPer@厦门

195 声望
9 粉丝
宣传栏