Insert into select引起死锁问题分析
正常使用Insert into select去迁移数据:
INSERT INTO order_record SELECT * FROM order_today WHERE pay_success_time < '2020-03-08 00:00:00';
因为是在生产环境直接备份数据,所以有加上“pay_success_time < '2020-03-08 00:00:00'”这个条件(因为历史数据是不会再改动的),这条sql看似没有任何问题,但是却导致线上很多数据出现添加,修改失败。这到底是什么原因导致的。我们先来了解下Insert into select的工作原理,在默认的事务隔离级别下:insert into order_record select * from order_today 加锁规则是:order_record表锁,order_today逐步锁(扫描一个锁一个)。从这里我们可以看出,在执行备份的时候,会导致order_today表被逐步锁定,知道备份到最后全表锁定。由于锁定的数据越来越多,就导致出现了大量数据插入失败。最后全部锁住,导致无法插入数据。
那我们要如何避免这类问题的发生呢?
由于查询条件会导致order_today全表扫描,什么能避免全表扫描呢,很简单嘛,给pay_success_time字段添加一个idx_pay_suc_time索引就可以了,由于走索引查询,就不会出现扫描全表的情况而锁表了,只会锁定符合条件的记录。当然这也必须保证在历史数据不会被更改的情况下。
修改后的sql:
INSERT INTO order_record SELECT * FROM order_today FORCE INDEX (idx_pay_suc_time) WHERE pay_success_time <= '2020-03-08 00:00:00';
最后总结:
使用insert into tablA select * from tableB语句时,一定要确保tableB后面的where,order或者其他条件,都需要有对应的索引,来避免出现tableB全部记录被锁定的情况。
参考:
参考文章:
https://blog.csdn.net/asdfsadfasdfsa/article/details/83030011

浙公网安备 33010602011771号