问题描述

版本:MySQL 5.7.24

SQL语句:

SELECT wave_no,
SUM(IF(picking_qty IS NULL, 0, picking_qty)) AS PICKED_QTY,
SUM(IF(differ_qty IS NULL, 0, differ_qty)) AS PICKED_DIFFER_QTY,
SUM(IF(relocate_qty IS NULL, 0, relocate_qty)) AS PICKED_RELOCATE_QTY
FROM picking_locate_d
WHERE yn = 0
AND wave_no IN
(
'BC76361213164811',
'BC76361213164810',
...
'BC76361213158692'
)
AND org_No = '661'
AND distribute_No = '763'
AND warehouse_No = '612'
GROUP BY wave_no;

该SQL在慢日志中记录执行信息为:

# Query_time: 558.604238  Lock_time: 0.070967 Rows_sent:   Rows_examined: 

表picking_locate_d上索引如下:

表picking_locate_d上索引如下:
PRIMARY KEY (`id`,`warehouse_no`,`org_no`,`distribute_no`),
KEY `FK_locate_d_REFERENCE_task_m` (`task_page_no`) USING BTREE,
KEY `index_outbound_no` (`outbound_no`),
KEY `idx_wave_no` (`wave_no`),
KEY `idx_update_time` (`update_time`),
KEY `ix_OUTBOUND_NO` (`outbound_no`),
KEY `idx_occupy_uuid` (`occupy_uuid`),
KEY `idx_opt_status` (`opt_status`)

表picking_locate_d上数据量为:

show table status like 'picking_locate_d' \G
*************************** . row ***************************
Name: picking_locate_d
Engine: InnoDB
Version:
Row_format: Dynamic
Rows:
Avg_row_length:
Data_length:
Max_data_length:
Index_length:
Data_free:
Auto_increment:
Create_time: -- ::
Update_time: -- ::
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:

当IN查询内部的值数量小于等于14238时,查询时间0.31秒,执行计划为:

+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+-------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+-------+----------+------------------------------------+
| | SIMPLE | picking_locate_d | NULL | range | idx_wave_no | idx_wave_no | | NULL | | 0.01 | Using index condition; Using where |
+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+-------+----------+------------------------------------+

当IN查询内部的值数量大于等于14239时,查询时间超过558秒

+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+----------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+----------+----------+-------------+
| | SIMPLE | picking_locate_d | NULL | index | idx_wave_no | idx_wave_no | | NULL | | 0.01 | Using where |
+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+----------+----------+-------------+

通过对比执行计划发现,两者出现性能问题的主要原因在于:

1、  前者使用INDEX SEEK查找特定键值,预估影响行数为14238。

2、  后者使用INDEX SCAN扫描全索引数据,预估影响行数为41612750。

将IN查询内部的值调整为16000个,通过调整SQL语句进行测试:

1、  去除WHERE语句中yn = 0条件,查询使用INDEX SCAN,预估影响行数为41612750

2、  去除WHERE语句中org_No/distribute_No/warehouse_No三列其中任意一列或多列,查询使用INDEX SEEK, 预估影响行数为16000

3、  修改WHERE语句中org_No/distribute_No/warehouse_No三列其中任意一列或多列为函数操作,如将org_No = '661'修改为CONCAT(org_No,’’) = '661', 查询使用INDEX SEEK, 预估影响行数为16000。

4、  修改GROUP BY语句中wave_no 为CONCAT(wave_no,’’), 查询使用INDEX SEEK, 预估影响行数为16000。

5、  关闭MySQL ICP(index condition pushdown)特性,查询使用INDEX SCAN,预估影响行数为41612750

原因分析:

1、  由于主键索引为 (`id`,`warehouse_no`,`org_no`,`distribute_no`),而索引idx_wave_no定义为(`wave_no`),因此索引idx_wave_no实际包含列为:(`wave_no`,`id`,`warehouse_no`,`org_no`,`distribute_no`),因此推测调整WHERE语句中org_No/distribute_No/warehouse_No三列其中任意一列或多列会影响查询预估。

2、  由于修改GROUP BY语句,使得查询的GROUP BY部分无法通过索引来避免排序分钟,导致查询会先完成WHERE条件过滤后使用临时表和文件排序,GROUP BY语句不会影响WHERE语句的预估,使得查询优化器更偏向于使用INDEX SEEK。

优化建议:

建议将SQL语句调整为:

SELECT CONCAT(wave_no,'') AS wave_no,
SUM(IF(picking_qty IS NULL, , picking_qty)) AS PICKED_QTY,
SUM(IF(differ_qty IS NULL, , differ_qty)) AS PICKED_DIFFER_QTY,
SUM(IF(relocate_qty IS NULL, , relocate_qty)) AS PICKED_RELOCATE_QTY
FROM picking_locate_d
WHERE yn =
AND wave_no IN
(
'BC76361213164811',
'BC76361213164810',
...
'BC76361213158692'
)
AND org_No = ''
AND distribute_No = ''
AND warehouse_No = ''
GROUP BY CONCAT(wave_no,'');

调整后执行时间为0.47秒,执行计划为:

+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+-------+----------+---------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+-------+----------+---------------------------------------------------------------------+
| | SIMPLE | picking_locate_d | NULL | range | idx_wave_no | idx_wave_no | | NULL | | 0.10 | Using index condition; Using where; Using temporary; Using filesort |
+----+-------------+------------------+------------+-------+---------------+-------------+---------+------+-------+----------+---------------------------------------------------------------------+

