MySQL 事务
MySQL 事务
多表关联的情况下,一条新记录的生成往往涉及多张表的操作。
一个典型的场景,银行转帐。要完成 A 转帐到 B,
- 从 A 帐户减去相应金额
- 给 B 帐户加上相应金额。
这两步要么一起成功,要么都失败,否则就会造成数据不一致。比如 A 的钱少了,但 B 的钱没增加,或者 A 的扣款失败,B 的钱也增加了。
所以需要一种机制来保证这一操作过程中每一步的正确性,当其中任意操作失败时应该将已经进行过的操作回滚,保证整体都失败。
此时这些被绑定的一连串操作便形成了 事务。
下面创建一张空表,为后面示例作准备。
mysql> CREATE TABLE test(idx int);
Query OK, 0 rows affected (0.02 sec)
事务的语法
- MySQL 中,通过
START TRANSACTION
语句来开始一个事务,也可以使用别名BEGIN
和BEGIN WORK
语句。 COMMIT
语句提交修改。- 通过
ROLLBACK
语句回滚。
其中 COMMIT
或 ROLLBACK
均可用来结束一个事务。
来看一个简单的示例:
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO test values(1);
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO test values(2);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM test;
+------+
| idx |
+------+
| 1 |
| 2 |
+------+
2 rows in set (0.00 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM test;
Empty set (0.00 sec)
上面创建了一个名为 test
的表,开始事务,向其中分两次插入记录。
然后查询刚刚插入的两条记录。因为此时是处于一个事务中,所以这两条记录实际上是可以被回滚的。
调用 ROLLBACK
后两次查询,表中已经没有了刚刚的两条记录。
autocommit
抛开事务,MySQL 默认情况下开启了一个自动提交的模式 autocommit
,一条语句被回车执行后该语句便生效了,变更会保存在 MySQL 的文件中,无法撤消。当使用相应语句比如 BEGIN
显式声明开始一个事务时,autocommit
默认会是关闭状态。
可通过查询 @@autocommit
变量来查看当前是否是自动提交的状态,
mysql> SELECT @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)
通过设置 autocommit
为 0
可关闭自动提交,
SET autocommit = 0;
无论是否是自动提交模式,语句执行后都会生效,区别在于,非自动模式下,没提交的那些操作是可以回滚的,一旦提交后便不可撤消了。换句话说,当 autocommit
关闭时,一直是处于事务操作中的,可随时调用 ROLLBACK
进行回滚。
下面的语句展示了 autocommit
关闭时 COMMIT
与否的影响,
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
Empty set (0.00 sec)
mysql> insert into test values(1);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test values(2);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+------+
| idx |
+------+
| 1 |
| 2 |
+------+
2 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
Empty set (0.00 sec)
mysql> insert into test values(3);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
+------+
| idx |
+------+
| 3 |
+------+
1 row in set (0.00 sec)
事务中会触发隐式提交的操作
虽说事务模式下关闭了 autocommit
必需手动执行 COMMIT
才能提交,但有些语句和操作是会隐式触发提交的,在进行事务过程中需要注意,这些操作可在官方文档 Statements That Cause an Implicit Commit 中查找到。
所以不能单纯地认为一个事务中所有操作都是绝对安全可回滚的。
Node.js 示例
以下是来自 npm 模块 mysqljs/mysql 关于事务的示例:
connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO posts SET title=?', title, function (error, results, fields) {
if (error) {
return connection.rollback(function() {
throw error;
});
}
var log = 'Post ' + results.insertId + ' added';
connection.query('INSERT INTO log SET data=?', log, function (error, results, fields) {
if (error) {
return connection.rollback(function() {
throw error;
});
}
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
});
});
总结
关于 autocommit
与事务,他们其实是这样的关系:
- MySQL 每一步操作都可看成一个原子操作。
- 默认情况下,
autocommit
是开启状态,所以第一条语句都是执行后自动提交,语句产生的效果被记录保存了下来。 - 关于
autocommit
后,语句不会自动提交,需要手动调用COMMIT
来让效果被持久化。 - 也可通过
BEGIN
开始一个事务,此时autocommit
隐式地被关闭了,因此事务操作过程中也是需要显式调用COMMIT
来让效果永久生效。 BEGIN
开启事务后,使用COMMIT
或ROLLBACK
来结束该事务。事务结束后autocommit
回到原有的状态。
所以,autocommit
这个开关相当于一个记录的事务标记,它被关闭时你一直处于一个可回滚的状态。而 BEGIN
开启的是一次临时事务,一旦 COMMIT
或 ROLLBACK
本次事务便结束了。