在日常開發過程中,事務是經常被使用的,然而大多數開發者并不了解事務隔離級別是什么,也不知道不同的隔離級別下使用事務時可能會發生的一些問題。
SQL標準定義的四個隔離級別為:
- READ UNCOMMITTED (讀未提交)
- READ COMMITTED (讀已提交)
- REPEATABLE READ (可重復讀)
- SERIALIZABLE (串行)
?
user表
id | name | money |
---|---|---|
1 | 小李 | 1000 |
?
READ UNCOMMITTED
事務A修改了一條數據,但未提交,事務B可以讀到被事務A修改的數據,存在臟讀問題,同時也存在不可重復讀、幻讀等問題(不一一寫實際例子)。
事務A | 事務B |
---|---|
start TRANSACTION | start TRANSACTION |
update user set name = '小明' where id = 1 | - |
- | select name from user where id = 1 |
?
READ COMMITTED
解決了臟讀的情況,但是當事務A提交之后,事務B在一次事務內讀取同一條數據會有不同的結果,存在不可重復讀的問題。
事務A | 事務B |
---|---|
start TRANSACTION | start TRANSACTION |
update user set name = '小明' where id = 1 | - |
- | select name from user where id = 1 |
COMMIT | - |
- | select name from user where id = 1 |
?
REPEATABLE READ
Mysql默認的事務隔離級別,解決了臟讀、不可重復讀問題,同時用
next_key_lock 解決了幻讀的問題,但是有出現更新覆蓋的可能。
事務A | 事務B |
---|---|
start TRANSACTION | start TRANSACTION |
select money into @money from user where id = 1 | select money into @money from user where id = 1 |
update user set money = @money-100 where id = 1 | - |
COMMIT | - |
- | update user set money = @money-50 where id = 1 |
- | COMMIT |
對應實際的業務場景,就是用戶分別消費了100元和50元,2個事務都成功了,但是實際上最終賬戶只減少了50元。
解決這種情況,需要在讀取數據的時候加上排他鎖(X鎖),使2個事務變成串行操作。如下:
事務A | 事務B |
---|---|
start TRANSACTION | start TRANSACTION |
select money into @money from user where id = 1 for update | select money into @money from user where id = 1 for update |
update user set money = @money-100 where id = 1 | - |
COMMIT | - |
- | update user set money = @money-50 where id = 1 |
- | COMMIT |
?
SERIALIZABLE
串行模式下,可以解決事務相互依賴導致的死鎖問題,以及REPEATABLE READ下可能出現的更新被覆蓋問題,但是由于不能同時執行2個或以上的事務,會使性能下降,所以大部分情況下不會選擇此模式。