问题描述

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

  1. UPDATE
  2. ob_internal_task
  3. SET
  4. OPERATE_STATUS = 0
  5. WHERE business_no IN
  6. (
  7. SELECT
  8. outbound_no
  9. FROM
  10. ob_internal_orderstatus
  11. WHERE wave_no = 'xxxxx'
  12. AND VSTATE = 10
  13. AND outbound_no <> 'xxxxxxxx'
  14. )
  15. AND business_type = 20;

对于执行计划为:

  1. *************************** 1. row ***************************
  2. id: 1
  3. select_type: UPDATE
  4. table: ob_internal_task
  5. partitions: NULL
  6. type: index
  7. possible_keys: NULL
  8. key: PRIMARY
  9. key_len: 8
  10. ref: NULL
  11. rows: 1657847
  12. filtered: 100.00
  13. Extra: Using where
  14. *************************** 2. row ***************************
  15. id: 2
  16. select_type: DEPENDENT SUBQUERY
  17. table: ob_internal_orderstatus
  18. partitions: NULL
  19. type: ALL
  20. possible_keys: NULL
  21. key: NULL
  22. key_len: NULL
  23. ref: NULL
  24. rows: 622710
  25. filtered: 0.09
  26. Extra: Using where

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

解决步骤

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

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

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

  1. *************************** 1. row ***************************
  2. id: 1
  3. select_type: UPDATE
  4. table: ob_internal_task
  5. partitions: NULL
  6. type: index
  7. possible_keys: NULL
  8. key: PRIMARY
  9. key_len: 8
  10. ref: NULL
  11. rows: 1656801
  12. filtered: 100.00
  13. Extra: Using where
  14. *************************** 2. row ***************************
  15. id: 2
  16. select_type: DEPENDENT SUBQUERY
  17. table: ob_internal_orderstatus
  18. partitions: NULL
  19. type: ref
  20. possible_keys: IDX_wave_no_VSTATE1
  21. key: IDX_wave_no_VSTATE1
  22. key_len: 131
  23. ref: const,const,func
  24. rows: 1
  25. filtered: 100.00
  26. Extra: Using where; Using index

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

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

  1. SELECT
  2. *
  3. FROM
  4. ob_internal_task
  5. WHERE business_no IN
  6. (
  7. SELECT
  8. outbound_no
  9. FROM
  10. ob_internal_orderstatus
  11. WHERE wave_no = 'BC20190523155122'
  12. AND VSTATE = 10
  13. AND outbound_no <> ''
  14. )
  15. AND business_type = 20;

对于执行计划为:

  1. *************************** 1. ROW ***************************
  2. id: 1
  3. select_type: SIMPLE
  4. TABLE: ob_internal_orderstatus
  5. PARTITIONS: NULL
  6. TYPE: ref
  7. possible_keys: IDX_wave_no_VSTATE1
  8. KEY: IDX_wave_no_VSTATE1
  9. key_len: 68
  10. ref: const,const
  11. ROWS: 26
  12. filtered: 90.00
  13. Extra: USING WHERE; USING INDEX; LooseScan
  14. *************************** 2. ROW ***************************
  15. id: 1
  16. select_type: SIMPLE
  17. TABLE: ob_internal_task
  18. PARTITIONS: NULL
  19. TYPE: ref
  20. possible_keys: idx_business_type,idx_business_no
  21. KEY: idx_business_no
  22. key_len: 303
  23. ref: innerdelivery.ob_internal_orderstatus.OUTBOUND_NO
  24. ROWS: 3
  25. filtered: 10.00
  26. Extra: USING INDEX CONDITION; USING WHERE

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

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

  1. UPDATE ob_internal_task T1
  2. INNER JOIN
  3. (
  4. SELECT
  5. outbound_no
  6. FROM
  7. ob_internal_orderstatus
  8. WHERE wave_no = 'BC20190523155122'
  9. AND VSTATE = 10
  10. AND outbound_no <> ''
  11. ) AS T2
  12. ON T1.business_no = T2.outbound_no
  13. SET T1.OPERATE_STATUS = 0
  14. WHERE T1.business_type = 20;

调整后的执行计划为:

  1. *************************** 1. row ***************************
  2. id: 1
  3. select_type: SIMPLE
  4. table: ob_internal_orderstatus
  5. partitions: NULL
  6. type: range
  7. possible_keys: IDX_wave_no_VSTATE1
  8. key: IDX_wave_no_VSTATE1
  9. key_len: 131
  10. ref: NULL
  11. rows: 25
  12. filtered: 100.00
  13. Extra: Using where; Using index
  14. *************************** 2. row ***************************
  15. id: 1
  16. select_type: UPDATE
  17. table: T1
  18. partitions: NULL
  19. type: ref
  20. possible_keys: idx_business_type,idx_business_no
  21. key: idx_business_no
  22. key_len: 303
  23. ref: func
  24. rows: 3
  25. filtered: 10.00
  26. 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. 今天使用Jmeter时遇到的几个问题及解决方法

    JDBC Request :Cannot load JDBC driver class 'com.mysql.jdbc.Driver'解决办法 在Jmeter中run JDBC Request时,收到 ...

  2. tf.tile()函数理解

    转载:https://blog.csdn.net/tsyccnh/article/details/82459859 tensorflow中的tile()函数是用来对张量(Tensor)进行扩展的,其特 ...

  3. 【SpringBoot】SpringBoot配置文件及YAML简介(三)

    SpringBoot配置文件 SpringBoot使用一个全局的配置文件,配置文件名是固定的; application.properties application.yml 配置文件的作用:修改Spr ...

  4. shell中函数的使用

    函数是一个脚本代码块,你可以对它进行自定义命名,并且可以在脚本中任意位置使用这个函数.如果想要这个函数,只要调用这个函数的名称就可以了.使用函数的好处在于模块化以及代码可读性强. (1).函数的创建语 ...

  5. Celery-管理与监控

    1. 监控和管理Workers 1.1 将celery实例放入shell [root@node2 app]# celery -A tasks shell Python 3.6.5 (default, ...

  6. Python - importlib 模块

    importlib 模块可以根据字符串来导入相应的模块 目录结构: 在根目录下创建 importlib_test.py 和 aaa/bbb.py bbb.py: class Person(object ...

  7. jQuery prop方法替代attr方法

    jquery attr()方法获取标签的 checked 会有问题,所以用了 prop() 方法.

  8. PowerShell学习笔记

    1,ps7官方文档 2,使用脚本生成帮助文档 function Add-Node { param ( $selectedNode, $name, $tag ) $newNode = new-objec ...

  9. redis启动警告解决

    vim /etc/rc.localecho never > /sys/kernel/mm/transparent_hugepage/enabled加入上面那句到/etc/rc.local,开机启 ...

  10. 【Spring Cloud学习之二】服务注册和发现

    环境 eclipse 4.7 jdk 1.8 Spring Boot 1.5.2 Spring Cloud 1.2 一.EurekaEureka是Netflix开源的一个RESTful服务,主要用于服 ...