1.概述

原文:因用了Insert into select语句,同事被开除了!

Insert into select 请慎用,同事因为使用了 Insert into select 语句引发了重大生产事故,最后被开除。

某天 xxx 接到一个需求,需要将表 A 的数据迁移到表 B 中去做一个备份。他本想通过程序先查询查出来然后批量插入,但 xxx 觉得这样有点慢,需要耗费大量的网络 I/O,决定采取别的方法进行实现。

通过在某度的海洋里遨游,他发现了可以使用 insert into select 实现,这样就可以避免使用网络 I/O,直接使用 SQL 依靠数据库 I/O 完成,这样简直不要太棒,然后他就被开除了。

事故发生的经过

由于数据数据库中 order_today 数据量过大,当时好像有 700W 了,并且每天在以 30W 的速度增加。

所以上司命令 xxx 将 order_today 内的部分数据迁移到 order_record 中,并将 order_today 中的数据删除,这样来降低 order_today 表中的数据量。

由于考虑到会占用数据库 I/O,为了不影响业务,计划是 9:00 以后开始迁移,但是 xxx 在 8:00 的时候,尝试迁移了少部分数据(1000 条),觉得没啥问题,就开始考虑大批量迁移。

在迁移的过程中,应急群是先反应有小部分用户出现支付失败,随后反应大批用户出现支付失败的情况,以及初始化订单失败的情况,同时腾讯也开始报警。

然后 xxx 就慌了,立即停止了迁移。本以为停止迁移就就可以恢复了,但是并没有。

后面发生的你们可以脑补一下,当时整个支付系统瘫痪了快一个小时,客服电话都被打爆。

事故还原

在本地建立一个精简版的数据库,并生成了 100w 的数据。模拟线上发生的情况。

建立表结构

订单表如下:

