這個問題其實有不少人都已經教科書式的總結了不少遍,如:面試
隔離級別中文描述此級別問題(面試官喜歡用這個)READ UNCOMMITED未提交讀髒讀READ COMMITED提交讀不可重複讀REPEATABLE READ可重複讀幻讀SERIALIZABLE串行化鎖sql
可是在這個表格中最後一列的問題因何產生,不少人會不明白其中的原因。我先說下個人理解,而後再來一點點解釋:併發
事務隔離的四個級別能夠先用「事務是否可併發」來劃分紅兩個對立面來理解:性能
- 事務不可併發在 Mysql 中只有 SERIALIZABLE 這一級別知足;其它的固然是事務可併發了;
- 事務不可併發,Mysql 選擇了串行化這一實現方式,所以引入了鎖,也帶來了性能問題;
- 事務可併發,所以在多個併發的事務期間,咱們並不知道哪一個事務的哪段邏輯(begin/rollback/commit)會在下一個時間片內被執行;
併發事務帶來的問題事務
在上面的描述中,二、3是對1的一個擴展,2不難理解,可是 3 可能有些生硬,咱們能夠簡單的換種理解方式,it
- 假設同一時間有兩個事務: A & B ,而且事務 A 執行 update,事務 B 執行 select。
- 假設事務的開啓、提交、回滾及事務中執行的 Action 都能在一個 cpu 時間片內完成,那麼可把 A&B 的事務拆成以下邏輯調用段:
#事務A事務B1beginbegin2updateselect3commitcommit4rollbackrollbackio
基於上面的假設,咱們再來理解事務併發狀況下各類問題的產生:擴展
髒讀date
- 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,在每一行後都有隱藏的兩列版本號來實現;大體與處理不可重複讀相同;