在线上环境中部署脚本,可谓是常在河边走,哪有不湿鞋,所以大大小小的案例总结下来,还是会发现一些有趣的地方,这些可以作为操作时的一些参考,仅供参考而已。
第一类脚本是修复脚本,比如提供的数据修复功能,数据补丁等,这类脚本的特点是后续的数据变更很可能会依赖于之前的操作,环环相扣。所以一旦执行过程中出现问题,就需要保证这个操作可回退,否则会是雪上加霜。
脚本1 | |||||
脚本2 | |||||
脚本3 | |||||
脚本4 | |||||
脚本5 |
第二类的脚本是彼此之间没有直接联系。哪怕是中间执行出一点问题也不会直接影响其他业务。
脚本1 | |
脚本2 | |
脚本3 | |
脚本4 | |
脚本5 |
第三类的脚本介于两者之间,有互相的依赖,也有彼此独立的部分。
脚本1 | ||
脚本2 | ||
脚本3 | ||
脚本4 | ||
脚本5 |
假设我们已经对上述三类需求很熟悉,很清楚自己在做什么。在MySQL的场景中是否可以都一一满足呢。
我们可以做一个简单的测试来说明。首先我们创建一个表test_abc,然后插入3条数,其中第2条是有问题的,插入可能会报错。
create table test_abc (id int primary key,name varchar(20));
insert into test_abc values(1,'aa');
insert into test_abc values('aa','bb');
insert into test_abc values(3,'cc');
那现在就有几种实现方式,
1)执行第2条报错,直接忽略,继续执行
2)执行第2条报错,直接在这里定格,然后退出
3)执行第2条报错,然后回滚退出
所以说这样一个看起来极其简单的语句其实可能有下面三种执行的结果,这就和我刚开始所说的场景很类似了。
我们来看看具体怎么实现。
方法1:首先使用source的方式执行脚本,发现执行在第2条insert出失败,但是从执行日志可以看出,是继续执行了。
mysql> source test1.sql
Query OK, 0 rows affected (0.04 sec)
Query OK, 1 row affected (0.00 sec)
ERROR 1366 (HY000): Incorrect integer value: 'aa' for column 'id' at row 1
Query OK, 1 row affected (0.01 sec)
查看执行后的表数据,确实id为1和3的记录都插入了。
mysql> select *from test_abc;
+----+------+
| id | name |
+----+------+
| 1 | aa |
| 3 | cc |
+----+------+
2 rows in set (0.00 sec)
方法2:方法2看起来很简洁,就是通过重定向的方式来执行,可以从错误日志看出是执行到了第2条语句失败了。
# mysql test < test1.sql
ERROR 1366 (HY000) at line 5: Incorrect integer value: 'aa' for column 'id' at row 1
查看数据的情况,会发现前面的执行是成功了,后面都没执行,直接退出了。
mysql> select *from test_abc;
+----+------+
| id | name |
+----+------+
| 1 | aa |
+----+------+
1 row in set (0.09 sec)
方法3:我们开启事务,看看能否达到我们的预期结果,可以顺利回滚。
mysql>begin;
mysql> source test1.sql
Query OK, 0 rows affected (0.03 sec)
Query OK, 1 row affected (0.00 sec)
ERROR 1366 (HY000): Incorrect integer value: 'aa' for column 'id' at row 1
Query OK, 1 row affected (0.01 sec)
这个时候查看数据结果,会发现id为1和3都已经插入了。
mysql> select*from test_abc;
+----+------+
| id | name |
+----+------+
| 1 | aa |
| 3 | cc |
+----+------+
2 rows in set (0.00 sec)
我们来尝试回滚
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
但是很不幸,没有任何反应
mysql> select*from test_abc;
+----+------+
| id | name |
+----+------+
| 1 | aa |
| 3 | cc |
+----+------+
2 rows in set (0.00 sec)
这个问题的主要原因是什么呢,其实是第一句是一个create语句,是DDL,会自动提交事务。所以后续的操作就直接无法回滚了。由此我们需要注意的就是在脚本中是否有DDL,如果有还是需要特别 注意的。
方法4:
所以我们剔除脚本里面的DDL,分开单独执行,脚本只保留了那3条insert.
然后我们手工开启事务。
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> source test1.sql
Query OK, 1 row affected (0.00 sec)
ERROR 1366 (HY000): Incorrect integer value: 'aa' for column 'id' at row 1
Query OK, 1 row affected (0.01 sec)
这个时候查看数据,id为1和3的结果都在。
mysql> select *from test_abc;
+----+------+
| id | name |
+----+------+
| 1 | aa |
| 3 | cc |
+----+------+
2 rows in set (0.00 sec)
果断回滚,会发现数据可以达到我们的预期了。
mysql> rollback;
Query OK, 0 rows affected (0.09 sec)
mysql> select *from test_abc;
Empty set (0.00 sec)
所以针对上面三种情况,我们可以得到一很明确的结果。
而如果可以尽可能还是在事务里来控制吧,毕竟MySQL是默认自动提交的。后悔了都来不及。
对于事务的完整性,还有两点需要说一下,第一个是事务正常退出,事务是回滚还是提交。另外一个则是杀掉执行的会话,事务会默认提交还是回滚。
我们一个一个来测试,先来看kill会话的部分。
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test_abc values(5,'ee');
Query OK, 1 row affected (0.00 sec)
然后打开另外一个窗口 kill掉当前执行的会话。然后继续观察。
查询的时候,会发现原来的会话其实已经杀掉了,会自动开启一个新的会话。很明显,事务做了回滚。
mysql> select *from test_abc;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 639
Current database: test
+----+------+
| id | name |
+----+------+
| 1 | aa |
+----+------+
1 row in set (0.09 sec)
另外一个则是正常退出情况下的
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test_abc values(1,'ff');
Query OK, 1 row affected, 0 warning (0.00 sec)
mysql> select *from t1;
+------+------+
| col1 | col2 |
+------+------+
| 1 | ff|
+------+------+
1 row in set (0.00 sec)
mysql> exit --正常退出
Bye
重新登录来验证,会发现事务已经回滚了。
mysql> select *from t1;
Empty set (0.00 sec)
所以通过上面的测试我们可以很清晰的知道这些可能的场景和具体的应对策略,如果明白了这些,在具体业务的操作中至少会长个心。