调整后的执行计划中增加Using temporary和Using filesort,导致执行时间0.47秒略高于之前的0.35秒,但会使得查询使用INDEX SEEK概率更高,有利于系统稳定。

其他优化建议:

1、  建议控制IN查询中值数量,避免传入过多值导致查询预估异常。

2、  建议主键尽量使用单列索引,使用多列符合索引为主键会影响非聚集索引的索引长度和查询预估准确率。

MySQL Execution Plan--IN子查询包含超多值引发的查询异常的更多相关文章

  1. MySQL Execution Plan--IN子查询包含超多值引发的查询异常1

    ======================================================================= SQL语句: SELECT wave_no, SUM(I ...

  2. MySQL Execution Plan--NOT EXISTS子查询优化

    在很多业务场景中,会使用NOT EXISTS语句来确保返回数据不存在于特定集合,部分场景下NOT EXISTS语句性能较差,网上甚至存在谣言"NOT EXISTS无法走索引". 首 ...

  3. query_string查询支持全部的Apache Lucene查询语法 低频词划分依据 模糊查询 Disjunction Max

    3.3 基本查询3.3.1词条查询 词条查询是未经分析的,要跟索引文档中的词条完全匹配注意:在输入数据中,title字段含有Crime and Punishment,但我们使用小写开头的crime来搜 ...

  4. Mysql查询优化器之关于子查询的优化

    下面这些sql都含有子查询: mysql> select * from t1 where a in (select a from t2); mysql> select * from (se ...

  5. MySQL(八)子查询和分组查询

    一.子查询 1.子查询(subquery):嵌套在其他查询中的查询. 例如:select user_id from usertable where mobile_no in (select mobil ...

  6. MySQL之多表查询一 介绍 二 多表连接查询 三 符合条件连接查询 四 子查询 五 综合练习

    MySQL之多表查询 阅读目录 一 介绍 二 多表连接查询 三 符合条件连接查询 四 子查询 五 综合练习 一 介绍 本节主题 多表连接查询 复合条件连接查询 子查询 首先说一下,我们写项目一般都会建 ...

  7. 为什么MySQL不推荐使用子查询和join

    前言: 1.对于mysql,不推荐使用子查询和join是因为本身join的效率就是硬伤,一旦数据量很大效率就很难保证,强烈推荐分别根据索引单表取数据,然后在程序里面做join,merge数据. 2.子 ...

  8. MySQL中 如何查询表名中包含某字段的表 ,查询MySql数据库架构信息:数据库,表,表字段

    --查询tablename 数据库中 以"_copy" 结尾的表 select table_name from information_schema.tables where ta ...

  9. mysql update不支持子查询更新

    先看示例: SELECT uin,account,password,create_user_uin_tree FROM sys_user 结果: 表中的create_user_uin_tree标识该条 ...

随机推荐

  1. .NET--------枚举扩展方法(枚举转list,获取枚举描述)

    /// <summary> /// get enum description by name /// </summary> /// <typeparam name=&qu ...

  2. eclipse调试远程tomcat

    1.设置tomcat远程调试端口 catalina.sh export JAVA_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,susp ...

  3. openvas scanner 服务未启动修复

    近日发现scan某个网段好久都没有report,到服务器上检查,发现openvassd服务未成功启动. openvas-setup -f systemctl -l |grep openv openva ...

  4. shell进程中的特殊状态变量

    $?:获取执行上一个指令的执行状态返回值(0为成功,非0为失败) $$:获取当前执行的shell脚本的进程号(PID) $!:获取上一个在后台工作的进程的进程号 $_:获取在此之前执行的命令或脚本的最 ...

  5. 服务集群session问题

    1. http协议本身无状态,可通过Session与Cookie记录前端与后端服务器的交互状态: 2. 但是每次客户端回传必须在头信息中带有cookie, 如果session过多,会增加数据传输量: ...

  6. VS添加WebService工具

    最近在做和WebService相关的项目,因为只是在学校里面用过,出来工作一直没有用到,所以不是很熟悉,于是自己配置了一个WebService工具给添加到了VS(VisualStudio)里面,其实就 ...

  7. c/c++ include 头文件的方式

    在编写c/c++代码时,#include 头文件有两种方式:一个是#include “文件名”,一个是#include <文件名>.区别在于: 前者在程序编译时系统首先在源程序所在的目录( ...

  8. 为什么在 Linux 系统中,不建议超频

    CPU 是一部计算机内的心脏啦!因为不论你做什么事情都需要 CPU 来加以运作的!(虽然有时工作量大.有时工作量小!),在 586 以前的计算机( 包含 386, 486, 与 586 ) ,CPU ...

  9. LINUX磁盘分区

    在学习 Linux 的过程中,安装 Linux 是每一个初学者的第一个门槛.在这个过程中间,最大的困惑莫过于给硬盘进行分区.虽然,现在各种发行版本的 Linux 已经提供了友好的图形交互界面,但是很多 ...

  10. windows 10安装docker一直挂起在Installing Components and Removing Files

    碰到这个问题百度了好久都没有找到解决方式,什么用管理员方式运行,给文件夹权限啊,都不好使. 后面在bing上面搜docker install compoents关键字找到一条结果,如下如 点进链接,内 ...