初識事務隔離 事務隔離級別的出現都是針對資料庫的具體問題的, SQL 92標準對事務併發處理會存在的異常情況進行了分級, 分別為臟讀(Dirty Read)、不可重覆讀(Unrepeatable Read)和幻讀(Phantom Read). 三種異常 舉個例子, 有個heros_temp表, 中有 ...
初識事務隔離
事務隔離級別的出現都是針對資料庫的具體問題的, SQL-92標準對事務併發處理會存在的異常情況進行了分級, 分別為臟讀(Dirty Read)、不可重覆讀(Unrepeatable Read)和幻讀(Phantom Read).
三種異常
舉個例子, 有個heros_temp表, 中有三條數據:
先說臟讀, 現在訪問資料庫, 對資料庫進行了一次INSERT操作, 插入一條"呂布"的記錄, 事務未提交, 現在重新開啟一個事務, 查詢資料庫可以將呂布的記錄查詢出來. 這個就是臟讀.
不可重覆讀呢? 現在先查詢資料庫記錄, 之後重新開啟一個事務, 修改一條記錄, 現在在前一個事務中再次查詢可以查詢出修改後的數據, 前後查詢出的數據不一致. 這個就是不可重覆度.
幻讀呢? 現在先查詢資料庫, 之後開啟新事務INSERT一條記錄, 在第一個事務中再次查詢會多出插入的記錄, 這個就是"幻讀".
- 臟讀: 讀到事務中還沒有提交的數據(可能事務回滾, 這個時候讀到的數據無意義)
- 不可重覆讀: 對某數據進行讀取, 發現兩次讀取的結果不同, 也就是沒有讀到相同的內容.(重點在於一條數據的修改, 也就是UPDATE 和 DELETE) 這個因為有其他事務對這個數據同時進行了修改和刪除.
- 幻讀: 事務A根據條件查詢得到了N條數據, 但此時事務B更改或者增加了M條符合事務A查詢條件的數據, 這樣當事務A再次進行查詢的時候大仙有N + M條數據, 產生了幻讀.(重點在於新增多條記錄INSERT)
事務的隔離級別有哪些?
解決異常數量從少到多的順序決定了隔離級別的高低, 這四種隔離級別從低禱告分別是: 讀未提交(READ UNCOMMITTED)、讀已提交(READ COMMITTED)、可重覆讀(REPEATABLE READ)和可串列化(SERIALIZABLE). 這個隔離級別解決的異常情況如下:
讀未提交,也就是允許讀到未提交的數據,這種情況下查詢是不會使用鎖的,可能會產生臟讀、不可重覆讀、幻讀等情況。
讀已提交就是只能讀到已經提交的內容,可以避免臟讀的產生,屬於 RDBMS 中常見的預設隔離級別(比如說 Oracle 和 SQL Server),但如果想要避免不可重覆讀或者幻讀,就需要我們在 SQL 查詢的時候編寫帶加鎖的 SQL 語句。
可重覆讀,保證一個事務在相同查詢條件下兩次查詢得到的數據結果是一致的,可以避免不可重覆讀和臟讀,但無法避免幻讀。MySQL 預設的隔離級別就是可重覆讀。
可串列化,將事務進行串列化,也就是在一個隊列中按照順序執行,可串列化是最高級別的隔離等級,可以解決事務讀取中所有可能出現的異常情況,但是它犧牲了系統的併發性。
模擬異常
模擬的資料庫表如下:
-- ----------------------------
-- Table structure for heros_temp
-- ----------------------------
DROP TABLE IF EXISTS `heros_temp`;
CREATE TABLE `heros_temp` (
`id` int(11) NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of heros_temp
-- ----------------------------
INSERT INTO `heros_temp` VALUES (1, '張飛');
INSERT INTO `heros_temp` VALUES (2, '關羽');
INSERT INTO `heros_temp` VALUES (3, '劉備');
開啟兩個客戶端, 由於MySQL的預設隔離級別是可重覆度, 所以需要先將級別設為讀未提交:
mysql> SHOW VARIABLES LIKE 'transaction_isolation'; // 查詢當前隔離級別
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; // 設置隔離級別為讀未提交
mysql> SET autocommit = 0; // 設置事務不自動提交(由於MySQL的事務是自動提交的, 這裡需要改一下)
臟讀
客戶端2中開啟事務, 寫入新英雄, 不要提交
客戶端1中查看
模擬不可重覆讀
客戶端1查詢
客戶端2修改
客戶端1再次查詢
模擬幻讀
客戶端1查詢
客戶端2新增
客戶端1再次查詢
隔離級別都是針對對應的異常問題, 並且隔離級別針對每種RDBMS都是相同的, 不同的是實現的原理不同.