MySQL事務隔離級別和MVCC (undo日志版本鏈,ReadView) MVCC文章勘誤
可重復讀概念
可重復讀的實現
Repeatable Read(可重復讀):一個事務在執行過程中可以看到其他事務已經提交的新插入的記錄(讀已經提交的,其實是讀早於本事務開始且已經提交的),但是不能看到其他事務對已有記錄的更新(即晚於本事務開始的),並且,該事務不要求與其他事務是“可串行化”的。(沒有問題)
這句話的核心,是“但是不能看到其他事務對已有記錄的更新”,那么RR隔離級別是怎么保證這一點的呢?
可重復讀實現原理
事務ID是遞增的。
使用MVCC(多版本並發控制)。InnoDB為每行記錄添加了一個事務ID,每當修改數據時,將當事務ID寫入。
在讀取事務開始時,系統會給事務一個當前版本號(事務ID),事務會讀取版本號<=當前版本號的數據,這時就算另一個事務插入一個數據,並立馬提交,新插入這條數據的版本號會比讀取事務的版本號高,因此讀取事務讀的數據還是不會變。
可重復讀注意點
但是,當前事務想再寫這行數據的話,還是以數據庫提交的數據為准。並且會加行鎖,其他的事務要再來修改的話,就得等到當前事務結束。
舉例:
我們先看看現象,再分析原理。我的mysql版本是5.5。
下面是一張表,只有一條數據,並且我開啟了事物
此時,另一個事物將record加1,因此我在開啟一個命令行客戶端,執行下面的命令:
成功加1之后,實際上,數據庫中record肯定是2。
然后回到之前的客戶端,再查一次:
沒毛病,record還是1,果然是可重復讀。有些人以為mysql的可重復讀是通過行鎖實現的,
從上面可以知道,肯定不是,如果是的話,第一次select * from test的時候,id=1的記錄就會加行鎖,我都加行鎖了,我還沒提交,另外的事物是怎么update成功的。
結論就是mysql使用的MVCC(多版本並發控制),MVCC詳解可以看:https://blog.csdn.net/whoamiyang/article/details/51901888
我們繼續,我之前的第一個事物還沒提交,不過提交之前,我也想加1;
加完之后我再查一下,額,record是3,好像很奇怪,但也不奇怪。
其實,update test set record=record+1 where id=1;這條語句中,在加1之前,他知道自己等於2,然后2+1=3。
也就是說,update時讀取數據是最新版本的數據,而select是到當前事物版本為止的數據。當更新成功之后,當前版本即為最新版本,再次select,讀取的是最新的數據。
在這里討論下樂觀鎖的必要性。下面是樂觀鎖的實現,實現樂觀鎖,我們一般會這么做
update test set record=record+1 where id=1 and record=1;
如果不用樂觀鎖,你用select讀取到的值其實根本不准確。除非你開啟悲觀鎖,像下面這樣:
select * from test where id=1 for update;
這樣可以讀取到最新的內容,同時在你當前的事物提交之前,其他事物的update此條記錄將會鎖等待。
故事到此,還沒有結束,此時我們開啟事物三,也做加1操作看會發生什么。
結果是,鎖等待超時,也就是說(事物一)在更新完后,會加行鎖,這個應該比較好理解。事物中,剛開始查詢的時候是不會加行鎖的,但是當有更新操作之后,會加行鎖,直到事物提交。
Innodb在RR級別如何避免幻讀
幻讀定義
事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。 同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那么,以后就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象 發生了幻覺一樣。
mysql如何實現避免幻讀
1.在快照讀讀情況下,mysql通過mvcc來避免幻讀。
2.在當前讀讀情況下,mysql通過next-key來避免幻。
什么是MVCC
mvcc全稱是multi version concurrent control(多版本並發控制)。mysql把每個操作都定義成一個事務,每開啟一個事務,系統的事務版本號自動遞增。每行記錄都有兩個隱藏列:創建版本號和刪除版本號
select:事務每次只能讀到創建版本號小於等於此次系統版本號的記錄,同時行的刪除版本號不存在或者大於當前事務的版本號。
update:插入一條新記錄,並把當前系統版本號作為行記錄的版本號,同時保存當前系統版本號到原有的行作為刪除版本號。
delete:把當前系統版本號作為行記錄的刪除版本號
insert:把當前系統版本號作為行記錄的版本號
什么是next-key鎖
行鎖+GAP間隙鎖
什么是快照讀和當前讀
快照讀:簡單的select操作,屬於快照讀,不加鎖。(當然,也有例外,下面會分析)
select * from table where ?;
當前讀:特殊的讀操作,插入/更新/刪除操作,屬於當前讀,需要加鎖。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;