1. 事務簡介
事務(Transaction)?是一組操作的集合,它是一個不可分割的工作單位,事務會把所有的操作作為一個整體一起向系統提交或撤銷操作請求,即這些操作要么同時成功,要么同時失敗。
經典案例:轉賬場景
以張三給李四轉賬 1000 元為例,正常流程需包含三步:
- 檢查張三賬戶余額
- 張三賬戶扣除 1000 元
- 李四賬戶增加 1000 元
正常情況: 轉賬這個操作, 需要分為以下這么三步來完成 , 三步完成之后, 張三減少1000, 而李四增加1000, 轉賬成功 :
異常情況: 轉賬這個操作, 也是分為以下這么三步來完成 , 在執行第三步是報錯了, 這樣就導致張三減少1000塊錢, 而李四的金額沒變, 這樣就造成了數據的不一致, 就出現問題了
為了解決上述的問題,就需要通過數據的事務來完成,我們只需要在業務邏輯執行之前開啟事務,執行完畢后提交事務。如果執行過程中報錯,則回滾事務,把數據恢復到事務開始之前的狀態
注意: 默認MySQL的事務是自動提交的,也就是說,當執行完一條DML語句時,MySQL會立即隱式的提交事務。
2. 事務操作
2.1 事務控制方式
方式一:修改全局提交模式(手動提交)
-- 查看事務提交模式(1為自動提交,0為手動提交)
SELECT @@autocommit;
-- 設置為手動提交模式
SET @@autocommit = 0; -- 提交事務(手動確認所有操作生效)
COMMIT;
-- 回滾事務(撤銷所有未提交操作)
ROLLBACK;
注意:修改全局模式后,所有 DML 語句需手動提交,否則不會生效。
方式二:單次事務控制(推薦使用)
-- 開啟事務
START TRANSACTION; -- 或 BEGIN;-- 業務操作(以轉賬為例)
SELECT * FROM account WHERE name = '張三'; -- 查詢余額
UPDATE account SET money = money - 1000 WHERE name = '張三'; -- 扣款
UPDATE account SET money = money + 1000 WHERE name = '李四'; -- 入賬-- 根據執行結果決定提交或回滾
COMMIT; -- 成功時提交
ROLLBACK; -- 失敗時回滾
3. 事務四大特性(ACID)
-
原子性(Atomicity):事務是不可分割的最小操作單元,要么全部成功,要么全部失敗。
-
一致性(Consistency):事務完成時,必須使所有的數據都保持一致狀態。
-
隔離性(Isolation):數據庫系統提供的隔離機制,保證事務在不受外部并發操作影響的獨立環境下運行。
-
持久性(Durability):事務一旦提交或回滾,它對數據庫中的數據的改變就是永久的。
上述就是事務的四大特性,簡稱ACID。
4. 并發事務的三大問題
4.1 臟讀(Dirty Read)
問題:一個事務讀到另外一個事務還沒有提交的數據
此時有兩個事務,一個事務A,一個事務B,首先事務A去數據庫查詢id為1的這條數據,然后執行了update修改了id為1的這個數據,但是此時事務A還沒有提交事務,然后事務B去查詢id為1的數據的時候,卻查詢到了事務A修改后的數據,這種情況就稱為臟讀。
4.2 不可重復讀(Non-repeatable Read)
問題:一個事務先后讀取同一條記錄,但兩次讀取的數據不同,稱之為不可重復讀
此時有兩個事務,一個事務A,一個事務B,事務A先查詢id為1的數據,然后事務B修改了id為1的數據,等到事務A再次去查詢id為1的數據的時候,此時讀取到的是事務B修改后的數據,這個時候,在事務A中,兩次查詢的操作,查詢的數據不一致,此時就出現了不可重復度的問題。
4.3 幻讀(Phantom Read)
問題:一個事務按照條件查詢數據時,沒有對應的數據行,但是在插入數據時,又發現這行數據 已經存在,好像出現了 "幻影"
此時有兩個事務,一個事務A,一個事務B,事務A先去查詢id為1的數據,查詢到的結果為空,然后事務B插入id為1的數據,然后等到事務A去插入id為1的數據的時候,就報錯了,說主鍵沖突,然后事務A在去查詢id為1的數據,但是查詢到的結果還是空,此時就發生了,幻讀的問題。
5. 事務隔離級別
為了解決并發事務所引發的問題,在數據庫中引入了事務隔離級別。主要有以下幾種:
隔離級別 | 臟讀 | 不可重復讀 | 幻讀 | 性能影響 |
---|---|---|---|---|
Read Uncommitted(讀未提交) | √ | √ | √ | 最高 |
Read Committed(讀已提交) | × | √ | √ | 較高 |
Repeatable Read(可重復讀,MySQL 默認) | × | × | √ | 中等 |
Serializable(串行化) | × | × | × | 最低 |
-- 查看當前事務隔離級別
SELECT @@TRANSACTION_ISOLATION;-- 設置隔離級別(SESSION為當前會話,GLOBAL為全局)
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE};
注意:事務隔離級別越高,數據越安全,但是性能越低。其中Mysql中默認的事物隔離級別時可重復度,隔離級別需要根據實際的情況選擇。