MySQL危险而诡异的update操作和惊魂5分钟
MySQL危险而诡异的update操作和惊魂5分钟
简介
Part1:写在最前
上班正忙的不可开交呢,一个消息过来,得知研发人员误操作数据库了....不带where条件,整表更新Orz,还让不让人好好活了,心中一万只XXX啊~无奈,分清事情的轻重,优先处理这起事故。
在简单沟通后,了解到事故的原因是研发人员使用update忘记带where条件。这本身没什么诡异的,诡异的是在决定要不要进行恢复的时候,笔者稍微犹豫了一下,因为看起来是不需要恢复的,那么具体是什么样的情况呢?
Part2:危险场景再现
研发人员update使用了错误的语法,本意是update helei3 set a='1' where b='a';
结果写成了update helei3 set a='1' and b='a';
这样的话使得helei3这张表的a列被批量修改为0或1。
过了几秒钟,发现写错并且已经敲了回车后,此时update语句还没有更新完,立即ctrl+c
那么数据到底有没有被写脏?
复现
Part1:创建所需表
首先我们创建测试表,a列b列均为varchar类型
root@127.0.0.1 (helei)> show create table helei3\G *************************** 1. row *************************** Table: helei3 Create Table: CREATE TABLE `helei3` ( `a` varchar(10) DEFAULT NULL, `b` varchar(255) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
表中数据如下
root@127.0.0.1 (helei)> select * from helei3; +------+------+ | a | b | +------+------+ | 1 | a | | 2 | b | | 3 | c | +------+------+ 3 rows in set (0.00 sec)
Part2:错误语句生成
我们都知道,update的语法是update tablename set col1=val,col2=val2 where xxx;
那么当逗号换成了and,会出现什么样的严重后果呢?
这个时候由于没有where条件,导致整表更新,那猜猜看后续结果是什么
root@127.0.0.1 (helei)> update helei3 set a='1' and b='a'; root@127.0.0.1 (helei)> select * from helei3; +------+------+ | a | b | +------+------+ | 1 | a | | 0 | b | | 0 | c | +------+------+ 4 rows in set (0.00 sec)
没错,这个SQL将a列整表更新为0,而之所以第一个a=1是由于a='1' and b='a'这个条件是真,所以为1。
Part3:ctrl+c
了解Part2后,我们再看下当update全表更新发现误操作后立即ctrl+c能不能回滚避免误操作。
提前准备好一张50万数据的表
root@127.0.0.1 (helei)> select count(*) from helei; +----------+ | count(*) | +----------+ | 500000 | +----------+ 1 row in set (0.06 sec) root@127.0.0.1 (helei)> show create table helei\G *************************** 1. row *************************** Table: helei Create Table: CREATE TABLE `helei` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `c1` int(10) NOT NULL DEFAULT '0', `c2` int(10) unsigned DEFAULT NULL, `c5` int(10) unsigned NOT NULL DEFAULT '0', `c3` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `c4` varchar(200) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `idx_c1` (`c1`), KEY `idx_c2` (`c2`) ) ENGINE=InnoDB AUTO_INCREMENT=500001 DEFAULT CHARSET=utf8mb4 1 row in set (0.00 sec)
误操作整表更新后等待几秒立即ctrl + c
root@127.0.0.1 (helei)> update helei set c2=1; ^CCtrl-C -- sending "KILL QUERY 2133" to server ... Ctrl-C -- query aborted. ^CCtrl-C -- sending "KILL 2133" to server ... Ctrl-C -- query aborted. ERROR 2013 (HY000): Lost connection to MySQL server during query root@127.0.0.1 (helei)> select * from helei where c2=1; Empty set (0.00 sec)
可以看到c2列并没有出现部分更新为1的情况,也就是说整表更新的这条操作回滚了。
细心点可以看到binlog pos号也没有发生变化
root@127.0.0.1 (helei)> show master status; +------------------+-----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+-----------+--------------+------------------+ | mysql-bin.000004 | 124886658 | | | +------------------+-----------+--------------+------------------+ 1 row in set (0.00 sec)
Part4:诡异
前三章看完后,我们来看下有什么地方是诡异的,在生产环境中,由于不知道刚刚那条SQL是否已经更新了部分数据,我们采取了这种方式来验证。
root@127.0.0.1 (helei)> select * from helei3 where a='0'; +------+------+ | a | b | +------+------+ | 0 | b | | 0 | c | +------+------+ 2 rows in set (0.00 sec) root@127.0.0.1 (helei)> select * from helei3 where a=0; +------+------+ | a | b | +------+------+ | 0 | b | | 0 | c | | zz | zz | +------+------+ 3 rows in set (0.00 sec)
发现数据不一致,生产环境的更唬人一些,列中并没有存储0,而都是字母或纯数字,当我执行上述两个SQL的时候,发现结果差了非常多,还爆出了很多的warnings。
| Warning | 1292 | Truncated incorrect DOUBLE value: 'XXX' |
那么我想知道刚刚的误操作到底是不是生效了呢,为什么会出现差个引号结果就差这么多呢?
分析
Part1:构建数据
root@127.0.0.1 (helei)> insert into helei3 values('zz','zz'); root@127.0.0.1 (helei)> select * from helei3; +------+------+ | a | b | +------+------+ | 1 | a | | 0 | b | | 0 | c | | zz | zz | +------+------+ 4 rows in set (0.00 sec)
Part2:查询对比
那么这时我们执行一条查询会有两种结果
root@127.0.0.1 (helei)> select * from helei3 where a='0'; +------+------+ | a | b | +------+------+ | 0 | b | | 0 | c | +------+------+ 2 rows in set (0.00 sec) root@127.0.0.1 (helei)> select * from helei3 where a=0; +------+------+ | a | b | +------+------+ | 0 | b | | 0 | c | | zz | zz | +------+------+ 3 rows in set (0.00 sec)
这是为什么呢?
Part3:root cause
root@127.0.0.1 (helei)> select 'zz'=0; +--------+ | 'zz'=0 | +--------+ | 1 | +--------+ 1 row in set, 1 warning (0.00 sec) root@127.0.0.1 (helei)> select 'zz3'=0; +---------+ | 'zz3'=0 | +---------+ | 1 | +---------+ 1 row in set, 1 warning (0.00 sec) root@127.0.0.1 (helei)> select '3'=0; +-------+ | '3'=0 | +-------+ | 0 | +-------+ 1 row in set (0.00 sec)
可以看出,当包含字母的时候,mysql认为=0是真,并抛出warning。
root@127.0.0.1 (helei)> show warnings; +---------+------+----------------------------------------+ | Level | Code | Message | +---------+------+----------------------------------------+ | Warning | 1292 | Truncated incorrect DOUBLE value: 'zz' | +---------+------+----------------------------------------+ 1 row in set (0.00 sec)
Part4:MySQL Doc
In InnoDB
, all user activity occurs inside a transaction. If autocommit
mode is enabled, each SQL statement forms a single transaction on its own. By default, MySQL starts the session for each new connection with autocommit
enabled, so MySQL does a commit after each SQL statement if that statement did not return an error. If a statement returns an error, the commit or rollback behavior depends on the error.
Part5:我的理解
InnoDB存储引擎符合事务的ACID特性。 它将一次完成所有操作,或者在中断时不会执行操作和回滚。 InnoDB也是MySQL 5.5及以上版本的默认引擎。
但是对于非事务性的MyISAM存储引擎。 他的原子操作是一行一行完成的。 所以如果你中断这个过程,那就会更新/删除到哪里就到哪里了。
——总结——
通过本文,您能了解到update中使用了and这种错误语法带来的严重后果,以及在SQL语句执行完之前,ctrl +c 到底有没有效果~由于笔者的水平有限,编写时间也很仓促,文中难免会出现一些错误或者不准确的地方,不妥之处恳请读者批评指正。
喜欢的读者可以点个赞来个关注,您的赞美和关注是对笔者继续发文的最大鼓励与支持!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
一个简单的主机管理模拟程序
最近写的一个小练习,主要是把前面学的东西整合一下。写了一个简单的主机管理界面,主要是练习以下知识点: Session和Cookie进行登录验证(装饰器) 数据库的基本操作 (单表,1对多,多对多) Form的简单使用实现验证 Bootstrap模板写个简单界面 自定义分页 信号,中间件,CSRF,模板语言,JavaScript,AJAX等等 界面比较low,毕竟不是专业的。 附件里面是 Django 的源代码,3个文件放在一起winrar解压就可以打开 移动端暂未显示博客附件,想要下载的朋友请移步PC端查看本文,谢谢~
- 下一篇
思路很重要!不同厂商Spanning-tree对接案例思考
朋友们是否经历过客户网络设备替换割接,如果被替换的设备和新设备不是一个厂商,往往在割接准备过程中会暴露出很多棘手的问题。例如,设备厂商往往有大量的私有协议,从而导致了在不同厂商设备之间无法正常互联互通。如果碰到这种情况你会怎么做? 我想无外乎以下两种种可能性: 修改新设备网络设计方案,针对目前旧设备做出妥协,从而避免私有协议带来的网络故障问题。 寻找一个共通的标准协议,并把老设备的协议逐步替换成业界标准协议。完成以后再准备替换工作。 但是两个方案都不是最优方案 方案一虽然实施起来相对容易,缩短工期。但是由于否决最优化组网设计并替换成其他兼容性设计对客户的网络会造成深远的影响。同时也大大增加了后期网络故障排错的困难。 方案二长期来说是最好的,但是实施起来费时费力,无形之间增加了项目的人力成本和时间成本。 那么,在某些特定情况下能否找到第三个方案,通过仔细研究其私有协议特性,从而让两个厂商的私有协议互联互通,最终达到和谐相处呢? 答案是肯定的,下面我将以一个实际网络替换案例,给大家演示如何从发现问题,研究分析问题,到最终解决问题的一套整体思路。希望朋友们能够从中有所收获并能举一反三应用到...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- 2048小游戏-低调大师作品
- CentOS8编译安装MySQL8.0.19
- Hadoop3单机部署,实现最简伪集群
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Linux系统CentOS6、CentOS7手动修改IP地址