问题描述

在系统中发现一条执行时间为为44652.060734秒(12.5小时)的慢SQL,SQL语句为:

UPDATE
ob_internal_task
SET
OPERATE_STATUS = 0
WHERE business_no IN
(
SELECT
outbound_no
FROM
ob_internal_orderstatus
WHERE wave_no = 'xxxxx'
AND VSTATE = 10
AND outbound_no <> 'xxxxxxxx'
)
AND business_type = 20;

对于执行计划为:

*************************** 1. row ***************************
id: 1
select_type: UPDATE
table: ob_internal_task
partitions: NULL
type: index
possible_keys: NULL
key: PRIMARY
key_len: 8
ref: NULL
rows: 1657847
filtered: 100.00
Extra: Using where
*************************** 2. row ***************************
id: 2
select_type: DEPENDENT SUBQUERY
table: ob_internal_orderstatus
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 622710
filtered: 0.09
Extra: Using where

由于两个表上都使用全表扫描,需要循环外表ob_internal_task记录1654884次*内表ob_internal_orderstatus记录622596次=1030324158864(预估影响行数)

解决步骤

表ob_internal_task列business_no上有索引,没有被正确使用,而表上ob_internal_orderstatus缺少合适索引,因此首先优化IN子查询内部,创建索引:

ALTER TABLE ob_internal_orderstatus
ADD INDEX `IDX_wave_no_VSTATE1` (`WAVE_NO`,`VSTATE`,`OUTBOUND_NO`);

创建完索引后执行计划变更为:

*************************** 1. row ***************************
id: 1
select_type: UPDATE
table: ob_internal_task
partitions: NULL
type: index
possible_keys: NULL
key: PRIMARY
key_len: 8
ref: NULL
rows: 1656801
filtered: 100.00
Extra: Using where
*************************** 2. row ***************************
id: 2
select_type: DEPENDENT SUBQUERY
table: ob_internal_orderstatus
partitions: NULL
type: ref
possible_keys: IDX_wave_no_VSTATE1
key: IDX_wave_no_VSTATE1
key_len: 131
ref: const,const,func
rows: 1
filtered: 100.00
Extra: Using where; Using index

外表仍然使用全表扫描,检查两表关联列,发现都是VARCHAR类型,不存在隐式转换。

将UPDATE语句调整为SELECT测试,调整后的SQL为:

SELECT
*
FROM
ob_internal_task
WHERE business_no IN
(
SELECT
outbound_no
FROM
ob_internal_orderstatus
WHERE wave_no = 'BC20190523155122'
AND VSTATE = 10
AND outbound_no <> ''
)
AND business_type = 20;

对于执行计划为:

*************************** 1. ROW ***************************
id: 1
select_type: SIMPLE
TABLE: ob_internal_orderstatus
PARTITIONS: NULL
TYPE: ref
possible_keys: IDX_wave_no_VSTATE1
KEY: IDX_wave_no_VSTATE1
key_len: 68
ref: const,const
ROWS: 26
filtered: 90.00
Extra: USING WHERE; USING INDEX; LooseScan
*************************** 2. ROW ***************************
id: 1
select_type: SIMPLE
TABLE: ob_internal_task
PARTITIONS: NULL
TYPE: ref
possible_keys: idx_business_type,idx_business_no
KEY: idx_business_no
key_len: 303
ref: innerdelivery.ob_internal_orderstatus.OUTBOUND_NO
ROWS: 3
filtered: 10.00
Extra: USING INDEX CONDITION; USING WHERE

对比UPDATE和SELECT语句的执行计划,发现内外表的位置发生变化,同时也导致表ob_internal_task上索引idx_business_no能被正常使用。

考虑到IN语句的影响,尝试将UPDATE语句中的依赖子查询调整为关联查询,调整后SQL为:

UPDATE ob_internal_task T1
INNER JOIN
(
SELECT
outbound_no
FROM
ob_internal_orderstatus
WHERE wave_no = 'BC20190523155122'
AND VSTATE = 10
AND outbound_no <> ''
) AS T2
ON T1.business_no = T2.outbound_no
SET T1.OPERATE_STATUS = 0
WHERE T1.business_type = 20;

调整后的执行计划为:

*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: ob_internal_orderstatus
partitions: NULL
type: range
possible_keys: IDX_wave_no_VSTATE1
key: IDX_wave_no_VSTATE1
key_len: 131
ref: NULL
rows: 25
filtered: 100.00
Extra: Using where; Using index
*************************** 2. row ***************************
id: 1
select_type: UPDATE
table: T1
partitions: NULL
type: ref
possible_keys: idx_business_type,idx_business_no
key: idx_business_no
key_len: 303
ref: func
rows: 3
filtered: 10.00
Extra: Using where

使用关联更新的UPDATE语句能够正常使用索引,执行时间由原来44652.060734秒(12.5小时)缩短至的在30ms内。

