Mysql筆記:事務隔離級別理解
這個問題其實有很多人都已經教科書式的總結了很多遍,如:
隔離級別中文描述此級別問題(面試官喜歡用這個)READ UNCOMMITED未提交讀髒讀READ COMMITED提交讀不可重複讀REPEATABLE READ可重複讀幻讀SERIALIZABLE序列化鎖
但是在這個表格中最後一列的問題因何產生,很多人會不明白其中的緣由。我先說下我的理解,然後再來一點點解釋:
事務隔離的四個級別可以先用“事務是否可併發”來劃分成兩個對立面來理解:
事務不可併發在 Mysql 中只有 SERIALIZABLE 這一級別滿足;其它的當然是事務可併發了;事務不可併發,Mysql 選擇了序列化這一實現方式,因此引入了鎖,也帶來了效能問題;事務可併發,因此在多個併發的事務期間,我們並不知道哪個事務的哪段邏輯(begin/rollback/commit)會在下一個時間片內被執行;
併發事務帶來的問題
在上面的描述中,2、3是對1的一個擴充套件,2不難理解,但是 3 可能有些生硬,我們可以簡單的換種理解方式,
假設同一時間有兩個事務: A & B ,並且事務 A 執行 update,事務 B 執行 select。假設事務的開啟、提交、回滾及事務中執行的 Action 都能在一個 cpu 時間片內完成,那麼可把 A&B 的事務拆成如下邏輯呼叫段:
#事務A事務B
1beginbegin2updateselect3commitcommit4rollbackrollback
基於上面的假設,我們再來理解事務併發情況下各種問題的產生:
髒讀
A begin=> update 後讓 cpu同時B begin=> select,但是事務 B 很心大,並沒有去驗證 A 的有效性,讀到了 A update 後的資料;A 在下一個 cpu 時間又得到了排程,A 發現自己剛才的操作無效了,A rollback 得到了執行,但是它無法告知 B 了,所以 B 讀到的資料是無效的;不可重複讀知道了髒讀的原因後,為了解決這個問題,Mysql 規定 B 讀的資料只能讀取已經 commit 狀態的資料:A begin=> update 後讓 cpu同時 B begin=> select,這次 B 很小心地驗證 A 的資料是否 commit 了,B 這次讀到了 A begin 以前的資料;事務 A 在下一個 cpu 時間又得到了排程,A commit 了;B 再次 select,但是已經 select 到了 A commit 後的資料了,B 在 A commit 前後讀到了兩次不一樣的資料,即不可重複讀了;幻讀知道了不可重複讀的原因後,Mysql 又規定,既然 B 第一次讀到的是 A commit 前的資料,那麼在事務 B 中後面無論多少次 select 都只能讀到 A commit 之前的資料。但是問題又來了:這次 A 不是 update 了,而是 insert,B select 也不是單條了,而是 select range;B 在 A commit 前後兩次 select range 會發現結果的數量不一至;這就是幻讀;InnoDB 針對幻讀也做了處理:MVCC,在每一行後都有隱藏的兩列版本號來實現;大致與處理不可重複讀相同;