












事務隔離級別是數據庫併發控制的核心,定義了事務之間如何隔離以及一個事務能看到其他事務更改的程度。本文基於 sql_store 數據庫,通過代碼示例詳細講解 MySQL 支持的四種標準事務隔離級別及其影響,結合場景說明隔離級別的作用,並解析死鎖問題及其規避方法,幫助讀者深入理解事務併發管理。
學習內容
1. 事務隔離級別概述
MySQL 支持四種標準事務隔離級別,從低到高分別為:
隔離級別越高,併發性能越低,但數據一致性越高。ATM 轉賬場景中,合適的隔離級別可確保賬戶餘額更新時不被其他事務干擾。
2. 查看與設置隔離級別
通過 SHOW VARIABLES LIKE 'transaction_isolation' 查看當前隔離級別,默認顯示 REPEATABLE-READ。可通過以下語句設置隔離級別:
3. 併發問題與隔離級別
不同隔離級別對髒讀、不可重複讀和幻讀的控制能力不同:
4. 死鎖
死鎖是併發事務中兩個或多個事務互相等待對方釋放資源,導致無法繼續執行。MySQL 會檢測死鎖並終止一個事務,報錯提示。減少死鎖需保持一致的資源訪問順序或縮短事務長度。
示例代碼與講解
1. 查看隔離級別
SHOW VARIABLES LIKE 'transaction_isolation';
顯示當前隔離級別,默認值為 REPEATABLE-READ,MySQL InnoDB 的標準配置。
2. 設置隔離級別
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
將當前會話的隔離級別設為 SERIALIZABLE,確保後續事務串行執行,避免所有併發問題。
3. 讀未提交(READ UNCOMMITTED)
控制台 1:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT points FROM customers WHERE customer_id = 1;
控制台 2:
START TRANSACTION;
UPDATE customers SET points = 20 WHERE customer_id
= 1;
-- 未提交
在控制台 1 設置 READ UNCOMMITTED 後,查詢客戶 1 的積分(原為 2303)。控制台 2 更新積分為 20 但未提交,控制台 1 再次查詢將看到 20,體現了髒讀。若控制台 2 回滾,控制台 1 的查詢結果無效,可能誤導業務決策。
4. 讀已提交(READ COMMITTED)
控制台 1:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT points FROM customers WHERE customer_id = 1;
控制台 2:
START TRANSACTION;
UPDATE customers SET points = 20 WHERE customer_id
= 1;
COMMIT;
在控制台 1 設置 READ COMMITTED 後,查詢積分。若控制台 2 未提交更改,控制台 1 仍看到原值 2303,避免髒讀。控制台 2 提交後,控制台 1 查詢得到 20。但若在同一事務內多次查詢,可能因其他事務提交導致結果不一致,產生不可重複讀。
控制台 1(不可重複讀示例):
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
SELECT points FROM customers WHERE customer_id = 1;
-- 假設返回 20
SELECT points FROM customers WHERE customer_id = 1;
-- 返回 30
COMMIT;
控制台 2:
START TRANSACTION;
UPDATE customers SET points = 30 WHERE customer_id
= 1;
COMMIT;
控制台 1 第一次查詢得到 20,控制台 2 提交後更新為 30,控制台 1 第二次查詢得到 30,體現不可重複讀,數據一致性可能受影響。
5. 可重複讀(REPEATABLE READ)
控制台 1:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT points FROM customers WHERE customer_id = 1;
-- 返回 30
SELECT points FROM customers WHERE customer_id = 1;
-- 仍返回 30
COMMIT;
控制台 2:
START TRANSACTION;
UPDATE customers SET points = 40 WHERE customer_id
= 1;
COMMIT;
在 REPEATABLE READ 下,控制台 1 事務開始時創建一致性視圖,基於事務啟動時的快照(積分為 30)。控制台 2 提交更改為 40,但控制台 1 第二次查詢仍返回 30,避免不可重複讀。MVCC 確保事務內數據一致。
幻讀示例:
控制台 1:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT * FROM customers WHERE state = 'CO';
-- 返回 1 條記錄
COMMIT;
控制台 2:
START TRANSACTION;
UPDATE customers SET state = 'CO' WHERE customer_id
= 1;
COMMIT;
控制台 1 查詢 state = 'CO' 的客戶,初始僅 1 條記錄。控制台 2 將客戶 1 的狀態改為 'CO' 並提交。控制台 1 提交事務後再次查詢,仍得到 1 條記錄,未反映數據庫最新狀態,產生幻讀。InnoDB 的間隙鎖可部分緩解幻讀。
6. 串行化(SERIALIZABLE)
控制台 1:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT * FROM customers WHERE state = 'CO';
COMMIT;
控制台 2:
START TRANSACTION;
UPDATE customers SET state = 'CO' WHERE customer_id
= 2;
COMMIT;
在 SERIALIZABLE 下,控制台 1 查詢 state = 'CO' 的客戶(初始 2 條)。控制台 2 嘗試將客戶 2 狀態改為 'CO' 但未提交,控制台 1 事務會被阻塞,等待控制台 2 提交,避免幻讀。提交後,控制台 1 查詢正確反映最新數據。
7. 死鎖
控制台 1:
START TRANSACTION;
UPDATE customers SET state = 'VA' WHERE customer_id
= 1;
UPDATE orders SET status = 1 WHERE order_id
= 1;
COMMIT;
控制台 2:
START TRANSACTION;
UPDATE orders SET status = 1 WHERE order_id
= 1;
UPDATE customers SET state = 'VA' WHERE customer_id
= 1;
COMMIT;
控制台 1 鎖定 customers 的行,嘗試鎖定 orders;控制台 2 鎖定 orders,嘗試鎖定 customers,形成循環依賴,導致死鎖。MySQL 檢測到死鎖後終止一個事務,報錯提示。規避死鎖需統一資源訪問順序或縮短事務長度。
總結
事務隔離級別決定了併發事務的數據可見性和一致性。READ UNCOMMITTED 性能高但風險大,SERIALIZABLE 安全但性能低,REPEATABLE READ 是 MySQL InnoDB 的默認折中方案。ATM 轉賬場景說明了隔離級別對數據一致性的重要性。本文基於 sql_store 數據庫,通過代碼示例解析四種隔離級別及死鎖問題。後續內容將探討索引設計或查詢優化,敬請關注。
此內容由慣性聚合(RSS閱讀器)自動聚合整理,僅供閱讀參考。 原文來自 — 版權歸原作者所有。