CREATE TABLE `order_today` (`id` varchar(32) NOT NULL COMMENT '主键',`merchant_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '商户编号',`amount` decimal(15,2) NOT NULL COMMENT '订单金额',`pay_success_time` datetime NOT NULL COMMENT '支付成功时间',`order_status` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '支付状态  S:支付成功、F:订单支付失败',`remark` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间 -- 修改时自动更新',PRIMARY KEY (`id`) USING BTREE,KEY `idx_merchant_id` (`merchant_id`) USING BTREE COMMENT '商户编号') ENGINE=InnoDB DEFAULT CHARSET=utf8;

订单记录表如下:

CREATE TABLE order_record like order_today;

今日订单表数据如下:

模拟迁移

把 8 号之前的数据都迁移到 order_record 表中去:

INSERT INTO order_record SELECT* FROMorder_today WHEREpay_success_time < '2020-03-08 00:00:00';

在 Navicat 中运行迁移的 SQL,同时开另个一个窗口插入数据,模拟下单:


从上面可以发现一开始能正常插入,但是后面突然就卡住了,并且耗费了 23s 才成功,然后才能继续插入。这个时候已经迁移成功了,所以能正常插入了。

出现的原因

在默认的事务隔离级别下:insert into order_record select * from order_today 加锁规则是:order_record 表锁,order_today 逐步锁(扫描一个锁一个)。

分析执行过程:

通过观察迁移 SQL 的执行情况你会发现 order_today 是全表扫描,也就意味着在执行 insert into select from 语句时,MySQL 会从上到下扫描 order_today 内的记录并且加锁,这样一来不就和直接锁表是一样了。

这也就可以解释,为什么一开始只有少量用户出现支付失败,后续大量用户出现支付失败,初始化订单失败等情况,因为一开始只锁定了少部分数据,没有被锁定的数据还是可以正常被修改为正常状态。

由于锁定的数据越来越多,就导致出现了大量支付失败。最后全部锁住,导致无法插入订单,而出现初始化订单失败。

解决方案

由于查询条件会导致 order_today 全表扫描,什么能避免全表扫描呢,很简单嘛,给 pay_success_time 字段添加一个 idx_pay_suc_time 索引就可以了。

由于走索引查询,就不会出现扫描全表的情况而锁表了,只会锁定符合条件的记录。

最终的 SQL:

INSERT INTO order_record SELECT* FROMorder_today FORCE INDEX (idx_pay_suc_time)WHEREpay_success_time <= '2020-03-08 00:00:00';

执行过程如下:

总结

使用 insert into tablA select * from tableB 语句时,一定要确保 tableB 后面的 where,order 或者其他条件,都需要有对应的索引,来避免出现 tableB 全部记录被锁定的情况。

【MySQL】MySQL Insert into select 大量锁表导致无法插入相关推荐

  1. MySQL的insert into select 引发锁表

    上周五HaC我要上线,有一个脚本需要执行,执行前需要备份一个表. 运维大佬:"这个表的备份为什么要这么久,,??" 1秒过去了--2秒过去了-- 期间运营反馈系统出现大量订单超时情 ...

  2. insert into select语句锁表故障

    深入研究insert into select语句锁表故障(上) 故障描述 前几天,一个mysql数据库运维同事,在生产上用insert into select * from语句,在生产上备份了一张表, ...

  3. delete 会不会锁表_MySQL的insert into select 引发锁表

    上周五HaC我要上线,有一个脚本需要执行,执行前需要备份一个表. 运维大佬:"这个表的备份为什么要这么久,,??" 1秒过去了--2秒过去了-- 期间运营反馈系统出现大量订单超时情 ...

  4. 查询很慢会导致锁表吗_MySQL的insert into select 引发锁表

    上周五HaC我要上线,有一个脚本需要执行,执行前需要备份一个表. 运维大佬:"这个表的备份为什么要这么久,,??" 1秒过去了--2秒过去了-- 期间运营反馈系统出现大量订单超时情 ...

  5. mysql查询数据会不会锁表_mysql select是否会锁表 ?

    mysql select是否会锁表 ? 有的人说mysql的 select 会锁表 ,有的人说 mysql 的查询不会锁表 . 其他他们都对,没有 ,但是很片面. 其实对于mysql的select 是 ...

  6. MySQL中INSERT INTO SELECT的使用

    1. 语法介绍       有三张表a.b.c,现在需要从表b和表c中分别查几个字段的值插入到表a中对应的字段.对于这种情况,可以使用如下的语句来实现: INSERT INTO db1_name (f ...

  7. 语法:MySQL中INSERT INTO SELECT的使用

    1. 语法介绍       有三张表a.b.c,现在需要从表b和表c中分别查几个字段的值插入到表a中对应的字段.对于这种情况,可以使用如下的语句来实现: INSERT INTO db1_name (f ...

  8. mysql+inser+select_解析MySQL中INSERT INTO SELECT的使用

    1. 语法介绍 有三张表a.b.c,现在需要从表b和表c中分别查几个字段的值插入到表a中对应的字段.对于这种情况,可以使用如下的语句来实现: INSERT INTO db1_name (field1, ...

  9. SELECT INTO 和 INSERT INTO SELECT 两种表复制语句

    SELECT INTO 和 INSERT INTO SELECT 两种表复制语句 Insert是T-sql中常用语句,Insert INTO table(field1,field2,...) valu ...

最新文章

  1. 技术/领域专家有什么要求?
  2. 小米开源文件管理器MiCodeFileExplorer-源码研究(8)-文件排序工具类FileSortHelper
  3. java怎么获取服务器文件夹,java获取远程服务器的文件夹
  4. Bootstrap学习笔记(三) 网格系统
  5. H3C 常用接口和线缆
  6. Hive,Hbase shell 中文变问号(??) 的解决方法
  7. TensorFlow学习笔记——自然语言处理
  8. java讲师北京_Java工程师提升空间大,前途好,该如何跨入它的大门呢?
  9. 阿里巴巴-飞猪 电话面试
  10. 远程服务器如何共享电脑文件共享,远程控制实现文件共享 -电脑资料
  11. list集合练习----斗地主
  12. 外贸大宗商品行业ERP管理解决方案
  13. word 插入表格,位置不在最左边
  14. Incremental Event Detection via Knowledge Consolidation Networks
  15. sql oracle 退格键,Oraclesqlplus中方向键、退格键的使用是怎样的? 爱问知识人
  16. 按当前位置与其它位置远近排序,按经纬度计算
  17. 2019美亚杯个人赛刷题
  18. linux at 邮箱,linux at 命令详解
  19. Spring的bean定义 2 : 通用bean定义逻辑 -- AbstractBeanDefinition
  20. 权限控制框架 shiro

热门文章

  1. 庭审出示用户观影信息是否违法?律师表示如是举证行为 不涉及侵犯隐私
  2. 大涨50%之后 瑞幸咖啡美股盘前再涨逾30%
  3. 美股本周第二次熔断:道指大跌近10% 费城半导体指数大跌11%
  4. 刘作虎:一加新品将全系支持 5G
  5. 大家一起看广告?微信朋友圈广告@好友评论互动功能全量开放
  6. 小米9稳定版系统更新:加入水滴屏形状切换开关
  7. 最新性能测试:Kafka、Pulsar 和 Pravega 哪个最强?
  8. 真机上装不上测试应用,Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE
  9. Android热修复实现及原理
  10. 离线安装Electron