背景
现网上,发现服务A的导入导出响应特别慢,通过 jstack 定位,查到某个线程一直卡在一个更新语句,并且这个更新语句执行了一个小时有余,这个更新语句是由定时器触发,定时器的频率为1小时。
—— 上个任务还没执行完,下个又来了。。。
把语句拉出来看了一下,就是一个很简单的update 语句,
大概是长这个样子的:
update A set aa = xx where id in (select cc from C where xxxxx = xxid);
大概是长这个样子,然后 A 表数据量不大, 子查询语句更是直接空数据。理论上,这个语句应该是毫秒级内就执行完成的,毕竟要更新的数据,一条都匹配不上,根本无数据可执行。。。但实际上,它硬是执行了一个多小时才执行完。这是个啥事噢。。。
解决
解决是不会解决的了,
看一下这个帖子找一下灵感:
在昨天系统上线之后发现后台有一个接口一直是超时状态,翻来覆去看了很多的地方同时也想了很多原因最后在定位在一条更新语句出现了性能问题导致的接口超时.
这种sql就是标题说的mysql中update里边使用in并且在in中使用子查询时出现的,具体原因网上有很多的解释,这里就大概说一下
sql的形状是 UPDATE 表X SET A = 1, B = 2 WHERE C IN (SELECT C FROM 表Y WHERE D = 3) AND E = 5
大家可以看到上边的语句单独哪一部分都不会出现问题,把update转换成select语句也不会出现问题,唯独上边这样写加上表中数据量有一些就有可能出现性能问题.
这块不再啰嗦,具体的解决办法可以将in中的查询单独拉出来在后端先执行然后以参数形式传入即可避免,
另外一种方法可以尝试改成如下形状只需要套一层 UPDATE 表X SET A = 1, B = 2 WHERE C IN (
SELECT * FROM (SELECT C FROM 表Y WHERE D = 3) ALLINFO) AND E = 5
标蓝处再添加一层可将里边的查询提前执行,避免N*N次这样的慢查询.
该项目对于此方案待验证,关于这个问题的原因。。。也是不知道为什么,一个问号。。。。