记一次MySQL更新操作 where 子查询(in 子句)出现的性能坑!!!!

由于这个问题纠缠了我一下午,现在记录一下,以免后续重覆辙!

场景简单描述大概是这样的,有两张表A(id,name,xxx),B(id,aid,name,xxx),其中B表的aid列是A表的外键,现在我想根据一些条件从B表选出aid然后根据这些aid对A表进行更新,其中A表的数据量大概是七百万条,B表的数据量是四五千条,刚开始我写的sql大概是这样子的:

UPDATE scene_info set onoff = -1
WHERE ID in(
select SCENEID from scene_operation tt left join global_dev_master_slave_ref ms
on ms.master_dev_id=tt.LINKERID and ms.slave_dev_id=tt.DEVID
where linkerid is not null
and isdisable = 0
and ms.master_dev_id is null
and ms.slave_dev_id is null);

理论上这点数据量应该很快就会执行完成,但是事实不是这样的,一直running,无语,多次直接杀掉执行进程分析,无果,各种加上索引后查看执行计划如图:

 

第一条对对主表的更新,走的是主键,但是看rows ,居然扫描了六百多万条记录,这个表总共的记录才七百多万,只过滤掉了100条数据,但是其实符合条件的记录只有几十条而已。

为什么会这样呢,上面的执行计划后两条的select type都是DEPENDENT SUBQUERY,这意味着什么?——子查询取决于外面的查询,MySql 先执行外查询,内查询根据这个查询结果(如执行计划里所述,6940599 rows)的每一条记录组成新的查询语句。

好坑,mysql内部居然这样执行,但是如果把整个更新语句改成查询语句,结果又大大不一样,查询结果出来的速度是毫秒级的。后面经过各种网上搜索找到解决方案,in子查询改为join联表更新,更改sql为如下样子:

update scene_info si inner join scene_operation so on si.id = so.SCENEID left join global_dev_master_slave_ref ms
on ms.master_dev_id=so.LINKERID and ms.slave_dev_id=so.DEVID set si.onoff = -1
where so.linkerid is not null
and so.isdisable = 0
and ms.master_dev_id is null
and ms.slave_dev_id is null

 我们再看看执行计划

 

 这个就比较完美了,至此问题顺利解决!

参考至 https://blog.csdn.net/defonds/article/details/46745143

posted @ 2020-03-06 23:48  松子无泪  阅读(990)  评论(0编辑  收藏  举报