排他鎖(Exclusive Lock)
排他鎖(Exclusive Lock) , 簡稱X鎖。
若事務T對數據對象A加上X鎖,則只允許T讀取和修改A,其他任何事務都不能再對A加任何類型的鎖,直到T釋放A上的鎖。這就保證了其他事務在T釋放A上的鎖之前不能再讀取和修改A。
規則1:寫一個數據之前加X鎖, 事務提交之后釋放該X鎖。
共享鎖(Share lock)
共享鎖(Share lock) ,簡稱S鎖, 這個鎖和之前的排他鎖X鎖有區別, 主要用於讀取數據。
如果一個數據加了X鎖, 就沒法加S鎖,沒法再加X鎖。
如果一個數據加了S鎖, 就可以加S鎖,沒法再加X鎖。
規則1:讀一個數據之前加S鎖, 讀完之后立刻釋放該S鎖。
規則2:讀一個數據之前加S鎖, 事務提交之后立刻釋放該S鎖。
“丟失修改”的問題
事務1的修改被事務2的修改覆蓋了,事務1的修改像是丟失了。
排他鎖可以解決兩個人同時修改導致的“丟失修改”的問題,事務1修改的時候不能被其他事務修改。
讀未提交(最低的事務隔離級別)-臟數據
現象:不會丟失數據,事務1還未提交的修改能被事務2讀取。可以讀到沒有提交或者回滾的內容 (臟數據)。
原理:寫數據時加上X鎖,直到事務結束, 讀的時候不加鎖。
讀已提交-不可重復讀
現象:能避免“丟失數據”和“臟數據”,事務1能讀到其他已提交事務的修改,出現“不可重復讀”的問題。
原理:寫數據的時候加上X鎖, 直到事務結束, 讀的時候加上S鎖, 讀完數據立刻釋放。(共享鎖規則1)
可重復讀-幻讀
現象:能避免“丟失數據”和“臟數據”, “不可重復讀”三個問題,事務1能讀取到其他事務新插入讀數據,出現“幻讀”的問題。
原理:寫數據的時候加上X鎖, 直到事務結束, 讀數據的時候加S鎖, 也是直到事務結束。(共享鎖規則2)
Serializable (串行化)
現象:能避免“丟失數據”和“臟數據”, “不可重復讀”,“幻讀”四個問題。
原理:嚴格有序執行,事務不能並發執行。
MVCC(多版本並發控制)
“串行化”隔離級別,雖然不會出錯,但是效率實在太低了。 避免使用!!
“可重復讀”,雖然會出現幻讀,但是也能忍受。但為了實現可重復讀, 需要在事務中對讀操作加鎖,並且得持續到整個事務結束,效率也一般,可選擇使用。
隔離級別在可重復讀和讀已提交情況下,有沒有可能在在讀的時候不用加鎖,也能實現可重復讀?
MVCC實現了保證可重復讀並在讀數據的時候不需要加鎖操作!! ps:但是在寫數據的時候,MySQL還是要加鎖的,防止寫-寫沖突。讀寫不互相等待,能極大地提高數據庫的並發能力啊。
原理
給數據庫里面的每張表加兩個隱藏的字段:事務ID,回滾指針。(事務ID就表明這一行數據是哪個事務操作的,事務ID是一個遞增的數字,每次開始新事務,這個數字就會增加。)
擴展一個Read View的數據結構記錄版本數據,它有三個部分:
(1) 當前活躍的事務列表 ,即[101,102]
(2) Tmin ,就是活躍事務的最小值, 在這里 Tmin = 101
(3) Tmax, 是系統中最大事務ID(不管事務是否提交)加上1。 在這里例子中,Tmax = 103
(注: 在可重復讀的隔離級別下,當第一個Read操作發生的時候,Read view就會建立。 在Read Committed隔離級別下,每次發出Read操作,都會建立新的Read view。)
流程變化
原始數據
事務ID | 回滾指針 | name | age |
100 | null | sf | 30 |
開啟兩個事務
事務id101 | 事務id102 |
開始事務 標記1 | |
select * from users where name='sf'; | 開始事務 |
do others things | update users set age = 35 where name='sf'; 標記2 提交事務 |
select * from users where name='sf'; |
在標號1 的地方,數據是這樣的: 與此同時,需要建立一個叫做Read View的數據結構。
事務ID | 回滾指針 | name | age |
100 | null | sf | 30 |
在標號為2的地方,數據是這樣的:事務2做了修改,所以事務ID修改為102,回滾指針指向上一個事務ID的數據,即事務ID100
事務ID | 回滾指針 | name | age |
102 | 上一版本的數據 | sf | 35 |
按照可重復讀的要求,事務1無論讀多少次總能讀到age=30的那行記錄,即使事務2修改了age,也不受影響。
那么如何用來判斷這些數據版本記錄中哪些對你來說是可見的(可讀的)。 ”
對於上面的例子,ReadView 中事務列表是[101,102], Tmin= 101, Tmax = 103,第一次讀和第二次讀是什么樣子。
當事務101第一次讀的時候,只有一條記錄, tid = 100 ,小於Tmin,所以是可以讀的。 然后事務102做了修改。
當事務101第二次讀的時候,tid=102,程序走到了‘tid是否在Read View中這一分支,由於102確實在Read View的活動事務列表中,那就順着回滾指針找到下一行記錄,即tid為100那一行,再次判斷,這就和第一次讀一樣了。