zoukankan      html  css  js  c++  java
  • MySQL基础


    人在江湖走,哪有不挨刀。作为后台的码农,时不时会被高冷DBA大佬甩一堆慢SQL过来,并贴上执行计划(query Execution plan ),让你去立刻马上去做优化。如果连基本的执行计划都不会看,那只能活该被DBA大佬白眼一番了。

    mysql> explain select age from user where age > 40;
    +----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+----------+--------------------------+
    | id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows  | filtered | Extra                    |
    +----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+----------+--------------------------+
    |  1 | SIMPLE      | user  | NULL       | range | idx_age       | idx_age | 5       | NULL | 49982 |   100.00 | Using where; Using index |
    +----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+----------+--------------------------+
    
    

    通过执行计划,我们可以分析:

    • SQL的执行顺序
    • 数据读取操作的操作类型
    • 哪些索引可以使用
    • 哪些索引被实际使用
    • 表之间的引用
    • 每张表有多少行被优化器查询

    上测试数据:创建测试数据。 (ps:本文的测试基于mysql 8.0)

    # mysql 8.0
    CREATE TABLE `user` (
      `id` int NOT NULL AUTO_INCREMENT,
      `name` varchar(30) DEFAULT NULL,
      `age` int DEFAULT NULL,
      `create_time` datetime DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `idx_age` (`age`)
    ) ;
    

    最正宗的味道,最原始的说明,还是官方的:8.8.2 EXPLAIN Output Format

    字段说明

    id

    SELECT标识符。这是查询中SELECT的顺序号。SELECT 执行顺序规则:

    1. id值越大,优先级越高,越先执行。
    2. id如果相同,可以认为是一组,从上往下顺序执行;
    3. 如果该行引用其他行的并集结果,则该值可以为NULL。
    mysql> explain select * from user where id in ((select id from user where age =100) union (select id from user where age = 99));
    +----+--------------------+------------+------------+--------+-----------------+---------+---------+------+-------+----------+-----------------+
    | id | select_type        | table      | partitions | type   | possible_keys   | key     | key_len | ref  | rows  | filtered | Extra           |
    +----+--------------------+------------+------------+--------+-----------------+---------+---------+------+-------+----------+-----------------+
    |  1 | PRIMARY            | user       | NULL       | ALL    | NULL            | NULL    | NULL    | NULL | 99964 |   100.00 | Using where     |
    |  2 | DEPENDENT SUBQUERY | user       | NULL       | eq_ref | PRIMARY,idx_age | PRIMARY | 4       | func |     1 |     5.00 | Using where     |
    |  3 | DEPENDENT UNION    | user       | NULL       | eq_ref | PRIMARY,idx_age | PRIMARY | 4       | func |     1 |     5.00 | Using where     |
    | NULL | UNION RESULT       | <union2,3> | NULL       | ALL    | NULL            | NULL    | NULL    | NULL |  NULL |     NULL | Using temporary |
    +----+--------------------+------------+------------+--------+-----------------+---------+---------+------+-------+----------+-----------------+
    

    select_type

    SELECT的类型,可以是下表中显示的任何类型。

    类型 说明
    SIMPLE 简单SELECT,不使用union或子查询
    PRIMARY 查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY
    UNION UNION查询中的第二个或后面的SELECT语句
    DEPENDENT UNION UNION中的第二个或后面的SELECT语句,取决于外面的查询
    UNION RESULT UNION的结果
    SUBQUERY 子查询中的第一个SELECT
    DEPENDENT SUBQUERY 子查询中的第一个SELECT,取决于外面的查询
    DERIVED 派生表的SELECT, FROM子句的子查询
    DEPENDENT DERIVED 派生表的SELECT, 取决于外面的查询
    MATERIALIZED Materialized subquery
    UNCACHEABLE SUBQUERY 一个子查询的结果不能被缓存,必须重新评估外链接的第一行
    UNCACHEABLE UNION UNION属于UNCACHEABLE SUBQUERY的第二个或后面的查询

    table

    表示查询涉及的表或衍生表(Derived Table)。可以是以下值:

    • <unionM,N>: The row refers to the union of the rows with id values of M and N.
    • < derivedN>: The row refers to the derived table result for the row with an id value of N. A derived table may result, for example, from a subquery in the FROM clause.
    • < subqueryN> : The row refers to the result of a materialized subquery for the row with an id value of N

    image-20201106101818243

    partitions

    The partitions from which records would be matched by the query. The value is NULL for nonpartitioned tables. See Section 23.3.5, “Obtaining Information About Partitions”.

    当前查询匹配记录的分区。对于未分区的表,返回null。

    type

    EXPLAIN Join Types:官方说明

    同一个SQL语句,为啥性能差异咋就这么大呢?:有例子讲解很清晰,值得看。

    连接类型。有如下几种取值,性能从好到坏排序 如下:

    • system:该表只有一行(相当于系统表),system是const类型的特例

    • const:针对主键或唯一索引的等值查询扫描, 最多只返回一行数据. const 查询速度非常快, 因为它仅仅读取一次即可。

    • eq_ref:当使用了索引的全部组成部分,并且索引是PRIMARY KEY或UNIQUE NOT NULL 才会使用该类型,性能仅次于system及const。

    • ref:当满足索引的最左前缀规则,或者索引不是主键也不是唯一索引时才会发生。如果使用的索引只会匹配到少量的行,性能也是不错的。

    • fulltext:全文索引

    • ref_or_null:该类型类似于ref,但是MySQL会额外搜索哪些行包含了NULL。这种类型常见于解析子查询

    • index_merge:此类型表示使用了索引合并优化,表示一个查询里面用到了多个索引

    • unique_subquery:该类型和eq_ref类似,但是使用了IN查询,且子查询是主键或者唯一索引。

    • index_subquery:和unique_subquery类似,只是子查询使用的是非唯一索引

    • range:范围扫描,表示检索了指定范围的行,主要用于有限制的索引扫描。比较常见的范围扫描是带有BETWEEN子句或WHERE子句里有>、>=、<、<=、IS NULL、<=>、BETWEEN、LIKE、IN()等操作符。(如果范围太大,会可能导致不走索引)

    • index:全索引扫描,和ALL类似,只不过index是全盘扫描了索引的数据。当查询仅使用索引中的一部分列时,可使用此类型。有两种场景会触发:

      • 如果索引是查询的覆盖索引,并且索引查询的数据就可以满足查询中所需的所有数据,则只扫描索引树。此时,explain的Extra 列的结果是Using index。index通常比ALL快,因为索引的大小通常小于表数据。
      • 按索引的顺序来查找数据行,执行了全表扫描。此时,explain的Extra列的结果不会出现Uses index。
    • ALL:全表扫描,性能最差。

    possible_keys

    指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用

    Key

    key列显示MySQL实际决定使用的键(索引)

    如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

    key_len

    The key_len column indicates the length of the key that MySQL decided to use. The value of key_len enables you to determine how many parts of a multiple-part key MySQL actually uses. If the key column says NULL, the key_len column also says NULL.

    Mysql explain key_len作用

    索引key长度。字段类型都有各自的长度,可以根据字段类型计算出key_len。如果是联合索引,根据联合索引的最左匹配原则,通过key_len知道,索引实际用了哪几个字段的。

    ref

    表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

    rows

    表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数

    filtered

    表示符合查询条件的数据百分比,最大100。用rows × filtered可获得和下一张表连接的行数。例如rows = 1000,filtered = 50%,

    Extra

    Extra Information

    如何利用工具,迅猛定位低效SQL? | 1分钟系列

    该列包含MySQL解决查询的详细信息,一般结合 type 字段综合分析。Extra 详细字段说看官网的: Extra Information,类型很多,这里说下常见的。

    • Using where:表示mysql 服务器将在存储引擎检索行后再进行过滤

      mysql> explain select * from user where name in ('11qqq');
      +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
      | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra       |
      +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
      |  1 | SIMPLE      | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 99964 |    10.00 | Using where |
      +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
      1 row in set, 1 warning (0.00 sec)
      
      mysql> explain select * from user where name in ('11qqq') and id < 100;
      +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
      | id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
      +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
      |  1 | SIMPLE      | user  | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |   99 |    10.00 | Using where |
      +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
      1 row in set, 1 warning (0.00 sec)
      
      

      上面两个sql都使用了【Using where 】,但是上面那个sql的 type = ALL,rows = 99964,说明走了全表扫描,返回了99964行数据,在99964行数据中通过【Using where 】过滤出结果。这个sql 性能较差。下面的sql,type = range,key=PRIMARY,rows = 99,说明走了主键索引和范围扫描,返回了99行数据,在99行数据中通过【Using where 】过滤出结果。

    • Using index:SQL所需要返回的所有列数据均在一棵索引树上,而无需访问实际的行记录(回表)。

      mysql> explain select age from user where age = 55 ;
      +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
      | id | select_type | table | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
      +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
      |  1 | SIMPLE      | user  | NULL       | ref  | idx_age       | idx_age | 5       | const |    973 |   100.00 | Using index |
      +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
      1 row in set, 1 warning (0.00 sec)
      
      mysql> explain select * from user where age = 55 ;
      +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
      | id | select_type | table | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
      +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
      |  1 | SIMPLE      | user  | NULL       | ref  | idx_age       | idx_age | 5       | const |    973 |   100.00 | NULL  |
      +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
      1 row in set, 1 warning (0.00 sec)
      

      上面两条sql 查询条件一样,都走了idx_age索引,返回行数rows=973,但是上面的sql的Extra=【Using index】,下面的sql是【NULL】。造成这个原因的是select字段不一样。上面的sql 只是select age,age是idx_age字段,数据均在idx_age索引树上(索引覆盖),不需要回表查询。但是下面的sql 是select *,也中了idx_age索引,但不是所有的列数据都在索引树上,还需要访问实际的行记录。。如何避免回表查询?什么是索引覆盖?

    • Using index condition:命中了索引,但不是所有的列数据都在索引树上,还需要访问实际的行记录(回表)。

      mysql> explain select * from user where age = 55 and id <100;
      +----+-------------+-------+------------+-------+-----------------+---------+---------+------+------+----------+-----------------------+
      | id | select_type | table | partitions | type  | possible_keys   | key     | key_len | ref  | rows | filtered | Extra                 |
      +----+-------------+-------+------------+-------+-----------------+---------+---------+------+------+----------+-----------------------+
      |  1 | SIMPLE      | user  | NULL       | range | PRIMARY,idx_age | idx_age | 9       | NULL |    2 |   100.00 | Using index condition |
      +----+-------------+-------+------------+-------+-----------------+---------+---------+------+------+----------+-----------------------+
      
      
    • Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询

      mysql> explain select name from user group by name;
      +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
      | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra           |
      +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
      |  1 | SIMPLE      | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 99964 |   100.00 | Using temporary |
      +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-----------------+
      

      需要建立临时表(temporary table)来暂存中间结果。这类SQL语句性能较低,往往也需要进行优化。

    • Using filesort:表示 MySQL 不能通过索引顺序达到排序效果,需额外的排序操作。 一般有 Using filesort, 都建议优化去掉, 因为这样的查询 CPU 资源消耗大.

      mysql> explain select * from user where age = 55 order by name;
      +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+----------------+
      | id | select_type | table | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra          |
      +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+----------------+
      |  1 | SIMPLE      | user  | NULL       | ref  | idx_age       | idx_age | 5       | const |  973 |   100.00 | Using filesort |
      +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+----------------+
      
      

      16 | “order by”是怎么工作的?

      “Using filesort”表示的就是需要排序,MySQL会给每个线程分配一块内存用于排序,称为sort_buffer。

      这个过程,可能在内存中完成,也可能需要使用外部排序,这取决于排序所需的内存和参数sort_buffer_size。sort_buffer_size,就是MySQL为排序开辟的内存(sort_buffer)的大小。如果要排序的数据量小于sort_buffer_size,排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序。

    • Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。

    • Impossible where:这个值强调了where语句会导致没有符合条件的行。

    • Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行

    资料

    如何利用工具,迅猛定位低效SQL? | 1分钟系列

    同一个SQL语句,为啥性能差异咋就这么大呢?

  • 相关阅读:
    C语言编译多文件
    vs(visual studio 2019)恢复默认设置
    everything 有文件搜不到
    potplayer显示右侧插入列表消息
    ubuntu 关机、重启命令
    post&get请求总结
    C# string格式的日期时间字符串转为DateTime类型
    css position: absolute、relative详解
    在C#用HttpWebRequest中发送GET/HTTP/HTTPS请求
    ASP.NET获取客户端及服务器的信息
  • 原文地址:https://www.cnblogs.com/zhaooo/p/13973552.html
Copyright © 2011-2022 走看看