1. 定義
MySQL 中事務的隔離級別一共分為四種,分別如下:
- 序列化(SERIALIZABLE):如果隔離級別為序列化,則用戶之間通過一個接一個順序地執行當前的事務,這種隔離級別提供了事務之間最大限度的隔離。
- 可重復讀(REPEATABLE READ):在可重復讀在這一隔離級別上,事務不會被看成是一個序列。不過,當前正在執行事務的變化仍然不能被外部看到,也就是說,如果用戶在另外一個事務中執行同條 SELECT 語句數次,結果總是相同的。(因為正在執行的事務所產生的數據變化不能被外部看到)。
- 提交讀(READ COMMITTED):READ COMMITTED 隔離級別的安全性比 REPEATABLE READ 隔離級別的安全性要差。處於 READ COMMITTED 級別的事務可以看到其他事務對數據的修改。也就是說,在事務處理期間,如果其他事務修改了相應的表,那么同一個事務的多個 SELECT 語句可能返回不同的結果。
- 未提交讀(READ UNCOMMITTED):READ UNCOMMITTED 提供了事務之間最小限度的隔離。除了容易產生虛幻的讀操作和不能重復的讀操作外,處於這個隔離級的事務可以讀到其他事務還沒有提交的數據,如果這個事務使用其他事務不提交的變化作為計算的基礎,然后那些未提交的變化被它們的父事務撤銷,這就導致了大量的數據變化。
2. 實踐
2.1 查看隔離級別
通過如下 SQL 可以查看數據庫實例默認的全局隔離級別和當前 session 的隔離級別:
MySQL8 之前使用如下命令查看 MySQL 隔離級別:
SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
查詢結果如圖:
可以看到,默認的隔離級別為 REPEATABLE-READ,全局隔離級別和當前會話隔離級別皆是如此。
設置隔離級別:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
2.1 READ UNCOMMITTED
首先創建一個簡單的表,預設兩條數據,如下:
臟讀
一個事務讀到另外一個事務還沒有提交的數據,稱之為臟讀。具體操作如下:
首先打開兩個SQL操作窗口,假設分別為 A 和 B,在 A 窗口中輸入如下幾條 SQL (輸入完成后不用執行):
START TRANSACTION;
UPDATE account set balance=balance+100 where name='javaboy';
UPDATE account set balance=balance-100 where name='itboyhub';
COMMIT;
在 B 窗口執行如下 SQL,修改默認的事務隔離級別為 READ UNCOMMITTED,如下:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
接下來在 B 窗口中輸入如下 SQL,輸入完成后,首先執行第一行開啟事務(注意只需要執行一行即可):
START TRANSACTION;
SELECT * from account;
COMMIT;
接下來執行 A 窗口中的前兩條 SQL,即開啟事務,給 javaboy 這個賬戶添加 100 元。
進入到 B 窗口,執行 B 窗口的第二條查詢 SQL(SELECT * from user;),結果如下:
可以看到,A 窗口中的事務,雖然還未提交,但是 B 窗口中已經可以查詢到數據的相關變化了。
這就是臟讀問題。
不可重復讀
不可重復讀是指一個事務先后讀取同一條記錄,但兩次讀取的數據不同,稱之為不可重復讀。具體操作步驟如下(操作之前先將兩個賬戶的錢都恢復為1000):
首先打開兩個查詢窗口 A 和 B ,並且將 B 的數據庫事務隔離級別設置為 READ UNCOMMITTED。具體 SQL 參考上文,這里不贅述。
在 B 窗口中輸入如下 SQL,然后只執行前兩條 SQL 開啟事務並查詢 javaboy 的賬戶:
START TRANSACTION;
SELECT * from account where name='javaboy';
COMMIT;
前兩條 SQL 執行結果如下:
在 A 窗口中執行如下 SQL,給 javaboy 這個賬戶添加 100 塊錢,如下:
START TRANSACTION;
UPDATE account set balance=balance+100 where name='javaboy';
COMMIT;
再次回到 B 窗口,執行 B 窗口的第二條 SQL 查看 javaboy 的賬戶,結果如下
javaboy 的賬戶已經發生了變化,即前后兩次查看 javaboy 賬戶,結果不一致,這就是不可重復讀。
和臟讀的區別在於,臟讀是看到了其他事務未提交的數據,而不可重復讀是看到了其他事務已經提交的數據(由於當前 SQL 也是在事務中,因此有可能並不想看到其他事務已經提交的數據)。