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 本次事務便結束了。

相關資源


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM