首页 > 数据库 >MySQL Execution Plan -- IN条件与ORDER BY组合优化

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

时间:2023-08-07 14:22:50浏览次数:38  
标签:status end -- time job Plan MySQL NULL id

测试环境

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操作改为等值操作。

标签:status,end,--,time,job,Plan,MySQL,NULL,id
From: https://www.cnblogs.com/gaogao67/p/17611316.html

相关文章

  • 爬虫不仅仅selenium自动化,还有这些。。。
    1.DrissionPage这款工具既能控制浏览器,也能收发数据包,甚至能把两者合而为一,简单来说:集合了WEB浏览器自动化的便利性和requests的高效率优点。采用全自研的内核,对比selenium,有以下优点:无webdriver特征,不会被网站识别,无需为不同版本的浏览器下载不同的驱动。运行速度更快,......
  • ping www.baidu.com 未知的名称或服务
    1、先确定登录的身份是否是root用户,如果不是,最好切换为root身份2、输入vim/etc/sysconfig/network-scripts/ifcfg-ens33,然后会看到下图3、将BOOTPROTO="dhcp" 改成 BOOTPROTO="static"4、将ONBOOT="on" 改成 ONBOOT="yes"5、在后面加上IPADDR=192.168.75.123//此次......
  • 追溯文档交付
    硅片追溯项目说明项目由来生产过程产生批量不良后,追溯率低,导致产品无法快速追溯到机台,严重影响工艺、设备排产产品不良问题点。项目原理在生产过程中,机台读头会对每个花篮芯片进行扫描,并读取其虚拟RFID(芯片ID)。运用无线电技术对该花篮里的所有硅片赋上九位数标准虚拟waferID......
  • txt
    素数又称质数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数(规定1既不是质数也不是合数)。长期以来,素数被认为在纯数学以外的地方只有极少数的应用。到了1970年代,发明公共密钥加密这个概念之后,情况改变了,素数变成了RSA加密算法等一阶算法之基础......
  • 复习笔记|第九、十章 Linux文件系统《操作系统原理教程》
    参考教材:《操作系统原理教程(第4版)》刘美华翟岩龙著大纲问题回答(精简版)1.Ext2文件卷的布局?各部分的作用是什么?Ext2文件卷的布局◼Ext2把磁盘块分为组,每组包含存放在相邻磁道的数据块和索引节点。块组的大小相等并顺序安排。◼Ext2用“块组描述符”来描述这些块组本身的结......
  • [转]virtualbox centos无法上网问题
    原文地址https://www.cnblogs.com/Joke-Jay/p/8215295.htmlvirualbox里面设置nat模式(我这儿配置virtual时, 第一张网卡是NAT模式,还加了一张网卡, 选择了host-only模式, 估计不是必须)修改centos里面文件:vi/etc/sysconfig/network-scripts/ifcfg-enp0s3 文件ONBOOT=n......
  • 平衡树从入门到入土【待更新】
    O.写在前面本文的题目叫「平衡树从入门到入土」。因为我想让每一个学过树形结构的同学,都能够学会这种十分重要的数据结构。不论是上课睡觉没有听还是准备提前预习的同学,都能从这篇文章受益。平衡树的核心思想在于如何保证「平衡」——显然,也是最难理解的。大部分平衡树是通过「......
  • 复习笔记|第十四章 Windows操作系统模型《操作系统原理教程》
    参考教材:《操作系统原理教程(第4版)》刘美华翟岩龙著大纲问题回答(精简版)1.Windows采用什么样的体系结构?从图中看出,系统划分为两种状态,核心态和用户态。粗线上方代表用户态进程,下方是核心态的操作系统服务。用户态的进程只能运行在受保护的地址空间。因此,四种类型的用户态进......
  • 浅谈伯努利数
    O.前言在翻洛谷日报的时候居然没看到伯努利数的讲解,于是有了这篇文章。想要看懂本文,你需要提前知道以下内容:二项式系数;幂级数;艾弗森括号;下降幂;第二类斯特林数。部分内容在文中给了对应的公式,故不放在前言内。I.伯努利数的定义:万恶之源\(m\)次幂的求和公式1.伯努......
  • 复习笔记|第十五章 Windows进程和线程管理《操作系统原理教程》
    参考教材:《操作系统原理教程(第4版)》刘美华翟岩龙著大纲问题回答(精简版)1.管理进程和线程的数据结构:执行体进程块EPROCESS、执行体线程块ETHREAD、内核进程块KPROCESS、内核线程块KTHREAD。structEPROCESS{P285KPROCESSPCB;内核进程块ObjectTable;进程的句......