非 996 删库跑路,发生在 update 上的悲剧

SQL herman 188浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xmtxtt,发送下载链接帮助你免费下载!
本博客日IP超过1800,PV 2600 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog,之前的微信号好友位已满,备注:返现
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序

最近 996 很“热闹”,但我还是希望大家能够回归平淡,理性看待它,毕竟生活还要继续。

昨天,我们公司发生了一件“删库跑路”的事件,为此我花费了一整个通宵的时间来处理事故现场,进行数据恢复。

当我在群里讨论这件事的时候,很多程序员自黑到肯定是因为 996,其实并不是了。因为我们公司很少有加班的,基本上都是自己主动去学习,去充电。

java 入坑指南

删库容易跑路难。事件的起因是这样的,最近有几个运营同事离职,4 月份大家都想换个环境,所以,领导就对部分运营同事的相关权限进行了回收。但是,意外发生了,领导不小心把在职的相关运营的同事的权限也回收了。而且,领导还出国去出差了。

而正在这时,运营还出了差错,发货的物流信息给搞错用户了。一些人购买的商品还没发货呢?一看订单,发现自己购买的商品被邮寄到北京了。总之就是和自己的地址不相符,这个时候投诉电话一大堆。

运营将问题上报,抛给了程序员。再加上领导在国外,不好分配权限,于是运营就一直决定让程序员改数据库。事情也还简单,于是就答应了,并将任务抛给了一个程序员,给他开了生产库的权限。

原本我以为事情就这么简单的结束了呢?谁知道被同事挖了一个大坑。他竟然不小心,使用 update 更新 SQL 的时候,把所有数据都更新了。年前放假前 3 天的事故再现《泪奔,同事执行 update 语句没有添加 where 条件!》,大 Boss 回来非剥了我的皮不可。

为了将风险降到最低,通知所有人,一致对外,系统正在升级,升级过程中会进行数据迁移,届时可能会造成部分数据显示不正确,敬请谅解!公关做好之后,客服也安静了。但是我却只能连夜加班,进行数据修复,苦的一逼。

造成事故的 SQL 如下:

UPDATE order_item SET logistics_no = '123123',logistics_type = 1 
    WHERE order_id IN(
        SELECT aorder_id FROM order WHERE order_no IN (1231231,23412,1223)
    );

这个 SQL 看起来没什么大问题,通过 in 查询,控制范围。但实际上,一执行竟然把所有数据都更新了。

原因出在哪里呢?

实际上,我一眼就看出来了这条 SQL 的问题。aorder_id 这个列根本就不存在。当你把下面这条语句拿出来执行,会报错。无法执行。

SELECT aorder_id FROM order WHERE order_no IN (1231231,23412,1223)

但当你把两条 SQL 合起来,用我上面的 in 的方式进行查询又不报错。

你在测试的时候,需要注意的是:子查询种的 aorder_id 是 order_item 中存在的,而不存在于 order 表中的。如果你的 order_item 中不存在 aorder_id 字段,那么这条语句在查询时会直接报错。

关于为什么只要子查询中取的字段是 order_item 中的字段就不报错的原因,目前网上还没有这类的文章介绍。但是我们可以通过我前面教的 EXPLAIN EXTENDED 和 SHOW WARNINGS; 来看看 MySQL 的子查询到底是如何执行的。

EXPLAIN EXTENDED select * from xttblog_order 
    WHERE ORDER_NO in(select ORDER_NO from test WHERE id in(1,2));

执行上面的语句之后,你会看到整条 SQL 被拆分成两次执行,还用到了 temporary 和 join。

EXPLAIN EXTENDED

接着我们在执行下面的 SQL 语句,看看结果。

SHOW WARNINGS;
SHOW WARNINGS

贴出来,如下所示:

Field or reference 'test.xttblog_order.ORDER_NO' of SELECT #2 was resolved in SELECT #1

/* select#1 */ select `test`.`xttblog_order`.`ID` AS `ID`,
`test`.`xttblog_order`.`ORDER_NO` 
AS `ORDER_NO`,`test`.`xttblog_order`.`CREATE_TIME` AS `CREATE_TIME`,
`test`.`xttblog_order`.`UPDATE_TIME` 
AS `UPDATE_TIME` from `test`.`xttblog_order` semi join (`test`.`test`) 
where (`test`.`test`.`id` in (1,2))

这表明,最终的查询变成了两表 join 查询。子查询如果在内层表里找不到字段会到外层去找这个字段。所以,你把子查询单独拿出来执行会报错,但是组合起来后并不会报错。 MySQL 会把子查询转为连接连接,为什么要这样呢?这是优化器和的底层实现决定。

以上,这个坑希望大家都能够铭记!

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加QQ1群:135430763(2000人群已满),QQ2群:454796847(已满),QQ3群:187424846(已满)。QQ群进群密码:xttblog,想加微信群的朋友,之前的微信号好友已满,请加博主新的微信号:xttblog,备注:“xttblog”,添加博主微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

本文原文出处:业余草: » 非 996 删库跑路,发生在 update 上的悲剧