MySQL危险而诡异的update操作和惊魂5分钟

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

简介

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类型

1
2
3
4
5
6
7
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

表中数据如下

1
2
3
4
5
6
7
8
9
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条件,导致整表更新,那猜猜看后续结果是什么

1
2
3
4
5
6
7
8
9
10
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万数据的表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

1
2
3
4
5
6
7
8
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号也没有发生变化

1
2
3
4
5
6
7
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是否已经更新了部分数据,我们采取了这种方式来验证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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'  |

wKioL1nDeQKSLRdBAACW1pK8v_Q625.jpg

那么我想知道刚刚的误操作到底是不是生效了呢,为什么会出现差个引号结果就差这么多呢?



分析

Part1:构建数据

1
2
3
4
5
6
7
8
9
10
11
12
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:查询对比

那么这时我们执行一条查询会有两种结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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。

1
2
3
4
5
6
7
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 到底有没有效果~由于笔者的水平有限,编写时间也很仓促,文中难免会出现一些错误或者不准确的地方,不妥之处恳请读者批评指正。










 本文转自 dbapower 51CTO博客,原文链接:http://blog.51cto.com/suifu/1967542
,如需转载请自行联系原作者

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
SQL 关系型数据库 MySQL
Python怎么操作Mysql数据库
Python怎么操作Mysql数据库
51 0
|
1月前
|
SQL 关系型数据库 MySQL
python如何操作mysql数据库
python如何操作mysql数据库
24 0
|
2月前
|
SQL 存储 算法
mysql常用指令操作
mysql常用指令操作
|
21天前
|
存储 SQL 关系型数据库
【MySQL】4. 表的操作
【MySQL】4. 表的操作
21 0
|
3月前
|
关系型数据库 MySQL
MySQL对小数进行四舍五入等操作
MySQL对小数进行四舍五入等操作
25 0
|
1月前
|
SQL 关系型数据库 MySQL
|
2月前
|
SQL 关系型数据库 MySQL
MySQL技能完整学习列表3、SQL语言基础——1、SQL(Structured Query Language)简介——2、基本SQL语句:SELECT、INSERT、UPDATE、DELETE
MySQL技能完整学习列表3、SQL语言基础——1、SQL(Structured Query Language)简介——2、基本SQL语句:SELECT、INSERT、UPDATE、DELETE
51 0
|
20天前
|
缓存 关系型数据库 MySQL
MySQL查询优化:提速查询效率的13大秘籍(合理使用索引合并、优化配置参数、使用分区优化性能、避免不必要的排序和group by操作)(下)
MySQL查询优化:提速查询效率的13大秘籍(合理使用索引合并、优化配置参数、使用分区优化性能、避免不必要的排序和group by操作)(下)
|
1月前
|
存储 SQL 关系型数据库
【mysql】—— 表的操作
【mysql】—— 表的操作
|
1月前
|
SQL 存储 关系型数据库
【mysql】—— 数据库的操作
【mysql】—— 数据库的操作
【mysql】—— 数据库的操作