zoukankan      html  css  js  c++  java
  • 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 语句来开始一个事务,也可以使用别名 BEGINBEGIN WORK 语句。
    • COMMIT 语句提交修改。
    • 通过 ROLLBACK 语句回滚。

    其中 COMMITROLLBACK 均可用来结束一个事务。

    来看一个简单的示例:

    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)

    通过设置 autocommit0 可关闭自动提交,

    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;
          });
        }
    
    <span class="pl-k">var</span> log <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>Post <span class="pl-pds">'</span></span> <span class="pl-k">+</span> <span class="pl-smi">results</span>.<span class="pl-smi">insertId</span> <span class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">'</span> added<span class="pl-pds">'</span></span>;
    
    <span class="pl-smi">connection</span>.<span class="pl-en">query</span>(<span class="pl-s"><span class="pl-pds">'</span>INSERT INTO log SET data=?<span class="pl-pds">'</span></span>, log, <span class="pl-k">function</span> (<span class="pl-smi">error</span>, <span class="pl-smi">results</span>, <span class="pl-smi">fields</span>) {
      <span class="pl-k">if</span> (error) {
        <span class="pl-k">return</span> <span class="pl-smi">connection</span>.<span class="pl-en">rollback</span>(<span class="pl-k">function</span>() {
          <span class="pl-k">throw</span> error;
        });
      }
      <span class="pl-smi">connection</span>.<span class="pl-en">commit</span>(<span class="pl-k">function</span>(<span class="pl-smi">err</span>) {
        <span class="pl-k">if</span> (err) {
          <span class="pl-k">return</span> <span class="pl-smi">connection</span>.<span class="pl-en">rollback</span>(<span class="pl-k">function</span>() {
            <span class="pl-k">throw</span> err;
          });
        }
        <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>success!<span class="pl-pds">'</span></span>);
      });
    });
    

    });
    });

    总结

    关于 autocommit 与事务,他们其实是这样的关系:

    • MySQL 每一步操作都可看成一个原子操作。
    • 默认情况下,autocommit 是开启状态,所以第一条语句都是执行后自动提交,语句产生的效果被记录保存了下来。
    • 关于 autocommit 后,语句不会自动提交,需要手动调用 COMMIT 来让效果被持久化。
    • 也可通过 BEGIN 开始一个事务,此时 autocommit 隐式地被关闭了,因此事务操作过程中也是需要显式调用 COMMIT 来让效果永久生效。
    • BEGIN 开启事务后,使用 COMMITROLLBACK 来结束该事务。事务结束后 autocommit 回到原有的状态。

    所以,autocommit 这个开关相当于一个记录的事务标记,它被关闭时你一直处于一个可回滚的状态。而 BEGIN 开启的是一次临时事务,一旦 COMMITROLLBACK 本次事务便结束了。

    相关资源

  • 相关阅读:
    CodeSmith快速向导
    生活忠告
    MSF 组队模型
    Javascript 全面理解 caller,callee,call,apply
    Sys.WebForms.PageRequestManagerServerErrorException(status code 500 OR 12031)
    经典问题:向setTimeout传递函数参数
    [转]JavaScript面向对象的特性
    数据库设计的体会
    Geomedia性能
    关于在SVG中如何实现gif动画的问题?
  • 原文地址:https://www.cnblogs.com/Wayou/p/mysql_transaction.html
Copyright © 2011-2022 走看看