MySQL事務細枝末節總結與分析[更新]

奕鵬發表於2021-03-26

事務基礎路線

m_5d609cce116fe7465ba28dd3ebac1443_r

事務定義

事務就是一組 DML 語句的集合。事務保證了對資料庫中資料的一致性操作。

儲存引擎

在日常開發中,我們常用的儲存引擎有 InnoDB 和 MyISAM 兩種儲存引擎。然而 MyISAM 是不支援事務操作的。

事務的提交方式

示例程式碼

CREATE TABLE `user`  (
  `id` int(10) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT,
  `name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `age` int(11) NULL DEFAULT 0,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;

mysql root@127.0.0.1:demo> desc user;
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id    | int(10) unsigned | NO   | PRI | <null>  |       |
| name  | varchar(10)      | NO   |     | <null>  |       |
| age   | int(11)          | YES  |     | 0       |       |
+-------+------------------+------+-----+---------+-------+
3 rows in set
Time: 0.012s

-- 插入語句
insert into `user`(`id`,`name`, `age`) values 
(1,'張三', 12),(2,'李四', 13),(3,'王五',14),(4,'趙六',15);

MySQL 中的事務預設採用的是自動提交,即一條 SQL 語句就是一個事務。預設提交方式可以通過引數 auto_commit 引數進行設定。MySQL 中的事務提交方式主要分為手動提交和自動提交方式。隱式提交屬於自動提交的一種特殊方式。
m_b74aaf1f08fe6ad3e6dd0813d36cf7d4_r

自動提交

下面的 SQL 語句,針對 user 表做一個 update 操作,當MySQL 執行這條 SQL 語句之後,資料就進行了持久化儲存,資料就不能進行回滾了,除非我們手動的 update 回原來的資料。

// 執行 update 語句之前表資料
mysql root@127.0.0.1:demo> select * from user;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1  | 張三 | 12  |
| 2  | 李四 | 13  |
| 3  | 王五 | 14  |
| 4  | 趙六 | 15  |
+----+------+-----+
4 rows in set
Time: 0.009s
// 執行 update 語句
mysql root@127.0.0.1:demo> update `user` set name = '張三1' where id = 1;
Query OK, 1 row affected
Time: 0.004s
// 查詢update 語句之後的表資料
mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.010s

通過上面的示例程式碼,可以看出,在預設的情況下,我們執行一個 SQL 語句,就會自動的儲存到表中。

手動提交

手動處理事務的提交方式,需要用到commit 關鍵字。下面羅列幾個 MySQL 事務中需要涉及到的幾個關鍵詞。

begin: 開啟一個事務,也可以使用 start transaction,兩者都是等價的。

rollback: 回滾一個事務的操作 ,也可以使用 rollback work,兩者都是等價的。

commit: 提交一個事務的操作, 也可以使用 commit work,兩者都是等價的。

savepoint transactionId: 儲存一個事務點。

release transactionId: 釋放一個事務點。

rollback transactionId: 回滾到指定的事務點。

1.此時我們新建一個客戶端A,開啟一個事務操作。

// 開啟事務
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 執行 update 語句
mysql root@127.0.0.1:demo> update `user` set name = '張三' where id = 1;
Query OK, 1 row affected
Time: 0.002s
// 檢視當前事務修改結果
mysql root@127.0.0.1:demo> select * from user;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1  | 張三 | 12  |
| 2  | 李四 | 13  |
| 3  | 王五 | 14  |
| 4  | 趙六 | 15  |
+----+------+-----+
4 rows in set
Time: 0.010s

2.此時新建一個客戶端 B,開啟一個事務操作。

// 開啟一個事務
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 執行查詢語句
mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.010s

通過上面的對比,發現在客戶端 A 執行了一個 update 操作,事務內查詢id=1 的 name 值為張三,然而在客戶端 B 查詢到 id=1 的 name 值為張三 1。這就是事務之間的隔離性(可重複讀)。是因為客戶端 A 還沒執行 commit 操作。

隱式提交

這裡的隱式提交指的是事務的巢狀,就是一個事務裡面包含著另外的一個事務。當事務裡面在開啟事務時,第一個事務預設會執行 commit 操作。
1.此時我們新建一個客戶端A,開啟一個事務操作。

mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
mysql root@127.0.0.1:demo> update `user` set name = '張三1' where id = 1;
Query OK, 1 row affected
Time: 0.002s
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 執行 sql 查詢語句
mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.009s

此時在客戶端 A 的事務內又開啟了另外一個事務,執行 SQL 查詢語句後,發現資料是修改後的值。

2.此時新建一個客戶端 B,無需開啟事務。

mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.009s

從理論上分析,由於客戶端 A 的事務沒有提交,此時我們查詢到的資料不應該是事務內修改後的值,然而我們執行查詢時,發現資料表的值是修改後的值,這證明巢狀事務會自動提交(此時的事務隔離級別是可重複讀)。 如果你認為當前的事務隔離級別是未提交讀,那你可以嘗試關閉 MySQL 服務這極端的操作在來查詢資料,你的到的結果也是如此。

提交點

所謂的提交點,就是指在一個事務中,做一個類似於資料快照一樣的操作。在資料為提交之前,我們可以針對不同的資料快照進行回退。
程式碼演示:

// 開啟事務
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 檢視修改前的資料
mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.010s
// 建立一個 savepoint
mysql root@127.0.0.1:demo> savepoint trans_1;
Query OK, 1 row affected
Time: 0.002s
// 執行修改操作
mysql root@127.0.0.1:demo> update `user` set name = '張三1' where id = 1;
Query OK, 1 row affected
Time: 0.002s
mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.010s
// 回滾savepoint
mysql root@127.0.0.1:demo> rollback trans_1;
// 查詢資料
mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.010s

執行回滾快照之後,update 語句執行的修改操作被撤回了。

事務的四大特性

事務的四大特性分別指的是 ACID,只有具備這四大特性的資料庫,才可具備事務的操作。這四大特性分別是原子性、永續性、隔離性和永續性。
m_3a69f58f5631b0409070539742b63094_r

原子性

定義:原子性指的是事務操作具備原子操作,一個事務裡面的 SQL 操作要麼全部成功要麼全部失敗,不能存在一些 SQL 成功,一些 SQL 執行失敗。
場景:銀行轉賬,你給小明轉賬 100 塊,此時給小明的賬戶增加 100 塊是一個 SQL 語句,你賬戶上減少 100 塊是一個 SQL 語句,原子性就是指的這兩個 SQL 要全部成功。
示例:

update user_account set money = money - 100.00 where user_name = '自己';
update user_account set money = money + 100.00 where user_name = '小明';

m_7a3328aae26bb8f5d1f283eda63d6979_r

一致性

定義:一致性指的是事務操作前後必須滿足業務約束。
場景:銀行轉賬,你當前賬戶有 200 塊,小明有 100 塊。你給小明轉賬 100 塊之後,你賬戶有 100 塊,小明賬戶有 200 塊。此時不管是轉賬失敗還是成功,賬戶的總金額還是和轉賬之前的總金額保持一致的。
示例:

// 計算事務操作之前的總和
select sum(money) as sum_money from user_account where user_name = '自己' and user_name = '小明';
|-----sum_money-----|
|        300.00            |
update user_account set money = money - 100.00 where user_name = '自己';
update user_account set money = money + 100.00 where user_name = '小明';
// 計算事務操作之後的總和
select sum(money) as sum_money from user_account where user_name = '自己' and user_name = '小明';
|-----sum_money-----|
|        300.00            |

m_c1832dccc1d9fd64b16d6b3645e4902c_r

隔離性

定義:隔離性指的是多個事務之間是相互隔離的,事務之間是互不受影響的。但這種場景需要考慮事務的隔離級別。文章後面內容頁會針對事務之間的隔離級別做特別的演示。
場景:銀行轉賬,你當前賬戶存在 200 塊錢,你此時給小明轉賬 100 塊,同時你女朋友用你的賬戶在執行支付操作,這兩種場景屬於兩個事務操作,兩種操作是互不受影響的。
示例:

// 轉賬小明操作
update user_account set money = money - 100.00 where user_name = '自己';
// 女朋友支付操作
update user_account set money = money - 100.00 where user_name = '自己';

m_1b90a7f5777600d881fbe47a8e1fa17b_r

永續性

定義:永續性指的是事務一旦提交,就不能進行回滾(撤回),永久的儲存在磁碟中。
場景:銀行轉賬,你當前給小明轉賬 100,點選了確認操作,此時你後悔轉賬了,此時是沒法撤回的,因為這個操作已經被提交,也就表明小明賬戶上已經多 100 了。

// 開啟事務
begin;
update user_account set money = money - 100.00 where user_name = '自己';
update user_account set money = money + 100.00 where user_name = '小明';
// 提交操作
commit;
select money from user_account where user_name = '自己'; // 100.00
select money from user_account where user_name = '小明'; // 200.00
// 執行回滾
rollback
//此時金額是不發生改變
select money from user_account where user_name = '自己'; // 100.00
select money from user_account where user_name = '小明'; // 200.00

事務隔離級別

事務的隔離級別是針對兩個或兩個以上的事務之間是否相互是隔離的,這裡的隔離其實就是指的事務對資料做了操作,在事務在commit 之前,其他的未提交事務是否可以讀到相應修改的資料。大致邏輯如下圖:
m_b414510f123f598da1de713509cec751_r

1.當事務1 開啟時,資料表中的 id=1 的資料,age=2;

2.事務1執行 update 操作,將 id=1的資料,age 設定為 1;

3.此時在事務 1 還未提交時,開啟事務 2;

4.事務2 對資料表id=1 的資料進去 select;

5.由於事務 1 執行了 update 操作,當事務 2 去執行 select 操作時,是返回 age=1?還是 age=2?這就涉及到事務的隔離級別。

系統隔離級別

MySQ預設使用的可重複讀隔離級別。如下可以檢視 MySQL 事務隔離級別。

mysql root@127.0.0.1:demo> show variables like '%iso%';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
| tx_isolation          | REPEATABLE-READ |
+-----------------------+-----------------+
2 rows in set
Time: 0.011s

transaction_isolation是tx_isolation 的別名,推薦使用transaction_isolation選項,因為在 MySQL8 開始,tx_isolation將被廢棄。

未提交讀

定義:所謂的未提交讀,指的是當一個事務 A內對資料做了操作還沒有執行commit ,另外一個事務B是可以讀到事務A修改後的資料。如上圖中,事務二去做查詢時,返回的值就是 2。
設定隔離級別:

mysql root@127.0.0.1:demo> set session transaction isolation level READ UNCOMMITTED;
Query OK, 0 rows affected
Time: 0.002s
mysql root@127.0.0.1:demo> show variables like '%iso%';
+-----------------------+------------------+
| Variable_name         | Value            |
+-----------------------+------------------+
| transaction_isolation | READ-UNCOMMITTED |
| tx_isolation          | READ-UNCOMMITTED |
+-----------------------+------------------+
2 rows in set
Time: 0.010s

程式碼演示:
1.開始事務 A。

// 開啟事務前查詢資料
mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.010s
// 設定當前會話的隔離級別
mysql root@127.0.0.1:demo> set session transaction isolation level READ UNCOMMITTED;
Query OK, 0 rows affected
Time: 0.002s
// 開啟事務
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 執行修改操作
mysql root@127.0.0.1:demo> update `user` set name = '張三' where id = 1;
Query OK, 1 row affected
Time: 0.002s
// 查詢資料已被修改(但當前事務還未被提交)
mysql root@127.0.0.1:demo> select * from user;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1  | 張三 | 12  |
| 2  | 李四 | 13  |
| 3  | 王五 | 14  |
| 4  | 趙六 | 15  |
+----+------+-----+
4 rows in set
Time: 0.011s

2.開啟事務 B。

// 設定當前會話的隔離級別
mysql root@127.0.0.1:demo> set session transaction isolation level READ UNCOMMITTED;
Query OK, 0 rows affected
Time: 0.002s
// 開啟事務
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 執行 SQL 查詢
mysql root@127.0.0.1:demo> select * from user;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1  | 張三 | 12  |
| 2  | 李四 | 13  |
| 3  | 王五 | 14  |
| 4  | 趙六 | 15  |
+----+------+-----+
4 rows in set
Time: 0.011s

通過上面的事務 A 和事務 B 可以看得出,在事務 A 還沒提交時,事務 B 就能讀取到事務A 修改後的資料。在演示程式碼時,需要注意的是,需要在事務 A 開啟之後執行 update 操作之前,將事務 B 開啟。

提交讀

定義:所謂的提交讀,指的是當一個事務 A內對資料做了操作並且提交了,另外一個事務B在還未提交時,是可以讀到事務A修改後的資料。如上圖中,事務二去做查詢時,如果此時事務一已經執行了 commit,此時返回的結果仍是 2。
設定隔離級別:

mysql root@127.0.0.1:demo> set session transaction isolation level READ COMMITTED;
Query OK, 0 rows affected
Time: 0.002s

程式碼演示:
1.開始事務 A。

mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.011s
// 設定當前會話的事務隔離級別
mysql root@127.0.0.1:demo> set session transaction isolation level READ COMMITTED;
Query OK, 0 rows affected
Time: 0.002s
// 開啟事務
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 執行修改操作
mysql root@127.0.0.1:demo> update `user` set name = '張三' where id = 1;
Query OK, 1 row affected
Time: 0.002s
// 提交事務
mysql root@127.0.0.1:demo> commit;
Query OK, 0 rows affected
Time: 0.003s
// 檢視事務執行結果
mysql root@127.0.0.1:demo> select * from user;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1  | 張三 | 12  |
| 2  | 李四 | 13  |
| 3  | 王五 | 14  |
| 4  | 趙六 | 15  |
+----+------+-----+
4 rows in set
Time: 0.011s

2.開啟事務 B。

// 設定當前會話的事務隔離級別
mysql root@127.0.0.1:demo> set session transaction isolation level READ COMMITTED;
Query OK, 0 rows affected
Time: 0.002s
// 開啟事務
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 檢視資料(此時事務 A 一定要處於未執行  commit 語句)
mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.011s
// 再次檢視資料(此時事務 A 一定要處於執行 commit 語句)
mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.011s

通過上面的示例程式碼,可以看出,當事務 A 提交之後,事務 B 內就可以讀到修改後的資料。記住在事務 B 內執行 select 語句時,事務 A 的提交狀態。

可重複讀

定義:所謂的可重複讀,指的是當一個事務 A內對資料做了操作並且提交了,另外一個事務B在還未提交時,讀到的資料永遠是事務B開始時的狀態,是不會讀取到事務 A 提交後的資料。如上圖中,事務二去做查詢時,如果此時事務一已經執行了 commit,此時返回的結果是 1。
設定隔離級別:

mysql root@127.0.0.1:demo> set session transaction isolation level REPEATABLE-READ;
Query OK, 0 rows affected
Time: 0.002s

1.開啟事務 A。

mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.011s
// 設定當前會話的事務隔離級別
mysql root@127.0.0.1:demo> set session transaction isolation level REPEATABLE-READ;
Query OK, 0 rows affected
Time: 0.002s
// 開啟事務
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 執行修改操作
mysql root@127.0.0.1:demo> update `user` set name = '張三' where id = 1;
Query OK, 1 row affected
Time: 0.002s
// 提交事務
mysql root@127.0.0.1:demo> commit;
Query OK, 0 rows affected
Time: 0.003s
// 檢視事務執行結果
mysql root@127.0.0.1:demo> select * from user;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1  | 張三 | 12  |
| 2  | 李四 | 13  |
| 3  | 王五 | 14  |
| 4  | 趙六 | 15  |
+----+------+-----+
4 rows in set
Time: 0.011s

2.開啟事務 B。

mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
+----+-------+-----+
4 rows in set
Time: 0.011s
// 設定當前會話的事務隔離級別
mysql root@127.0.0.1:demo> set session transaction isolation level REPEATABLE-READ;
Query OK, 0 rows affected
Time: 0.002s
// 開啟事務
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 執行查詢操作
mysql root@127.0.0.1:demo> select * from user;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四 | 13  |
| 3  | 王五 | 14  |
| 4  | 趙六 | 15  |
+----+------+-----+
4 rows in set
Time: 0.011s
// 提交事務
mysql root@127.0.0.1:demo> commit;
Query OK, 0 rows affected
Time: 0.003s
// 檢視事務執行結果
mysql root@127.0.0.1:demo> select * from user;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1  | 張三 | 12  |
| 2  | 李四 | 13  |
| 3  | 王五 | 14  |
| 4  | 趙六 | 15  |
+----+------+-----+
4 rows in set
Time: 0.011s

通過上面的示例程式碼,可以看出,當事務 A 提交之後,事務 B 內是不可以讀到修改後的資料,當事務 B 提交之後(事務 B 沒錯 DML 語句操作),但是查詢到資料和事務 A 提交後的結果一致。

序列

定義:所謂的序列,指的是當一個事務 A內對資料做了操作並且處於未提交狀態,另外一個事務B在去運算元據時,是不會被立馬執行的,而是需要等到事務 Acommit 之後,才可以進行資料操作。如上圖中,事務二去做查詢時,如果此時事務一處於未提交狀態,事務二會一直處於等待狀態,直到事務 A commit 之後返回結果值 2。
設定隔離級別:

set session transaction isolation level serializable;
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s

程式碼演示:
1.開始事務 A

// 設定當前會話的隔離級別
mysql root@127.0.0.1:demo>set session transaction isolation level serializable;
// 開啟事務
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 執行修改
mysql root@127.0.0.1:demo> update `user` set name = '張三1' where id = 1;
Query OK, 1 row affected
Time: 0.002s
// 提交事務
mysql root@127.0.0.1:demo> commit;
Query OK, 0 rows affected
Time: 0.004s
// 檢測修改
mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
| 5  |QI  | 16  |
+----+-------+-----+
5 rows in set
Time: 0.009s

2.開始事務 B

// 設定當前會話的隔離級別
mysql root@127.0.0.1:demo>set session transaction isolation level serializable;
// 開啟事務
mysql root@127.0.0.1:demo> begin;
Query OK, 0 rows affected
Time: 0.002s
// 執行查詢(此時事務 A 一定要還未執行 commit 命令)
mysql root@127.0.0.1:demo> select * from user;
+----+-------+-----+
| id | name  | age |
+----+-------+-----+
| 1  | 張三1 | 12  |
| 2  | 李四  | 13  |
| 3  | 王五  | 14  |
| 4  | 趙六  | 15  |
| 5  |QI  | 16  |
+----+-------+-----+
5 rows in set
Time: 14.159s(查詢消耗時間,是由於 sessionA處於阻塞過程中)

此時一定要特別注重事務 B 在執行 select 語句所用的時間,這裡為什麼是 14s 多,就是因為在這段時間中,事務 A 還未處於commit 狀態,因此事務 B 在執行查詢時,一直處於等待狀態,直到事務 A 進行了 commit 操作。

隔離級別的幾種現象

事務隔離級別 髒讀 不可重複度 幻讀 加鎖度
未提交讀 ×
提交讀 × ×
可重複讀 × × ×
序列 × × ×

髒讀:指的是事務處於未提交狀態,對資料做了操作,其他的事務是可以讀取到當前事務所做的操作。
m_eae303d7c16a2c08f4e677ed9669b6fe_r

不可重複度:一個事務 A對資料做了操作並進行了提交。其他事務對於事務A在提交前和提交後讀到的資料不一致。側重(update/delete)語句。
m_d90d7261c949b2246ea45306e99d1f7f_r

幻讀:一個事務 A對資料做了操作並進行了提交。其他事務對於事務A在提交前和提交後讀到的資料不一致。側重(insert)語句。
m_8e8d6bbb66be3ace59b55a900484f2e9_r

可重複讀:事務 B 在提交前,不管事務 A 是否提交,讀到的資料永遠是事務 B 開始時的狀態。
m_ae96c565a0fabd92412f56ef2d6ff057_r

隔離級別優缺點分析

a.通過上面幾種情況的分析,可重複讀的隔離級別是最優的選擇。真正做到了事務之間是相互隔離,避免了髒讀、不可重複讀和幻讀的問題。

b.序列是最大程度上保證了資料的一致性,但是屬於枷鎖處理,併發性不高。

c.未提交讀和不可重複度,雖然存在資料不一致的情況,但效率更高,適合用在對資料一致性要求不高的場景中。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章