## 測試環境 mysql> select version(); +------------+ | version() | +------------+ | 5.7.11-log | +------------+
數據庫事務特性 ACID,即html
A(Atomicity) -原子性mysql
C(Consistency)- 一致性sql
I(Isolation) - 隔離性數據庫
D(Durability) - 持久性session
MySQL 提供了 4 種不一樣的隔離級別,用來支持多版本併發控制(MVCC,Multi-Version Concurrency Control)。併發
默認的事務隔離級別是 REPEATABLE-READ(可重讀):測試
mysql> select @@global.tx_isolation, @@session.tx_isolation; +-------------------------+---------------------------+ | @@global.tx_isolation | @@session.tx_isolation | +-------------------------+---------------------------+ | REPEATABLE-READ | REPEATABLE-READ | +-------------------------+---------------------------+
在該事務級別下,一個事務期間內,該事務不考慮其餘提交語句。spa
1. 建立測試表rest
mysql> CREATE TABLE `transaction_test` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `val` varchar(20) NOT NULL, `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1
2. 開啓兩個 MySQL 客戶端進行測試code
step 1:
在 Client 1 下開啓事務,查詢測試表中的數據:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from transaction_test; Empty set (0.00 sec)
step 2:
在 Client 2 下開啓事務,而且往測試表中插入數據,但不提交事務:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into transaction_test (val) values ('x'),('y'),('z'); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> select * from transaction_test; +----+-----+---------------------+ | id | val | created | +----+-----+---------------------+ | 1 | x | 2017-02-06 00:20:59 | | 2 | y | 2017-02-06 00:20:59 | | 3 | z | 2017-02-06 00:20:59 | +----+-----+---------------------+ 3 rows in set (0.00 sec)
step 3:
在 Client 1 下查看錶中數據:
mysql> select * from transaction_test; Empty set (0.00 sec)
仍然是空表。
step 4:
Client 2 提交事務:
mysql> commit; Query OK, 0 rows affected (0.12 sec)
step 5:
Client 1 下查看錶中數據:
mysql> select * from transaction_test; Empty set (0.00 sec)
任然是空表。
step 6:
Client 1 提交事務,查看錶中數據:
mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from transaction_test; +----+-----+---------------------+ | id | val | created | +----+-----+---------------------+ | 1 | x | 2017-02-06 00:20:59 | | 2 | y | 2017-02-06 00:20:59 | | 3 | z | 2017-02-06 00:20:59 | +----+-----+---------------------+ 3 rows in set (0.00 sec)
當 Client 1 完成事務後,才能看到其餘事務提交的數據。
step 1:
Client 1 中清空表,改變數據庫隔離級別:
mysql> truncate table transaction_test; Query OK, 0 rows affected (0.10 sec) mysql> set @@session.tx_isolation = 'READ-COMMITTED'; Query OK, 0 rows affected (0.00 sec) mysql> select @@global.tx_isolation, @@session.tx_isolation; +-----------------------+------------------------+ | @@global.tx_isolation | @@session.tx_isolation | +-----------------------+------------------------+ | REPEATABLE-READ | READ-COMMITTED | +-----------------------+------------------------+ 1 row in set (0.00 sec)
step 2:
Client 1 開啓事務,查詢表中數據:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from transaction_test; Empty set (0.00 sec)
step 3:
Client 2 開啓事務,向表中插入數據,但不提交事務:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into transaction_test (val) values ('x'),('y'),('z'); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> select * from transaction_test; +----+-----+---------------------+ | id | val | created | +----+-----+---------------------+ | 1 | x | 2017-02-06 00:31:00 | | 2 | y | 2017-02-06 00:31:00 | | 3 | z | 2017-02-06 00:31:00 | +----+-----+---------------------+ 3 rows in set (0.00 sec)
step 4:
Client 1 下查看錶中數據:
mysql> select * from transaction_test; Empty set (0.00 sec)
仍然是空表。
step 5:
Client 2 提交事務:
mysql> commit; Query OK, 0 rows affected (0.13 sec)
step 6:
Client 1 下查看錶中數據:
mysql> select * from transaction_test; +----+-----+---------------------+ | id | val | created | +----+-----+---------------------+ | 1 | x | 2017-02-06 00:31:00 | | 2 | y | 2017-02-06 00:31:00 | | 3 | z | 2017-02-06 00:31:00 | +----+-----+---------------------+ 3 rows in set (0.00 sec)
與 REPEATABLE-READ 不一樣的是,Client 1 沒有結束事務也能看到其餘事務提交的數據。
step 1:
Client 1 下清空表,設置隔離級別:
mysql> truncate table transaction_test; Query OK, 0 rows affected (0.10 sec) mysql> set @@session.tx_isolation = 'READ-UNCOMMITTED'; Query OK, 0 rows affected (0.00 sec) mysql> select @@global.tx_isolation, @@session.tx_isolation; +-----------------------+------------------------+ | @@global.tx_isolation | @@session.tx_isolation | +-----------------------+------------------------+ | REPEATABLE-READ | READ-UNCOMMITTED | +-----------------------+------------------------+ 1 row in set (0.00 sec)
step 2:
Client 1 下開啓事務,查詢表數據:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from transaction_test; Empty set (0.00 sec)
step 3:
Client 2 下開啓事務,向表中插入數據,可是不提交事務:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into transaction_test (val) values ('x'),('y'),('z'); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> select * from transaction_test; +----+-----+---------------------+ | id | val | created | +----+-----+---------------------+ | 1 | x | 2017-02-06 00:43:59 | | 2 | y | 2017-02-06 00:43:59 | | 3 | z | 2017-02-06 00:43:59 | +----+-----+---------------------+ 3 rows in set (0.00 sec)
step 4:
Client 1 中查詢數據:
mysql> select * from transaction_test; +----+-----+---------------------+ | id | val | created | +----+-----+---------------------+ | 1 | x | 2017-02-06 00:43:59 | | 2 | y | 2017-02-06 00:43:59 | | 3 | z | 2017-02-06 00:43:59 | +----+-----+---------------------+ 3 rows in set (0.00 sec)
與 READ-COMMITTED 不一樣的是,在 Client 2 不提交事務的狀況下,Client 1 也能讀到其餘事務插入的數據,即髒數據或者說產生了「髒讀」。在一個事務期間讀到了另外一個事務在未提交以前產生的數據,那麼第一個事務就讀到了髒數據,產生了對第二個事務未提交數據的依賴,若是第二個事務回滾,那麼第一個事務讀到的數據是錯誤的髒數據。
「髒讀」與「幻讀」、「不可重複讀」的區別是:幻讀是讀取結果集條數的對比,一個事務按相同的查詢條件查詢以前檢索過的數據,發現檢索出來的結果集條數變多或者減小(由其餘事務插入、刪除的),相似產生幻覺。
不可重複讀是讀取的數據自己的對比,一個事務在讀取某些數據後的一段時間後,再次讀取這個數據,發現其讀取出來的數據內容已經發生了改變,就是不可重複讀。
step 5:
Client 2 回滾事務:
mysql> rollback; Query OK, 0 rows affected (0.04 sec) mysql> select * from transaction_test; Empty set (0.00 sec)
step 6:
Client 1 查詢表數據:
mysql> select * from transaction_test; Empty set (0.00 sec)
空表。
step 1:
Client 1 下清空表,設置隔離級別:
mysql> truncate table transaction_test; Query OK, 0 rows affected (0.21 sec) mysql> set @@session.tx_isolation ='SERIALIZABLE'; Query OK, 0 rows affected (0.00 sec) mysql> select @@global.tx_isolation, @@session.tx_isolation; +-----------------------+------------------------+ | @@global.tx_isolation | @@session.tx_isolation | +-----------------------+------------------------+ | REPEATABLE-READ | SERIALIZABLE | +-----------------------+------------------------+ 1 row in set (0.00 sec)
step 2:
Client 1 開啓事務,查詢表:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from transaction_test; Empty set (0.00 sec)
step 3:
Client 2 開啓事務,向表中插入數據:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into transaction_test (val) values ('x'),('y'),('z');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
此時 Client 2 插入數據(INSERT 操做)會被阻塞,直到第一個(Client 1)事務提交後,Client 2 的插入操做才能完成。
step 4:
Client 1 提交事務:
mysql> commit; Query OK, 0 rows affected (0.00 sec)
step 5:
Client 2 插入數據:
mysql> insert into transaction_test (val) values ('x'),('y'),('z'); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> select * from transaction_test; +----+-----+---------------------+ | id | val | created | +----+-----+---------------------+ | 4 | x | 2017-02-06 00:54:17 | | 5 | y | 2017-02-06 00:54:17 | | 6 | z | 2017-02-06 00:54:17 | +----+-----+---------------------+ 3 rows in set (0.00 sec)
參考: