MySQL Execution Plan -- IN条件与ORDER BY组合优化

这篇具有很好参考价值的文章主要介绍了MySQL Execution Plan -- IN条件与ORDER BY组合优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

测试环境

MySQL版本: 5.7.27-30-log Percona Server (GPL), wsrep_31.39

涉及表结构:

CREATE TABLE `scout_job` (
  `task_id` varchar(22) NOT NULL DEFAULT '' COMMENT '任务id',
  `job_id` int(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'jobId',
  `env_id` varchar(10) NOT NULL DEFAULT '' COMMENT '环境id',
  `status` int(2) NOT NULL DEFAULT '0' COMMENT '0-初始化任务 1-任务执行中 2-执行成功 3-执行失败 -1:任务被清理',
  `start_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '开始时间',
  `end_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '结束时间',
  PRIMARY KEY (`job_id`) USING BTREE,
  KEY `idx_envid` (`env_id`) USING BTREE,
  KEY `idx_id_status_endTime` (`env_id`,`status`,`end_time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3416771 DEFAULT CHARSET=utf8mb4 COMMENT='任务记录表'

涉及SQL:

SELECT job_id FROM scout_job WHERE env_id = '393684' and status in (2,3) ORDER by end_time desc limit 2;

在系统没有任何压力情况下,该SQL执行时间超过200ms。

问题分析

查看SQL对应执行计划:

mysql> DESC SELECT job_id FROM scout_job WHERE env_id = '393684' and status in (2,3) ORDER by end_time desc limit 2 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: scout_job
   partitions: NULL
         type: ref
possible_keys: idx_envid,idx_id_status_endTime
          key: idx_envid
      key_len: 42
          ref: const
         rows: 152938
     filtered: 20.00
        Extra: Using index condition; Using where; Using filesort
1 row in set, 1 warning (0.00 sec)

查看满足WHERE条件数据:

mysql> SELECT COUNT(1) FROM scout_job WHERE env_id = '393684' and status in (2,3);
+----------+
| COUNT(1) |
+----------+
|    94828 |
+----------+
1 row in set (0.15 sec)

通过profiling查看耗时情况:

mysql> SHOW PROFILE CPU,BLOCK IO,SWAPS FOR QUERY 1;
+--------------------------+----------+----------+------------+--------------+---------------+-------+
| Status                   | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out | Swaps |
+--------------------------+----------+----------+------------+--------------+---------------+-------+
| starting                 | 0.000065 |     NULL |       NULL |         NULL |          NULL |  NULL |
| checking permissions     | 0.000005 |     NULL |       NULL |         NULL |          NULL |  NULL |
| Opening tables           | 0.000014 |     NULL |       NULL |         NULL |          NULL |  NULL |
| init                     | 0.000031 |     NULL |       NULL |         NULL |          NULL |  NULL |
| System lock              | 0.000008 |     NULL |       NULL |         NULL |          NULL |  NULL |
| optimizing               | 0.000011 |     NULL |       NULL |         NULL |          NULL |  NULL |
| statistics               | 0.000156 |     NULL |       NULL |         NULL |          NULL |  NULL |
| preparing                | 0.000019 |     NULL |       NULL |         NULL |          NULL |  NULL |
| Sorting result           | 0.000004 |     NULL |       NULL |         NULL |          NULL |  NULL |
| executing                | 0.000002 |     NULL |       NULL |         NULL |          NULL |  NULL |
| Sending data             | 0.000005 |     NULL |       NULL |         NULL |          NULL |  NULL |
| Creating sort index      | 0.208818 |     NULL |       NULL |         NULL |          NULL |  NULL |
| innobase_commit_low (-1) | 0.000011 |     NULL |       NULL |         NULL |          NULL |  NULL |
| end                      | 0.000005 |     NULL |       NULL |         NULL |          NULL |  NULL |
| query end                | 0.000016 |     NULL |       NULL |         NULL |          NULL |  NULL |
| innobase_commit_low (-1) | 0.000008 |     NULL |       NULL |         NULL |          NULL |  NULL |
| closing tables           | 0.000011 |     NULL |       NULL |         NULL |          NULL |  NULL |
| freeing items            | 0.000033 |     NULL |       NULL |         NULL |          NULL |  NULL |
| cleaning up              | 0.000017 |     NULL |       NULL |         NULL |          NULL |  NULL |
+--------------------------+----------+----------+------------+--------------+---------------+-------+
19 rows in set, 1 warning (0.00 sec)

根据profiling结果可以发现99.9%的耗时在Creating sort index环节,查询条件中包含IN操作,MySQL需要对满足env_id = '393684' and status in (2,3)条件的结果集进行排序(ORDER by end_time desc)然后取前2行(limit 2),由于满足条件记录较多,所以排序操作消耗时间较长。

问题优化

由于表上存在索引idx_id_status_endTime (env_id,status,end_time) ,如果IN条件仅包含1个可选值,通过该索引经过WHERE条件过滤后的数据在end_time列上有序,即可避免排序操作,如:

mysql> DESC SELECT job_id FROM scout_job WHERE env_id = '393684' and status in (2) ORDER by end_time desc limit 2 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: scout_job
   partitions: NULL
         type: ref
possible_keys: idx_envid,idx_id_status_endTime
          key: idx_id_status_endTime
      key_len: 46
          ref: const,const
         rows: 34002
     filtered: 100.00
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

相比IN中包含多个值的执行计划,IN单个值的执行计划中的rows仍较大,但Extra列中Using filesort已被消除。

通过profiling查看耗时情况:

+--------------------------+----------+----------+------------+--------------+---------------+-------+
| Status                   | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out | Swaps |
+--------------------------+----------+----------+------------+--------------+---------------+-------+
| starting                 | 0.000066 |     NULL |       NULL |         NULL |          NULL |  NULL |
| checking permissions     | 0.000005 |     NULL |       NULL |         NULL |          NULL |  NULL |
| Opening tables           | 0.000013 |     NULL |       NULL |         NULL |          NULL |  NULL |
| init                     | 0.000028 |     NULL |       NULL |         NULL |          NULL |  NULL |
| System lock              | 0.000007 |     NULL |       NULL |         NULL |          NULL |  NULL |
| optimizing               | 0.000013 |     NULL |       NULL |         NULL |          NULL |  NULL |
| statistics               | 0.000126 |     NULL |       NULL |         NULL |          NULL |  NULL |
| preparing                | 0.000016 |     NULL |       NULL |         NULL |          NULL |  NULL |
| Sorting result           | 0.000003 |     NULL |       NULL |         NULL |          NULL |  NULL |
| executing                | 0.000002 |     NULL |       NULL |         NULL |          NULL |  NULL |
| Sending data             | 0.000039 |     NULL |       NULL |         NULL |          NULL |  NULL |
| innobase_commit_low (-1) | 0.000004 |     NULL |       NULL |         NULL |          NULL |  NULL |
| end                      | 0.000002 |     NULL |       NULL |         NULL |          NULL |  NULL |
| query end                | 0.000009 |     NULL |       NULL |         NULL |          NULL |  NULL |
| innobase_commit_low (-1) | 0.000005 |     NULL |       NULL |         NULL |          NULL |  NULL |
| closing tables           | 0.000005 |     NULL |       NULL |         NULL |          NULL |  NULL |
| freeing items            | 0.000022 |     NULL |       NULL |         NULL |          NULL |  NULL |
| cleaning up              | 0.000011 |     NULL |       NULL |         NULL |          NULL |  NULL |
+--------------------------+----------+----------+------------+--------------+---------------+-------+

耗时为208ms的Creating sort index 已被优化掉,查询从208ms优化到0.1毫秒。

对于IN包含多个值的情况,可以通过SQL改写来优化:

# 改写前SQL:
DESC SELECT job_id FROM scout_job WHERE env_id = '393684' and status in (2,3) ORDER by end_time desc limit 2 

# 改写后SQL:
SELECT job_id FROM (
SELECT * FROM (SELECT job_id, end_time FROM scout_job WHERE env_id = '393684' AND STATUS IN (2) ORDER BY end_time DESC LIMIT 2) AS T2
UNION 
SELECT * FROM (SELECT job_id, end_time FROM scout_job WHERE env_id = '393684' AND STATUS IN (3) ORDER BY end_time DESC LIMIT 2) AS T3
) AS T1 ORDER BY end_time DESC LIMIT 2

由于MySQL的UNION限制,对于含有ORDER BY的查询需要使用派生表的方式解决。

如果IN包含值较多,改写后的SQL会看起来比较"复杂",也可以考虑在应用程序端进行调整,将IN操作改为等值操作。文章来源地址https://www.toymoban.com/news/detail-630590.html

到了这里,关于MySQL Execution Plan -- IN条件与ORDER BY组合优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SQL优化(3):order by优化

    MySQL的排序,有两种方式: Using filesort : 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。 Using index : 通过有序索引顺序扫描直接返回有序数据,这种情况即为 using

    2024年02月01日
    浏览(48)
  • mysql中order by多个字段 order by字段可以为空吗

    在MySQL中,要使用“ORDER BY”语句来进行多字段排序,必须先将多个字段的名称按照顺序排列放在“ORDER BY”后面,然后按照每个字段单独的排序规则进行排序。 排序字段的顺序按照order by语句中的先后顺序进行, 先根据第一个排序字段排序 ,如果有相同的值,则根据第二个

    2024年02月03日
    浏览(43)
  • mysql GROUP BY 怎么 order by 排序

    在 MySQL 中使用 GROUP BY 子句时,如果需要对结果进行排序,可以使用 ORDER BY 子句来对分组后的结果进行排序。 ORDER BY 子句应该放在 GROUP BY 子句之后,使用逗号来分隔需要排序的列,并在排序列后指定排序顺序。例如: 在这个例子中,SELECT 语句选择了表 table1 中的列 column1 和

    2024年02月16日
    浏览(57)
  • mysql order by 索引问题综合分析

    一,文章1 Mysql-索引失效 order by优化_orderby索引失效_zyk1.的博客-CSDN博客 总结: 0,索引 与 查询条件 与 排序字段关系,Using filesort出现场景 1.联合索引,最左匹配原则,不仅查询条件需要遵循,排序也需要遵循,查询+排序组合也要遵循 2.extra 避免出现filesort,使用index排序

    2024年02月07日
    浏览(43)
  • SQL 查询优化指南:SELECT、SELECT DISTINCT、WHERE 和 ORDER BY 详解

    SQL的SELECT语句用于从数据库中选择数据。SELECT语句的基本语法如下: 其中, column1 , column2 ,等是您要从表中选择的字段名称,而 table_name 是您要选择数据的表的名称。 如果要选择表中的所有列,您可以使用 SELECT * 语法。 以下是一些示例: 从Customers表中选择 CustomerName 和 Ci

    2024年02月05日
    浏览(64)
  • MySQL实战解析底层---“order by“是怎么工作的

    目录 前言 全字段排序 rowid排序 全字段排序 VS rowid排序 前言 在开发应用的时候,一定会经常碰到需要根据指定的字段排序来显示结果的需求 以举例市民表为例,假设你要查询城市是“杭州”的所有人名字,并且按照姓名排序返回前1000个人的姓名、年龄 假设这个表的部分定

    2024年02月10日
    浏览(56)
  • MySQL使用SELECT 语句不加ORDER BY默认是如何排序的?

    大家好,我是阿飞云 怕什么真理无穷,进一步有近一步的欢喜 记录一个 MySQL 查询排序的问题,一个SQL语句没有加 order by ,那么查询出来的结果到底是按照什么规则排序的呢?查询了网上的一些资料,分享如下: •MyISAM 表 MySQL Select 默认排序是按照物理存储顺序显示的(不

    2024年02月10日
    浏览(49)
  • 【MySQL】union (all) 后 order by 子查询排序不生效问题解决方案

    2308. 按性别排列表格 表:Genders Column Name Type user_id int gender varchar user_id 是该表的主键(具有唯一值的列)。 gender 的值是 ‘female’,‘male’,‘other’ 之一。 该表中的每一行都包含用户的 ID 及其性别。 表格中 ‘female’,‘male’,‘other’ 数量相等。 编写一个解决方案以重新

    2024年01月17日
    浏览(58)
  • MySQL 数据库查询与数据操作:使用 ORDER BY 排序和 DELETE 删除记录

    使用 ORDER BY 语句按升序或降序对结果进行排序。 ORDER BY 默认按升序排序。要按降序排序结果,使用 DESC 。 示例按名称按字母顺序排序结果: ORDER BY DESC 使用 DESC 以降序排序结果。 示例按名称以字母逆序排序结果: 您可以使用\\\"DELETE FROM\\\"语句从现有表格中

    2024年02月05日
    浏览(80)
  • Mysql 中,为什么 WHERE 使用别名会报错,而 ORDER BY 不会报错?

       我们先对salary * 12 命名一个别名annual_sal  这段代码以annual_sal升序输出且正常执行没有报错。说明 order by 可以使用别名  我们再看看这个段代码 这段代码就报错了,报错说明是 Unknown column \\\'annual_sal\\\' in \\\'where clause\\\'。 由此可以得出where语句执行是找不到annual_sal. 要解决这个问

    2023年04月15日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包