1 演示錯誤案例
先給大家來一個錯誤演示。
我們打開兩個會話窗口,默認情況下隔離級別是可重復讀,我們來看下:
首先在 A 會話中查看當前 user 表,查看完成后開啟事務:
?
可以看到id=3的數據sex是男。
接下來在 B 會話中修改 sex:
?
查看修改成功
?
接下來回到A會話查詢:
?
可以看到,A 會話的記錄也變了。完整測試流程如下:
一致性問題 | |
---|---|
A會話 | B會話 |
select? * from user; | |
begin; | |
update user set sex='女' where id = 3; | |
select * from user; |
說好的可重復讀呢?
按理說,可重復讀就是別的事務對數據的操作不影響當前事務,但是上面這個案例似乎和我們理解的可重復讀有出入。
2 分析
不知道小伙伴們是否還記得可重復讀的特點:
用戶在另外一個事務中執行同條 SELECT 語句數次,結果總是相同的。
從這個角度來說,第一小節的案例似乎也沒有問題,因為我們在 A 會話中執行 SELECT 語句多次,查到的結果也都是相同的,sex 都是 女。
但是我們疑惑的是明明 B 會話的事務后開啟的,但是我們卻在 A 會話中讀取到了 B 的修改,這似乎不應該。
這里就涉及到一個問題,事務的一致性視圖是何時建立的?
事實上,我們執行的 begin 語句并不是一個事務真正的起點。執行完 begin 之后,接下來執行的第一句 SQL,事務才真正啟動。
我們稍微修改一下第一小節的案例:
一致性問題 | |
---|---|
A會話 | B會話 |
select? * from user; | |
begin; | |
select * from user; | |
update user set sex='男' where id = 3;(之前已經改成了女) | |
select * from user; |
A會話結果如下:
?
B會話結果如下:
?
在 A 會話中,事務開啟之后,立馬先執行一條 SELECT 語句,然后再去 B 會話中做修改,修改完成后再回到 A 會話繼續查詢,此時發現 B 中的修改對 A 并不可見,這個結果也符合用戶在另外一個事務中執行同條 SELECT 語句數次,結果總是相同的。
如果我們想要執行完 begin 之后,就立馬開啟事務,那么可以通過如下方式來執行:
start transaction with consistent snapshot;
這個 SQL 執行完之后,事務立馬就啟動了。
接下來,回到第一小節的案例,我們修改一下事務啟動的命令:
一致性問題 | |
---|---|
A會話 | B會話 |
select? * from user; | |
start transaction with consistent snapshot; | |
update user set sex='女' where id = 3; | |
select * from user; |
此時,A 會話中事務的查詢就看不見 B 中的修改了。