首先我們做一個模擬,執行以下的sql,其中有如下圖數據:
時間 | 會話1 | 會話2 | 會話3 |
---|---|---|---|
1 | begin; select * from t_student where id = 2 |
||
2 | update t_student set age = age + 1 where id =2;; | ||
3 | begin; update t_student set age = age + 1 where id =2;; |
||
4 | select * from t_student where id =2; COMMIT |
||
5 | select * from t_student where id = 2; | ||
6 | COMMIT; select * from t_student where id = 2; |
我把執行結果按照表格如下展示:
時間 | 會話1 | 會話2 | 會話3 |
---|---|---|---|
1 | 15 | ||
2 | |||
3 | |||
4 | 17 | ||
5 | 15 | ||
6 | 17 |
分析:
在會話1當中,只有當會話1的事務提交後,才能查到最終會話2更改的數據。
在會話2當中,開啓事務後更新數據,之後查詢發現數據變成了17。
針對上面的現象我們進行個原理分析:
實際上產生上述顯現是因爲InnoDB採用的MVCC(多版本併發控制),其中針對每條數據會有它自己的事務id,以及一個最大事務id。針對事務中數據每次修改,會產生不同的版本。
1)假設開始id = 2的數據,其事務txid = 1000;
2)當會話1開始,此時txid變成了1001,而會話2開啓,txid又變成了1002,同理會話3會變成1003,此時都生成了不同版本的快照。
3)會話1在事務當中去讀取時候,採用了快照讀的方式,即拿到一個1001的事務id,此時只會讀取小於等於自己版本的數據,所以在事務中最終只能拿到值爲17的數據。
4)會話2在更新數據的時候,採用的當前讀的方式,即對數據增加X鎖,獲取最新的事務id,讀取最新的版本數據。所以在更新之前,就讀取到了age的年齡是16,之後在進行+1,得到17.
總結一下:
快照讀解決了幻讀的問題,即多次讀取數據不一致的問題。
update、insert、delete都會執行當前讀,防止併發更新數據導致數據錯誤,此過程或添加X鎖。