PS1:相关表的统计信息未存在明显异常,排除统计信息导致执行计划异常。

优化总结

1、在优化DELETE/UPDATE语句时,通常会将DELETE/UPDATE语句改写成SELECT语句进行测试,以避免测试操作导致数据变更,需要注意两者的执行计划可能不同。

2、当IN语句生产的执行计划为DEPENDENT SUBQUERY类型时,需要考虑外表的循环数量,如果外表循环次数较大,可以考虑调整SQL语句(如关联查询)来优化内外表位置。

MySQL Execution Plan--IN子查询对UPDATE语句影响的更多相关文章

  1. 子查询在UPDATE 语句中的应用

    在UPDATE语句中可以在更新列表中以及WHERE语句使用子查询.下面演示一个将图书的出版日期全部更新为所有图书中的最新出版日期,SQL语句如下: UPDATE T_Book SET FYearPub ...

  2. mysql sql_safe_updates 不支持子查询的更新。

    考虑到开发人员有时候不小心误更新数据,要求线上库的 MySQL 实例都设置 sql_safe_updates=1 来避免没有索引的 update.delete. 结果有一天开发发现下面的一个SQL 没 ...

  3. mssql sql高效关联子查询的update 批量更新

    /* 使用带关联子查询的Update更新     --1.创建测试表 create TABLE Table1     (     a varchar(10),     b varchar(10),   ...

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

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

  5. sql子查询 嵌套SELECT语句

    嵌套SELECT语句也叫子查询,一个 SELECT 语句的查询结果能够作为另一个语句的输入值.子查询不但能够出现在Where子句中,也能够出现在from子句中,作为一个临时表使用,也能够出现在sele ...

  6. MySQL锁类型以及子查询锁表问题、解锁

    MySQL中select * for update锁表的范围 MySQL中select * for update锁表的问题 由于InnoDB预设是Row-Level Lock,所以只有「明确」的指定主 ...

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

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

  8. Mysql - 性能优化之子查询

    记得在做项目的时候, 听到过一句话, 尽量不要使用子查询, 那么这一篇就来看一下, 这句话是否是正确的. 那在这之前, 需要介绍一些概念性东西和mysql对语句的大致处理. 当Mysql Server ...

  9. MySQL 使用JOIN优化子查询

    1.数据准备 mysql> select * from student; +----+--------+----------+---------+-------------+ | id | na ...

随机推荐

  1. Git采坑与问题排查

    目录 Case1:代码库中存大文件 Case1:代码库中存大文件 背景 有这种情况,项目运行需要依赖一个文件(比如需要读一个文件中的数据),那么最直接的方式就是将该文件添加到代码中的分支中,然后将分支 ...

  2. 搭建Portainer可视化界面(转)

    转载地址:https://blog.csdn.net/u011781521/article/details/80469804 一.什么是Portainer? Portainer是Docker的图形化管 ...

  3. jzy3D从入门到弃坑_4尝试使用jzy3D1.0画图失败

    jzy3D从入门到弃坑_4 尝试使用jzy3D1.0画图失败 觉得有用的话,欢迎一起讨论相互学习~Follow Me 记录一下使用jzy3D1.0失败 究其原因在于 本人才疏学浅,对于JAVA ope ...

  4. Spark实战系列目录

    1 Spark rdd -- action函数详解与实战 2 Spark rdd -- transformations函数详解与实战(上) 3 Spark rdd -- transformations ...

  5. Python浮点数数据精度控制

    代码 import decimal from decimal import Decimal, getcontext if __name__ == '__main__': decimal.getcont ...

  6. 【linux基础】如何配置ubuntu系统为静态IP地址

    前言 连接远程server重启的时候发现IP发生变化,影响远程连接,此时,需要将server配置为静态IP. 系统环境 ubuntu16.04 操作过程 1. 设置IP和DNS command sud ...

  7. Tomcat总结四种部署方式

    静态部署 一.Webapps 把项目复制到Tomcat的Webapps 把war包复制到Tomcat的Webapps下 *修改Webapps的默认路径: * Tomcat的conf目录下的server ...

  8. IDEA和JIRA任务联动(redmine同理)

    IDEA和JIRA任务联动-操作流程详解 redmine和这个步骤类似,只是第一步选择的工具不一样 操作流程 添加jira配置,File->Setting-->tools-->Tas ...

  9. 【转载】嵌入式 Linux 移植 Dropbear SSH server

    0. 背景  OpenSSH因为其相对较大,一般不太适用于嵌入式平台,多用于PC或者服务器的Linux版本中.  Dropbear是一个相对较小的SSH服务器和客户端.它运行在一个基于POSIX的各种 ...

  10. Maven 初学+http://mvnrepository.com/

    了解 maven是一款服务于java平台的自动化构建工具(项目管理工具) 构建:全方位.多角度.深层次地建立 项目构建是一个项目从:源代码.编译.测试.打包.部署.运行的过程 用来解决团队开发遇到的问 ...