目錄
一、什么是事務?
1.1.事務的定義
1.2.事務的基本語句
1.3.事務的四大特性(ACID)
二、數據庫的并發控制
2.1.什么是并發及并發操作帶來的影響?
2.2.并發操作帶來的隔離級別
三、使用事務的場景
3.1.銀行轉賬場景示例
3.2.模擬丟失修改、不可重復讀、讀臟數據
一、什么是事務?
1.1.事務的定義
MySQL事務是數據庫操作的一個基本單元,它確保一組操作要么全部成功,要么全部失敗。事務的主要目的是保證數據的一致性和完整性。
1.2.事務的基本語句
一個事務由應用程序的一組操作序列組成,它以 BEGIN TRANSACTION語句開始,以 END TRANSACTION 結束語句。
事務定義的語句如下:
- BEGIN TRANSACTION: 事務開始。
- END TRANSACTION:事務結束。
- COMMIT:事務提交。該操作表示事務成功地結束,它將通知事務管理器該事務的所有更新操作現在可以被提交或永久地保留。
- ROLLBACK:事務回滾。該操作表示事務非成功地結束,它將通知事務管理器出故障了,數據庫可能處于不一致狀態,該事務的所有更新操作必須回滾或撤銷。這意味著將撤銷該事務對數據庫的更新。這樣,數據庫恢復到該事務執行第一條語句之前的狀態。
典型示例:
典型的例子是銀行轉賬業務。對“從賬戶 A 轉入賬戶 B 金額x元”業務,站在顧客角度來看,轉賬是一次單獨操作;而站在數據庫系統的角度它至少是由兩個操作組成的,第一步從賬戶A減去x元,第二步給賬戶B加上x元。下面是銀行轉賬事務的偽代碼:
BEGIN TRANSACTION
????????read(A);????????????????????????/*讀賬戶A的余額*/
????????A=A-x;
????????IF(A<0)THEN
? ? ? ? ? ? ? ? ?print("金額不足,不能轉賬");????????????????ROLLBACK;????????/*撤銷該事務,回到事務執行前的狀態*
????????ELSE
????????????????write(A);????????????????/*寫入賬戶A的余額*/
????????????????read(B);
????????????????B=B+1;
????????????????write(B);
????????????????COMMIT;????????????????/*提交事務*/
????????ENDIF;
END TRANSACTION
1.3.事務的四大特性(ACID)
- 原子性(Atomicity):事務中的所有操作要么全部完成,要么全部不完成。如果事務中的任何操作失敗,整個事務將回滾到初始狀態。
- 一致性(Consistency):事務執行前后,數據庫的狀態必須保持一致。這意味著事務必須遵循數據庫的約束和規則。
- 隔離性(Isolation):多個事務并發執行時,一個事務的操作不會影響其他事務。每個事務都感覺不到其他事務的存在。
- 持久性(Durability):一旦事務提交,它對數據庫的修改就是永久性的,即使系統發生故障也不會丟失。
二、數據庫的并發控制
2.1.什么是并發及并發操作帶來的影響?
所謂并發操作,是指在多用戶共享的系統中,許多用戶可能同時對同一數據進行操作。這種機制就有可能導致,其他用戶想要得到原始數據,但由于其他用戶的讀寫操作,導致數據已經進行修改等情況的產生。
通常并發操作導致數據的不一致性主要有三類:
1.丟失修改:
????????如圖 12-7(a)所示,事務 T!、T,都是對數據 A 做減1操作。事務T,在時劍ち把A 修改后的值15寫入數據庫,但事務T在時刻t再把它對A減1后的值15寫入。兩個事務都是對 A的值進行減1操作并且都執行成功,但A中的值卻只減了1。現實的例子如售票系統,同時售出了兩張票,但數據庫里的存票卻只減了一張,造成數據的不一致。原因在于T事務對數據庫的修改被 T事務覆蓋而丟失了,破壞了事務的隔離性。2.不可重復讀:
????????如圖 12-7(b)所示,事務 T讀取 A、B 的值后進行運算,事務 T在 t時刻對 B的值做了修改以后,事務 T又重新讀取 A、B 的值再運算,同一事務內對同一組數據的相同運算結果不同,顯然與事實不相符。同樣是事務 工干擾了事務 T的獨立性。
3.讀臟數據:
????????如圖 12-7(c)所示,事務 T對數據 ℃ 修改之后,在 t時刻事務 T讀取修改后的 ℃ 值做處理,之后事務 T回滾,數據 ℃ 恢復了原來的值,事務 T對C所做的處理是無效的,它讀的是被丟掉的垃圾值。
其主要原因是事務的并發操作破壞了事務的隔離性。DBMS的并發控制子系統負責協調并發事務的執行,保證數據庫的完整性不受破壞,避免用戶得到不正確的數據。
2.2.并發操作帶來的隔離級別
并發操作對應產生了由于不同隔離級別所產生的不同并發問題。一般規律也是:隔離級別越高,數據一致性越強,但并發性能相應就越低。
并發問題 | 讀未提交(Read Uncommitted) | 讀已提交(Read committed) | 可重復讀(Repeatable Read) | 串行化(Serializable) |
臟讀(Dirty Read) | × | √ | √ | √ |
不可重復讀 | × | × | √ | √ |
幻讀(Phantom) | × | × | MySQL在可重復讀級別通過Next-Key鎖解決幻讀 | √ |
丟失更新 | × | × | 不能完全解決 | √ |
幻讀和不可重復讀:
類型 | 關注點 | 變化 |
不可重復讀 | 同一條記錄的數據變化 | 余額從?1000 ?→?900 (值變化) |
幻讀 | 結果集的行數變化 | 第一次查有?5 ?條,第二次變?6 ?條。 |
三、使用事務的場景
3.1.銀行轉賬場景示例
測試事務的一致性(要么同時成功,要么同時失敗)
-- 表結構設計
drop table book;
CREATE TABLE `book` (`recID` INT(11) PRIMARY KEY AUTO_INCREMENT,`title` VARCHAR(50) NOT NULL,`type` VARCHAR(20) NOT NULL,`price` DECIMAL(10,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;show TABLES;select * from book b;
-- 初始數據
INSERT INTO `book` (`title`, `type`, `price`) VALUES
('Java Programming', 'Computer', 16.00),
('Java EE Technology', 'Computer', 5.00),
('Information System', 'Computer', 15.00);-- 事務操作語句
BEGIN;
-- 批量降價8元
UPDATE book SET price = price - 8 WHERE recID = 1;
UPDATE book SET price = price - 8 WHERE recID = 2;
UPDATE book SET price = price - 8 WHERE recID = 3;-- 驗證價格是否不低于0
SELECT * FROM book WHERE price < 0;-- 根據驗證結果選擇提交或回滾
COMMIT; -- 無負值時提交
ROLLBACK; -- 出現負值時回滾
驗證:
驗證價格是否不低于0,因為出現負值,我們執行回滾操作,看數據是否會回到我們最開始的狀態值。
3.2.模擬丟失修改、不可重復讀、讀臟數據
由于mysql其自身的默認隔離級別就已經是可重復讀,即使并發執行,也不會產生丟失修改和不可重復讀問題。
-- 查看當前會話的隔離級別(MySQL 5.7+ 和 MariaDB)SELECT @@transaction_isolation;-- 查看全局隔離級別SELECT @@global.transaction_isolation;-- mysql中的默認隔離級別是:REPEATABLE-READ
模擬:
-- 1. 表結構設計(以銀行賬戶為例)
CREATE TABLE account (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50) NOT NULL,balance DECIMAL(10, 2) NOT NULL -- 賬戶余額
);
-- 插入初始數據:
INSERT INTO account (name, balance) VALUES ('Alice', 1000.00);-- 2. 模擬不可重復讀
-- 現象:事務A多次讀取同一數據,事務B修改并提交后,事務A兩次讀取結果不一致。
-- 隔離級別:READ COMMITTED(讀已提交)
-- 操作步驟:-- 事務A(首次讀取):
BEGIN;
SELECT balance FROM account WHERE name = 'Alice'; -- 結果:1000.00-- 事務B(修改并提交):
BEGIN;
UPDATE account SET balance = 900.00 WHERE name = 'Alice';
COMMIT; -- 修改生效-- 事務A(再次讀取):
SELECT balance FROM account WHERE name = 'Alice'; -- 結果:900.00(與第一次不一致)
COMMIT;
-- 結果:事務A兩次讀取結果不同,即不可重復讀。-- 3. 模擬丟失修改
-- 現象:兩個事務同時讀取并修改同一數據,后提交的事務覆蓋前者的修改。
-- 隔離級別:READ UNCOMMITTED(讀未提交)或 READ COMMITTED
-- 操作步驟:-- 事務A(讀取并修改):
BEGIN;
SELECT balance FROM account WHERE name = 'Alice'; -- 讀取1000.00
UPDATE account SET balance = 1000.00 - 100 = 900.00 WHERE name = 'Alice';-- 事務B(同時讀取并修改):
BEGIN;
SELECT balance FROM account WHERE name = 'Alice'; -- 仍讀取1000.00(未提交的數據)
UPDATE account SET balance = 1000.00 - 200 = 800.00 WHERE name = 'Alice';
COMMIT; -- 先提交-- 事務A提交:
COMMIT; -- 覆蓋事務B的修改
-- 結果:余額變為 900.00(事務B的修改丟失),正確應為 700.00。
開啟事務A B
事務A讀取到的值
事務B修改提交之后:
事務A再次讀取: