mysql事務

測試環境:centos6.8_x86_64 mysql-5.6.28

主要關注的詞:
Mysql|Innodb |transaction |ACID |read ucommited | read commited |repeatable read |serializable |髒讀 |不可重複讀|幻讀

一. mysql事務的基本要素:
1、原子性(Atomicity):事務開始後所有操作,要麼全部做完,要麼全部不做,不可能停滯在中間環節。事務執行過程中出錯,會回滾到事務開始前的狀態,所有的操作就像沒有發生一樣。也就是說事務是一個不可分割的整體,就像化學中學過的原子,是物質構成的基本單位。
2、一致性(Consistency):事務開始前和結束後,數據庫的完整性約束沒有被破壞 。
3、隔離性(Isolation):同一時間,只允許一個事務請求同一數據,不同的事務之間彼此沒有任何干擾。
4、持久性(Durability):事務完成後,事務對數據庫的所有更新將被保存到數據庫,不能回滾。

二. 事務併發導致的問題:

  1. 髒讀(Dirty Read):所有事務都可以看到其他未提交事務的執行結果。
  2. 不可重複讀(Nonrepeatable Read):一個事務只能看見已經提交事務所做的改變。
  3. 幻讀(Phantom Read):它確保同一事務的多個實例在併發讀取數據時,會看到同樣的數據行。

三. SQL定義的事務的4種隔離級別
這裏以幾個例子來說明mysql事務的4種隔離級別:

  1. Read Uncommitted(讀未提交)
    (1)所有事務都可以看到其他未提交事務的執行結果
    (2)本隔離級別很少用於實際應用,因爲它的性能也不比其他級別好多少
    (3)引發的問題是——髒讀(Dirty Read):讀取到了未提交的數據

mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 200 |
| 2 | tom | 300 |
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

session A上執行:
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

mysql> set tx_isolation = 'READ-UNCOMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> update t6 set sal = sal + 50 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 300 |
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

然後session B上執行:
mysql> set tx_isolation = 'READ-UNCOMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 | -----------A未提交事務,B可以查到修改的數據
| 2 | tom | 300 |
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

然後session A上回滾操作:
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 200 |
| 2 | tom | 300 |
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

再在session B上查看:
mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 200 |
| 2 | tom | 300 |
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

鎖情況:
在session A上:
mysql> start transaction ;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t6 where id = 1 for update;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
+----+----------+------+
1 row in set (0.00 sec)

然後session B上:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t6 where id = 1 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

A上commit後,B上正常更新。可見上了行鎖(注意理解innodb行鎖實現的原理,否則可能還是會上表鎖的);

2 . Read Committed(讀已提交)
(1)這是大多數數據庫系統的默認隔離級別(但不是MySQL默認的)
(2)它滿足了隔離的簡單定義:一個事務只能看見已經提交事務所做的改變
(3)這種隔離級別出現的問題是——不可重複讀(Nonrepeatable Read):不可重複讀意味着我們在同一個事務中執行完全相同的select語句時可能看到不一樣的結果。

Session A上執行:
mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 300 |
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

mysql> set tx_isolation = 'read-committed';
Query OK, 0 rows affected (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> update t6 set sal = sal + 60 where id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 360 |
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)
Session B上執行:
mysql> set tx_isolation = 'read-committed';
Query OK, 0 rows affected (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 300 | -----查詢不到A事務對數據的修改,因爲A事務沒提交
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

然後session A上執行:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

再在session B上查看:
mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 360 | ----A事務提交後,B事務就能看到修改的記錄
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

3.Repeatable Read(可重讀)
(1)這是MySQL的默認事務隔離級別
(2)它確保同一事務的多個實例在併發讀取數據時,會看到同樣的數據行
(3)此級別可能出現的問題——幻讀(Phantom Read):當用戶讀取某一範圍的數據行時,另一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的數據行時,會發現有新的“幻影” 行
(4)InnoDB和Falcon存儲引擎通過多版本併發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題

Session A上操作:
mysql> set tx_isolation = 'repeatable-read';
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 360 |
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> update t6 set sal = sal + 60 where id =2 ;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 420 |
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

Session B 上執行:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 360 | -----查詢不到A事務對數據的修改,A事務沒提交
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

可以看到A事務沒提交, B事務上查詢不到A事務對數據的修改,那麼A事務提交後,B事務是否能查詢到呢?
session A 上操作:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

Session B上操作:
mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 360 | A事務提交了,B事務還是查不到修改的記錄(幻讀)
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

那麼B事務提交後,看是否能查看?
Session B 上執行:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 420 | B提交後,再查詢,可以看到修改的記錄
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

4.Serializable(可串行化)
(1)這是最高的隔離級別
(2)它通過強制事務順序執行,從而解決幻讀問題。當一個事務有更新操作時,其它事務只有等待這個事務提交後才能進行讀、寫操作。
(3)在這個級別,可能導致大量的超時現象和鎖競爭
Session A 上執行:
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE |
+----------------+
1 row in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> update t6 set sal=sal+60 where id = 2;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 480 |
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

Session B上執行:
mysql> set tx_isolation = 'serializable';
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE |
+----------------+
1 row in set (0.00 sec)

mysql> select from t6;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> update t6 set sal=sal+50 where id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
可以看到讀、寫都等待鎖。A事務commit提交後,B事務可以正常執行:
mysql> select
from t6;
+----+----------+------+
| id | name | sal |
+----+----------+------+
| 1 | zhangsan | 250 |
| 2 | tom | 480 |
| 3 | jim | 400 |
| 4 | liu | 500 |
+----+----------+------+
4 rows in set (0.00 sec)

mysql> update t6 set sal=sal+50 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql事務

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章