MySQL杂记

  created  by  鱼鱼 {{tag}}
创建于 2019年01月26日 18:53:12 最后修改于 2019年02月25日 13:52:34
评论区
评论
{{comment.creator}}
{{comment.createTime}} {{comment.index}}楼
评论

MySQL杂记

MySQL杂记

常用SQL

查询死锁

SELECT concat('kill',id,';')  FROM `information_schema`.`PROCESSLIST`  WHERE state='statistics';

使用Explain分析一个SQL

    Explain 可以分析一个SELECT语句的性能,只要加在查询语句之前即可,会输出关于查询语句的分析,分析这个例子:

select_type

select_type包含以下几类:

type

type是衡量性能的一个重要依据,包含以下几类(按基本的效率从高到低排):

    system, 表中只有一条数据. 这个类型是特殊的 const 类型

    const, 针对主键或唯一索引的等值查询扫描, 最多只返回一行数据. const 查询速度非常快,例如下面的这个查询, 它使用了主键索引, 因此 type 就是 const 类型的

EXPLAIN SELECT * FROM user WHERE id = 0

    eq_ref, 此类型通常出现在多表的 join 查询, 表示对于前表的每一个结果, 都只能匹配到后表的一行结果. 并且查询的比较操作通常是 =, 查询效率较高,例如下面的语句对于blog表使用的是eq_ref,因为此表与blog_tag是一对多的关系。

EXPLAIN SELECT * FROM blog JOIN blog_tag ON blog.id = blog_tag.blog_id WHERE tag = 'java' ORDER BY timeline DESC

    ref, 此类型通常出现在多表的 join 查询, 针对于非唯一或非主键索引, 或者是使用了 最左前缀 规则索引的查询,注意与上面那个区分开。同样在上面的例子中,若blog_tag的tag字段使用了普通索引,则type为ref。

    range, 表示使用索引范围查询, 通过索引字段范围获取表中部分数据记录. 这个类型通常出现在 =, <>, >, >=, <, <=, IS NULL,<=>,BETWEEN, IN() 等操作中,当 type 是 range 时, 那么 EXPLAIN 输出的 ref 字段为 NULL, 并且 key_len 字段是此次查询中使用到的索引的最长的那个,这个很容易理解,例如下面的例子(tag建立了索引),但是当待查询数据量大的时候,索引会失效,此时type为ALL,详见issue

EXPLAIN SELECT * FROM blog_tag WHERE tag LIKE "mysql%"

    index,示全索引扫描(full index scan), 和 ALL 类型类似, 只不过 ALL 类型是全表扫描, 而 index 类型则仅仅扫描所有的索引, 而不扫描数据,同样适用于联合索引。例如下面的例子(建立了tag,blog_id联合索引,不区分顺序)

EXPLAIN SELECT tag,blog_id FROM blog_tag

    ALL,表示全表扫描, 这个类型的查询是性能最差的查询之一. 通常来说, 我们的查询不应该出现 ALL 类型的查询, 因为这样的查询在数据量大的情况下, 对数据库的性能是巨大的灾难. 如一个查询是 ALL 类型查询, 那么一般来说可以对相应的字段添加索引来避免。

    查询效率的高低并不是绝对的,正如有时MySQL会主动抛弃索引去使用全表查询或是有时全表扫面比索引扫描效率更高,需结合实际情况建立对应的索引。

extra

    extra会显示一些额外的信息,比较有用的有:

    Using filesort, 表示使用了额外的排序,多数情况下最好使用索引优化,在下面的例子中,timeline所示不加索引,便会产生这一提示(优化掉需要返回字段不含非索引字段或是含有LIMIT限制),例如(timeline添加索引):

EXPLAIN SELECT timeline,id FROM blog ORDER BY blog.timeline

    Using temporary,查询中使用了临时表,建议优化。


语句执行顺序

    MySQL的语句一共分为11步,如下图所标注的那样,最先执行的总是FROM操作,最后执行的是LIMIT操作。其中每一个操作都会产生一张虚拟的表,这个虚拟的表作为一个处理的输入,只是这些虚拟的表对用户来说是透明的,但是只有最后一个虚拟的表才会被作为结果返回。如果没有在语句中指定某一个子句,那么将会跳过相应的步骤。

    下面我们来具体分析一下查询处理的每一个阶段 :
1.FORM: 对FROM的左边的表和右边的表计算笛卡尔积。产生虚表VT1 
2.ON: 对虚表VT1进行ON筛选,只有那些符合的行才会被记录在虚表VT2中。 
3.JOIN: 如果指定了OUTER JOIN(比如left join、 right join),那么保留表中未匹配的行就会作为外部行添加到虚拟表VT2中,产生虚拟表VT3, rug from子句中包含两个以上的表的话,那么就会对上一个join连接产生的结果VT3和下一个表重复执行步骤1~3这三个步骤,一直到处理完所有的表为止。 
4.WHERE: 对虚拟表VT3进行WHERE条件过滤。只有符合的记录才会被插入到虚拟表VT4中。 
5.GROUP BY: 根据group by子句中的列,对VT4中的记录进行分组操作,产生VT5. 
6.CUBE | ROLLUP: 对表VT5进行cube或者rollup操作,产生表VT6. 
7.HAVING: 对虚拟表VT6应用having过滤,只有符合的记录才会被 插入到虚拟表VT7中。 
8.SELECT: 执行select操作,选择指定的列,插入到虚拟表VT8中。 
9.DISTINCT: 对VT8中的记录进行去重。产生虚拟表VT9. 
10.ORDER BY: 将虚拟表VT9中的记录按照进行排序操作,产生虚拟表VT10. 
11.LIMIT:取出指定行的记录,产生虚拟表VT11, 并将结果返回。

MySQL的日志

    MySQL也有自己的操作日志和对应的日志级别,一般默认开启error日志,执行如下语句可以看到有关MySQL日志的全局配置项:

show variables like '%log%'

    其日志类型主要分为:查询日志、错误日志、慢查询日志、二进制日志,分别对应general_log、log_error、slow_query_log、log_bin,执行如下语句开启指定的日志(全局的),值可以指定为ON|OFF、1|0、也可以指定为路径,不建议开全局查询日志,记录所有的SQL会损耗性能

set global slow_query_log=1

    我们并不能通过这种方式设定开启二进制日志。


参考链接:mysql join 语句中 on 和 where 的问题MySQL性能优化神器Explain使用分析



2019-02-25鱼鱼

{{commentTitle}}

评论   ctrl+Enter 发送评论