文章目錄
- 前言
- 一、事務控制
-
- START TRANSACTION 例子
- COMMIT AND CHAIN
- SAVE POINT 與ROLL BACK
- 二、事務隔離級别
前言
資料庫事務是一個很重要的概念,日常開發中如何保證資料的準确性是一個很大的學問。MYSQL支援的事務的實作依賴于其ACID特性。ACID是什麼?即
原子性
、
一緻性
、
隔離性
、
持久性
。
事務名稱 | 解釋 |
---|---|
原子性(Atomicity) | 事務是一個原子操作,原子操作簡單了解指的是這個操作要麼全部成功,要麼全部失敗 |
一緻性(Consistency) | 事務無論成功與否,資料庫必須保證所處的資料不應被破壞,舉個例子:A給B無論成功或失敗轉賬,那麼A的錢+B的錢前後應該總和相等 |
隔離性(Isolation) | 同一份的資料可能有很多事務進行操作,是以要将各種事務隔離開,防止資料被損壞 |
持久性(Durability) | 事務如果一旦完成,結果都應不變,因為這樣無論系統發送了什麼錯誤,都能進行資料恢複 |
一、事務控制
MYSQL通過SET AUTOCOMMIT、START TRANSACTION、COMMIT、ROLLBACK 等語句支援事務,MYSQL的事務預設是自動送出的。可以通過語句查詢目前MYSQL的事務資訊:
mysql> show variables like '%autocommit%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.01 sec)
mysql>
也可以手動開啟事務、送出事務以及復原事務,下面介紹事務控制語句詳情:
- START TRANSACTION 開啟新事務
- COMMIT 和ROLLBACK用于送出和復原事務
- SET AUTOCOMMIT 用于設定是否開啟事務
- CHAIN 出現目前事務送出後,會立即重新開機新的事務并保持相同的隔離級别。
- RELEASE 表示送出事務後會立即斷開與用戶端的連接配接。
下面以實操感受一下資料庫事務特性,首先建立一個資料庫表如下:
mysql> create table tb_user
(id int primary key auto_increment,
name varchar(125) not null,
age int not null);
Query OK, 0 rows affected (0.04 sec)
START TRANSACTION 例子
回話1 | 回話2 |
---|---|
查詢tb_user表id為1的記錄 mysql> select * from tb_user where id =1; Empty set (0.00 sec) | 查詢tb_user表id為1的記錄 mysql> select * from tb_user where id =1; Empty set (0.00 sec) |
用START TRANSACTION開啟一個事務并插入一條記錄,沒有進行事務commit mysql> insert into tb_user(name,age) values(‘Michael’,23) ; Query OK, 1 row affected (0.00 sec) | 再次查詢表id為1的記錄依然為空 mysql> select * from tb_user where id =1; Empty set (0.00 sec) |
事務送出commit | 再次查詢表後可以查到結果: mysql> select * from tb_user where id=1; |
直接插入一條資料,這個事務是自動送出的 mysql> insert into tb_user(name,age) values(‘John’,24) ; Query OK, 1 row affected (0.00 sec) | 可以直接查詢剛剛回話一添加的記錄 mysql> select * from tb_user where id=2; |
COMMIT AND CHAIN
目前事務送出并開啟一個新的事務,保持與目前事務同一個事務隔離水準。
會話1 | 會話2 |
---|---|
重新開啟一個事務并在向表中插入一條資料 mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into tb_user(name,age) values(‘T-bag’,25); Query OK, 1 row affected (0.00 sec) 然後用commit and chain指令進行送出 mysql> commit and chain;Query OK, 0 rows affected (0.00 sec) | 然後在會話2中無法查詢到會話1中插入的資料 |
在此用commit指令送出事務 | 然後在此查詢可以看到會話一中新插入的資料 mysql> select * from tb_user where id =3; |
SAVE POINT 與ROLL BACK
SAVE POINT可以指定復原事務的一部分,可以定義不同的SAVE POINT滿足不同條件下復原不同的事務,SAVE PONIT 一般不能重名,因為這會造成後定義的SAVE PONIT會覆寫之前同名的SAVE POINT,如果不在使用SAVE PONIT可以通過RELEASE SAVE PONT指令進行删除,一旦被删除就無法在進行事務復原。
會話1 | 會話2 |
---|---|
啟動一個新的事務,并向表中插入新的資料 mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into tb_user(name,age) values(‘Sara’,22); Query OK, 1 row affected (0.00 sec) | 會話2中無法查詢到name為Sara的記錄 mysql> select * from tb_user where name=‘Sara’; Empty set (0.00 sec) |
會話1中是可以查到此條記錄的 | |
此處定義SAVE POINT并命名 mysql> savepoint transaction1; Query OK, 0 rows affected (0.00 sec) | |
此處在此插入資料 mysql> insert into tb_user(name,age) values(‘Bellick’,32); Query OK, 1 row affected (0.00 sec) | 會話2中無法查詢到name為Sara和name為Bellick的兩條記錄 |
會話1中可以查詢name為Sara和name為Bellick記錄 mysql> select * from tb_user where name=‘Sara’ or name=‘Bellick’; | |
復原事務點 mysql> rollback to savepoint transaction1; Query OK, 0 rows affected (0.00 sec) | |
在此查詢name為Sara和name為Bellick的記錄,發現隻能查詢到Sara的記錄 | |
commit送出事務 | 會話2中可以查詢到name為Sara的記錄 |
上面我們驗證了savepoint的使用方法,接下來我們示範其出現的覆寫問題,以及删除savepoint的用法如下:
## 開啟新事務
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
## 插入資料
mysql> insert into tb_user(name,age) values('Bellick',32);
Query OK, 1 row affected (0.00 sec)
## 生成savepoint
mysql> savepoint transaction1;
Query OK, 0 rows affected (0.00 sec)
## 插入新的資料
mysql> insert into tb_user(name,age) values('Veronica',21);
Query OK, 1 row affected (0.00 sec)
mysql> select * from tb_user;
+----+----------+-----+
| id | name | age |
+----+----------+-----+
| 1 | Michael | 23 |
| 2 | John | 24 |
| 3 | T-bag | 25 |
| 4 | Sara | 22 |
| 5 | Bellick | 32 |
| 6 | Veronica | 21 |
+----+----------+-----+
6 rows in set (0.00 sec)
## 復原事務
mysql> rollback to savepoint transaction1;
Query OK, 0 rows affected (0.00 sec)
## 在此查詢
mysql> select * from tb_user;
+----+---------+-----+
| id | name | age |
+----+---------+-----+
| 1 | Michael | 23 |
| 2 | John | 24 |
| 3 | T-bag | 25 |
| 4 | Sara | 22 |
| 5 | Bellick | 32 |
+----+---------+-----+
5 rows in set (0.00 sec)
## 重新在插入資料
mysql> insert into tb_user(name,age) values('Veronica',21);
Query OK, 1 row affected (0.00 sec)
mysql> select * from tb_user;
+----+----------+-----+
| id | name | age |
+----+----------+-----+
| 1 | Michael | 23 |
| 2 | John | 24 |
| 3 | T-bag | 25 |
| 4 | Sara | 22 |
| 5 | Bellick | 32 |
| 6 | Veronica | 21 |
+----+----------+-----+
6 rows in set (0.00 sec)
mysql> savepoint transaction1;
Query OK, 0 rows affected (0.00 sec)
## 再次插入資料并復原
mysql> select * from tb_user;
+----+----------+-----+
| id | name | age |
+----+----------+-----+
| 1 | Michael | 23 |
| 2 | John | 24 |
| 3 | T-bag | 25 |
| 4 | Sara | 22 |
| 5 | Bellick | 32 |
| 6 | Veronica | 21 |
| 7 | Fernando | 24 |
+----+----------+-----+
7 rows in set (0.00 sec)
## 復原事務
mysql> rollback to savepoint transaction1;
Query OK, 0 rows affected (0.00 sec)
## 查詢發現兩次復原同一個名稱為transaction1的savepoint發現查詢結果并不一樣
mysql> select * from tb_user;
+----+----------+-----+
| id | name | age |
+----+----------+-----+
| 1 | Michael | 23 |
| 2 | John | 24 |
| 3 | T-bag | 25 |
| 11 | Sara | 22 |
| 12 | Bellick | 32 |
| 14 | Veronica | 21 |
+----+----------+-----+
6 rows in set (0.00 sec)
## 删除savepoint
mysql> release savepoint transaction1;
Query OK, 0 rows affected (0.00 sec)
二、事務隔離級别
隔離級别 | 描述 |
---|---|
| 允許事務讀取其他事務未送出的資料,不可重複讀、髒讀、幻讀将會出現 |
| 隻允許事務讀取其他事務已送出的資料,可以避免髒讀,但不可重複讀以及幻讀依然存在 |
| 確定事務可以多次對同一字段讀取是同一個值,該事務持續期間禁止其他事務進行更新,可以避免髒讀、不可重複讀,但仍然存在幻讀的可能性 |
| 確定事務可以多次讀取同一行的值,事務操作期間不允許其他事務進行更新、删除、添加操作,可以避免上述并發的問題,但問題是效率低下 |
- 事務的隔離級别
典型的事務隔離不同所造成問題如下:
-
:髒讀發送在A事務讀取B事務已經改寫但是還未送出的資料,若此時B事務復原了,那麼A事務擷取就是髒資料髒讀
-
:不可重複讀發送在當A事務執行2次查詢,每一次擷取的資料結果都不相同,這是由于B事務在A事務2次查詢期間進行了更新不可重複讀
-
幻讀
: 幻讀發送在當A事務讀取了幾行資料,緊接着B事務進行輸入的插入,在随後的查詢中A事務就會讀了原本不存在的記錄
⚠️ 不可重複讀特指修改的記錄,而幻讀指的是新增或删除的記錄
-
資料庫預設隔離級别
MYSQL預設隔離級别
,ORACLE 預設隔離級别REPEATABLE_READ
。READ_COMMITED