mysql如何實現四大隔離級別的?
在mvcc下,mysql中用到的鎖還是共享鎖和排他鎖么?如果是的話,那麼是怎樣結合鎖和mvcc來實現rc和rr隔離級別的呢?還有mysql中在ru隔離級別下,兩個事務同時讀取數據對象A,當事務1修改A之後,事務2可以立即讀到修改數據,但是在1提交之前,2想修改A會被阻塞,據說ru下沒有mvcc。那麼是怎麼實現出這樣的效果的呢?
以下是個人推測:行鎖:read uncommitted:
事務修改數據加排他鎖,改完釋放排它鎖,加共享鎖,直到提交事務釋放。事務讀取數據讀永遠讀最高版本數據。read committed:事務修改數據加排它鎖,直到事務提交釋放。事務讀取數據永遠讀最高版本數據。repeatable read :事務修改數據加排它鎖,直到事務提交釋放。事務讀取數據版本號小於等於事務版本號的數據。
serializablr:不使用mvcc。事務修改數據加排他鎖,讀取數據加共享鎖,直到提交釋放。(低並發,極易死鎖)這是個人根據測試結果進行猜測的。望大神指點更正!
概念
事務ID
事務ID是一個遞增的整數,唯一的標識一個事務。ID的大小可以用來表示事務的串列化順序,用於事務可見性的判斷。多版本存儲
MySQL InnoDB實現了多版本並發控制(MVCC),在多版本存儲上,MySQL採用從新到舊(Newest To Oldest)的版本鏈。B+Tree葉結點上,始終存儲的是最新的數據(可能是還未提交的數據)。而舊版本數據,通過UNDO記錄(做DELTA)存儲在回滾段(Rollback Segment)里。每一條記錄都會維護一個ROW HEADER元信息,存儲有創建這條記錄的事務ID,一個指向UNDO記錄的指針。通過最新記錄和UNDO信息,可以還原出舊版本的記錄。
如下圖, V1被一個事務更新為V2,V2被另一個事務更新為V3,Δ1存儲V1到V2的更新,Δ2存儲V2到V3的更新。此時,如果一個事條定位到B+Tree葉子節點的記錄V3,則通過V3+Δ2可以還原出V2,通過V3+Δ2+Δ1可以還原出V1。
ReadView (或者可以稱之為Snapshot)
ReadView是某一個時間點,事務執行狀態的一個快照,可以用來判斷事務的可見性。ReadView的基本結構如下:
ReadView {
creator_trx_id
low_limit_id
up_limit_id
ids
...
}
creator_trx_id 創建這個ReadView的事務ID
low_limit_id 所有事務ID大於或等於low_limit_id對當前事務都不可見
up_limit_id 所有事務ID嚴格小於up_limit_id的事務對當前事務可見
ids 未提交的事務ID列表
可見性的判斷
事務通過用當前事務(或語句,取決於隔離級別)的RaadView來判斷一個事務id的操作是否對當前事務可見。判斷可見性的偽代碼如下:
IsVisible(trx_id)
if (trx_id == creator_trx_id) // 當前事務
return true;
else if (trx_id &< up_limit_id) // ReadView創建時, 事務已提交
return true;
else if (trx_id &>= low_limit_id) // ReadView創建時,事務還未被創建
return false;
else if (trx_id is in m_ids) // ReadView創建時,事務正在執行,但未提交
return false
else // ReadView創建時, 事務已提交
return true;
不同隔離級別的實現
可串列化(Serializable)
在可串列化級別上,MySQL執行S2PL並發控制協議, 一階段申請,一階段釋放。讀寫都要加鎖。可重複讀(Repeatable Read)
可重複讀是MySQL默認的隔離級別,理論上說應該稱作快照(Snapshot)隔離級別。讀不加鎖,只有寫才加鎖,讀寫互不阻塞,並發度相對於可串列化級別要高,但會有Write Skew異常。事務在開始時創建一個ReadView,當讀一條記錄時,會遍歷版本鏈表,通過當前事務的ReadView判斷可見性,找到第一個對當前事務可見的版本,讀這個版本。
對於寫操作,包括Locking Read(SELECT ... FOR UPDATE), UPDATE, DELETE,需要加寫鎖。根據謂詞條件上索引使用情形,鎖定有不同的方式:
1)有索引:
對於索引上有唯一約束且為等值條件的情形,不用GAP LOCK,只鎖定索引記錄。對於其它情形,使用GAP LOCK,相當於謂詞鎖。2)沒有索引:
由於MySQL沒有實現通用的謂詞鎖,這時就相當於鎖全表。讀已提交(Read Committed)
MySQL的讀已提交實際是語句級別快照。與可重複讀級別主要有兩點不同:
1)獲得ReadView的時機。每個語句開始執行時,獲得ReadView,可見性判斷是基於語句級別的ReadView。讀的策略與可重複讀類似。2)寫鎖的使用方式。這裡不需要GAP LOCK,只使用記錄鎖。並且事務只持有被UPDATE/DELETE記錄的寫鎖(可重複讀需要保留意向寫鎖直到事務結束)。
讀未提交(Read Uncommitted)
讀最新的數據,不管這條記錄是不是已提交。不會遍歷版本鏈,少了查找可見的版本的步驟。這樣可能會導致臟讀。對寫仍需要鎖定,策略和讀已提交類似,避免臟寫。
你的推測:ru隔離級別:寫加X鎖,事務提交或回滾釋放X鎖,讀不加鎖rc:寫加排它鎖,讀一條語句看到的是一個snapshoot versionrr:一個事務看到的一個snapshoot
推薦搜下 資料庫內核月報 ,想知道的幾年都有~
資料庫系統實現 你想要的都在這本書裡面
我第一想法是安利你看源碼...(逃ε=ε=ε=┏(゜ロ゜;)┛。。
推薦閱讀:
※MySQL中inner join 和 cross join 的區別?
※mysql如何解決評論遞歸查詢?
※MySQL 和 PostgreSQL 相比,對 JSON 的支持如何?
※SQL中 LEFT JOIN ON 條件的效率高低比較?
※為什麼php在向mysql提交數據時變數外要用單引號?