目錄
一、什么是事務
(一)概念
(二)事務的四大屬性
(三)事務的作用
(四)事務的提交方式
二、事務的啟動、回滾與提交
(一)事務的啟動、回滾與提交
(二)特殊情況
1、未 commit 事務,客戶端崩潰,MySQL將自動回滾
2、commit 后,客戶端崩潰,插入數據不受影響
3、手動開啟事務不受自動提交事務影響
4、自動提交事務對單條?SQL 語句的影響
5、結論
三、事務的隔離級別
(二)事務的隔離級別
(三)四種隔離級別詳解
1、讀未提交
2、讀已提交
3、可重復讀
4、串行化
一、什么是事務
(一)概念
????????MySQL 事務是指數據庫管理系統執行過程中的一個邏輯單位,由一個有限的數據庫操作序列構成,這些操作要么全部執行成功,要么全部不執行,是一個不可分割的工作單位。事務主要用于保證數據的一致性和完整性,特別是在需要多個操作同時成功或同時失敗的場景中,比如銀行轉賬、訂單處理等。
(二)事務的四大屬性
? ? ? ? 事務的特性:
- 原子性:事務中的所有操作要么全部完成,要么全部不完成,不會結束在中間某個狀態;
- 一致性:事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態;
- 隔離性:多個事務并發執行時,一個事務的執行不會影響其他事務;
- 持久性:一旦事務提交,其結果就是永久性的,即使系統崩潰也不會丟失。
(三)事務的作用
? ? ? ? 事務的出現是為了簡化程序開發時可能需要考慮的多種細節問題。例如:當銀行賬戶 A 向銀行賬戶 B 發起轉賬,首先需要現在 A 中扣除目標金額后再向B中添加目標金額,假如在扣除A賬戶的金額后出現了網絡異常導致轉賬失敗,這時的正常情況應該是賬戶A上返回了目標金額,事務的出現就使得該操作可以由 MySQL 自動完成而不需要程序員特殊處理。
(四)事務的提交方式
? ? ? ? 事務的提交方式分為自動提交和手動提交。在先前學習MySQL語句時并沒有對事務進行過特殊操作,這是因為 MySQL 默認設置自動提交。也就是每當執行一條語句后 MySQL自動將該語句進行事務的提交。
? ? ? ? 查看事務自動提交方式:
//MySQl 默認打開自動提交
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.02 sec)
//設置自動提交事務關閉
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | OFF |
+---------------+-------+
1 row in set (0.00 sec)
二、事務的啟動、回滾與提交
? ? ? ? 為方便演示,需關閉事務自動提交、將事務的隔離級別設為最低并準備一個測試表:
//打開事務自動提交
mysql> set autocommit=1;
Query OK, 0 rows affected (0.00 sec)mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)
//設置隔離級別(需重啟終端)
mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
//創建測試表
mysql> create table test(-> id int primary key,-> name varchar(20) not null,-> salary decimal(10,2) default 0.0-> );
Query OK, 0 rows affected (0.02 sec)
(一)事務的啟動、回滾與提交
//手動開啟事務 (手動開始事務后該事務不受自動提交影響)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
//保存點1
mysql> savepoint save1;
Query OK, 0 rows affected (0.00 sec)mysql> insert into test values(1,'張三',5000);
Query OK, 1 row affected (0.00 sec)
//保存點2
mysql> savepoint save2;
Query OK, 0 rows affected (0.00 sec)mysql> insert into test values(2,'李四',4500);
Query OK, 1 row affected (0.00 sec)
//此時有兩條記錄
mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
| 2 | 李四 | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)
//回滾至保存點2
mysql> rollback to save2;
Query OK, 0 rows affected (0.00 sec)
//此時變為一條記錄
mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
+----+--------+---------+
1 row in set (0.00 sec)
//回滾至最開始
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
//無記錄
mysql> select * from test;
Empty set (0.00 sec)
? ? ? ? 由上述結果可以看出,手動開啟事務后將不受自動提交的影響。以上便是手動開啟事務以及回滾操作。
(二)特殊情況
? ? ? ? 以下情況都是最低隔離級別下的操作(讀未提交),為方便說明開啟兩個終端進行演示:
1、未 commit 事務,客戶端崩潰,MySQL將自動回滾
//客戶端A
mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> insert into test values(1, '張三', 5000);
Query OK, 1 row affected (0.00 sec)mysql> insert into test values(2, '李四', 4500);
Query OK, 1 row affected (0.00 sec)mysql> Aborted
[X@centos-414 ~]$ //客戶端B
mysql> select * from test;
Empty set (0.00 sec)mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
+----+--------+---------+
1 row in set (0.00 sec)mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
| 2 | 李四 | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)mysql> select * from test;
Empty set (0.01 sec)
2、commit 后,客戶端崩潰,插入數據不受影響
//客戶端A
mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> insert into test values(1, '張三', 5000);
Query OK, 1 row affected (0.00 sec)mysql> insert into test values(2, '李四', 4500);
Query OK, 1 row affected (0.00 sec)mysql> commit;
Query OK, 0 rows affected (0.00 sec)mysql> Aborted//客戶端B
mysql> select * from test;
Empty set (0.00 sec)mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
+----+--------+---------+
1 row in set (0.00 sec)mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
| 2 | 李四 | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
| 2 | 李四 | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)
3、手動開啟事務不受自動提交事務影響
//客戶端A
mysql> set autocommit=1;
Query OK, 0 rows affected (0.00 sec)mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> insert into test values(3, '王五', 5500);
Query OK, 1 row affected (0.00 sec)mysql> Aborted//客戶端B
mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
| 2 | 李四 | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
| 2 | 李四 | 4500.00 |
| 3 | 王五 | 5500.00 |
+----+--------+---------+
3 rows in set (0.00 sec)mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
| 2 | 李四 | 4500.00 |
+----+--------+---------+
2 rows in set (0.01 sec)
4、自動提交事務對單條?SQL 語句的影響
//客戶端A
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | OFF |
+---------------+-------+
1 row in set (0.00 sec)mysql> insert into test values(3, '王五', 5500);
Query OK, 1 row affected (0.00 sec)mysql> Aborted//客戶端B
mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
| 2 | 李四 | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
| 2 | 李四 | 4500.00 |
| 3 | 王五 | 5500.00 |
+----+--------+---------+
3 rows in set (0.00 sec)mysql> select * from test;
+----+--------+---------+
| id | name | salary |
+----+--------+---------+
| 1 | 張三 | 5000.00 |
| 2 | 李四 | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)
5、結論
- 只要輸入 begin 或者 start transaction,事務就必須通過 commit 提交才會持久化,與是否設置自動提交無關;
- 對于 InnoDB 每一條 SQL 語言都默認封裝成事務,自動提交。(select有特殊情況,因為 MySQL 有 MVCC );
- 如果沒有設置保存點,也可以回滾,只能回滾到事務的開始。直接使用 rollback(前提是事務還沒有提交)
- 如果一個事務被提交了則無法回退;
- InnoDB 支持事務, MyISAM 不支持事務
三、事務的隔離級別
(一)什么是隔離性
????????事務的隔離性是數據庫事務的四大特性之一,它確保并發執行的多個事務相互獨立,一個事務的操作不會被其他事務干擾,從而避免數據不一致問題。隔離性通過不同的隔離級別來控制事務之間的可見性和影響程度。
? ? ? ? 查看隔離級別:
//查看全局隔離級別(一般默認為REPEATABLE-READ)
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED |
+-----------------------+
1 row in set, 1 warning (0.01 sec)
//查看此次會話隔離級別(一般開啟MySQL客戶端后該值由全局隔離級別進行初始化)
mysql> select @@session.tx_isolation;
+------------------------+
| @@session.tx_isolation |
+------------------------+
| READ-UNCOMMITTED |
+------------------------+
1 row in set, 1 warning (0.00 sec)
//查看此次會話隔離級別
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set, 1 warning (0.00 sec)
? ? ? ? 在使用MySQL客戶端時的隔離級別由會話隔離級別等級決定。
? ? ? ? 設置隔離級別:
//設置會話隔離級別
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED |
+-----------------------+
1 row in set, 1 warning (0.00 sec)mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE |
+----------------+
1 row in set, 1 warning (0.00 sec)
(二)事務的隔離級別
? ? ? ? SQL標準定義了四種隔離級別:
- 讀未提交
允許事務讀取其他事務未提交的數據,可能出現臟讀、不可重復讀和幻讀的情況;- 讀已提交
只允許讀取已提交的數據,避免臟讀,但存在不可重復讀和幻讀的情況;- 可重復讀
確保同一事務多次讀取同一數據結果的一致性,可能會出現幻讀的情況;- 串行化
最高隔離的級別,事務完全串行化,可避免所有并發問題,但性能低。
(三)四種隔離級別詳解
1、讀未提交
????????在多個并行的會話中啟動事務,一個事務在改動數據庫哪怕沒有commit提交,其他事務也是能夠實時的看到它修改的數據。一個事務在執行中,讀到另一個執行中事務的更新(或其他操作)但是未commit的數據,這種不合理的現象叫做臟讀(dirty read)。
? ? ? ? 上圖可以看出,即使左端事務沒有進行提交操作,但右端事務仍可以看到表的操作,這就是臟讀。
2、讀已提交
????????事務A在commit提交事務之前,所做的修改是不會被其他事務看到的,一旦事務A發起commit之后,其他事務就能看到事務A對數據的修改。這就造成了其他事務在不同的時間點select查看數據庫時,會查到不同的數據。這種現象叫不可重復讀。
? ? ? ? 上圖可知,只要事務的操作被提交,那么其他事務可以查看到該事務的插入操作,這就會導致其他事務對同一表的查詢結果可能會發生變化,這就是不可重復讀。
3、可重復讀
????????可重復讀是MySQL默認的隔離級別。
? ? ? ? 上圖可知,即使事務的操作被提交,其他事務仍然無法查看到該事務對表的操作,只要其他事務也提交以后才能查看到其他事務對表的操作。
4、串行化
????????串行化就是對所有事務進行加鎖,事務的執行(一般對查詢操作不進行加鎖)全部挨個排隊,這就導致了效率低下問題。
????????開啟事務A和事務B,兩個事務同時select讀取將使用共享鎖,不會串行化;事務A中有更新等操作,會阻塞A,直到事務B提交。如果事務A阻塞時間過長,將會由于鎖等待超時退出當前事務。