MySql數據庫——事務隔離級別和鎖關系學習
引言:
對於事務來說,有四種隔離級別,本文通過對多篇博客的理解和匯總,加上實際的效果展示和個人理解,對MySql四種事務隔離級別和鎖進行分析。
一.事務隔離級別:
事務具有ACID屬性,而事務的隔離級別可以不同程度的解決事務並發時可能產生的問題,可以根據不同業務邏輯需求,來選擇不同的事務隔離等級,事務隔離等級越高,越能保證數據的一致性,但就更趨近於串行化,降低並發性能,導致效率變低。
四種事務隔離級別:
未提交讀(RU)
已提交讀(RC)
可重復讀(RR)
序列化/串行化(Serializable)
這四種隔離級別可以在不同程度上解決事務在並發時產生的問題——臟讀,不可重復讀,幻讀。
1.臟讀:有AB兩個事務,B事務對一條數據進行修改,但未提交;而A事務在這之后對同一條數據進行讀操作,讀到的若是未提交的修改后的數據,就說明產生了臟讀現象。
2.不可重復讀:有一個A事務和若干其他事務,A事務對一條數據(或多條符合同一查詢條件的數據)進行多次的讀操作;其他的事務對A事務所讀的數據進行了修改(這里不包括插入新的數據),若是未提交,在RU級別可以讀到修改的數據,若是已提交,在RC級別及以下可以讀到修改的數據;那么這種情況就可能會導致A事務在多次讀取數據的時候,發現讀取的數據值不一致,這種現象被稱為不可重復讀。
3.幻讀:幻讀的概念在網上很難找到一個全面且完全正確的解釋,這里的解釋是我個人的理解,可能會有偏差。
幻讀大致上有兩種情況:
幻讀情況1:同樣的查詢語句,前后兩次讀取,發現數據量的個數發生了改變。(RU,RC級別不能解決這個問題,RR和序列化可以解決這個問題)
幻讀情況2:第二種情況中還可以分為兩種情況一,有AB兩個事務,A事務按某個條件查詢數據,B事務在A事務查詢之后插入了一條符合A事務查詢條件的數據,這里B事務提交與否的情況與不可重復讀的一致,在B事務做完操作后,A事務也想插入B事務剛才插入的數據,但卻發現插入不成功,第一次沒有讀到的數據,但卻插入不成功,這種情況即為幻讀;二,有AB兩個事務,A事務按某個條件查詢數據,B事務在A事務查詢之后,刪除了一條之前A事務查詢到的數據,在B完成操作后,A事務也想把B剛才刪除的數據刪除掉,但發現影響的行數是0,明明查到了,但刪不掉,這種情況也是幻讀,並且在RR的隔離級別下,不僅影響行數為0,再查的時候,數據還依然存在,造成這種現象的原因是RR級別使用了快照讀(在下文會有解釋)。(RR隔離級別可以在一定程度上避免這種情況,序列化可完全避免)
二.鎖的概念:
1.按功能划分:
共享鎖(讀鎖):有A事務對某數據加讀鎖,那么其他事務只可以讀這條數據,但不能修改。
排他鎖(寫鎖):有事務A對某數據加寫鎖,那么只有A事務可以操作此數據,別的事務既不能讀,更不能改。
其實還有兩種意向鎖,但本文內容與意向鎖關聯不大,暫不作介紹。
2.按范圍划分:
行鎖:mysql數據庫下的InnoDB引擎支持行鎖,鎖住一行數據。
表鎖:mysql數據庫下的MyISAM和InnoDB引擎都支持表鎖,鎖住一整張表。
頁鎖在這里不做介紹
3.按用途划分:
樂觀鎖:樂觀鎖其實從實現的角度上,並沒有進行加鎖的操作,而是使用版本號,來控制數據的一致性,例如在提交修改的數據時,會判斷這條修改的數據的版本號是否大於修改前的版本號,若大於則修改。因為樂觀鎖沒有加鎖和解鎖的開銷,所以從效率上比較可觀,但是樂觀鎖的缺點是不可以跨應用操作,所以就產生了悲觀鎖的概念。
悲觀鎖:悲觀鎖是真正的對需要操作的數據進行了加鎖和解鎖的操作,來確保事務並發而帶來的問題,雖然把數據鎖死可以減少錯誤,但加鎖解鎖會加大開銷,效率會相應下降。
三.事務隔離級別與鎖之間的關系:
這部分主要是介紹mysql中四種事務隔離級別是如何實現的,能解決那些並發問題,以及使用了哪些鎖來實現這四種隔離界別。
隔離級別 |
臟讀 |
不可重復讀 |
幻讀 |
未提交讀(RU) |
可能 |
可能 |
可能 |
已提交度(RC) |
不可能 |
可能 |
可能 |
可重復讀(RR) |
不可能 |
不可能 |
可能 |
可序列化(Serializable) |
不可能 |
不可能 |
不可能 |
在開始下面內容之前,需要知道mysq中兩種不同的select方式:
快照讀:讀取的是記錄的歷史版本,在一個沒有結束的事務中,快照讀每次讀取的都和在本次事務中第一次讀到的信息一致(不加讀鎖)。
當前讀:讀取的是記錄的最新版本(會加讀鎖)。
1.未提交讀(RU):
1.1實現機制:
事務在讀數據的時候采用當前讀
事務在修改數據的時候加共享鎖,提交后釋放(解決了修改時,數據被刪除或修改的情況)
1.2臟讀情況:會發生
首先是開啟分別開啟兩個會話,后續會用A事務和B事務來代指這兩個會話,然后把事務隔離級別設置為RU
以下操作按順序進行:
A事務查詢某表信息:
B事務插入新數據,但不提交:
A事務再次查詢此表:讀到了B事務未提交的操作,臟讀情況發生
1.3不可重復讀情況:會發生
A事務查詢id為30的數據:
B事務對id為30的數據進行修改:提交與否不影響結果
A事務再次查詢id為30的數據:與第一次讀的num數據值不同,不可重復讀情況發生
1.4幻讀情況:會發生
A事務查詢id<30的數據:
B事務插入一條id<30的數據:提交與否不影響結果
A事務再次查詢id<30的數據:查到的數據比第一次多一條,幻讀情況1發生
2.已提交讀(RC):
2.1實現機制:
事務在讀數據的時候加讀鎖(當前讀),讀完即釋放共享鎖
事務在修改某數據時會加上寫鎖,直到事務結束再釋放,這樣的機制保證了RC隔離級別不會發生臟讀,只有提交過的事務,才能被其他事務看見
2.2臟讀情況:不會發生
首先是開啟分別開啟兩個會話,后續會用A事務和B事務來代指這兩個會話,然后把事務隔離級別設置為RC。
以下操作按順序進行:
A事務查詢某表信息:
B事務插入新數據,但不提交:
A事務再次查詢此表:沒有讀到B事務插入的數據,臟讀情況沒有發生
2.3不可重復讀情況:會發生
RC隔離級別下的不可重復讀的情況和RU是類似的,只是B事務在修改數據的時候,需要提交。
2.4幻讀情況:會發生
RC隔離級別下的幻讀的情況和RU是類似的,只是B事務在插入數據的時候,需要提交。
3.可重復讀(RR):
3.1實現機制:MVCC(Mysql下的InnoDB引擎為例)
事務在讀數據的時候采用的是快照讀(解決不可重復讀問題)
事務在修改數據的時候加寫鎖,並且Mysql采用了間隙鎖,但觸發間隙鎖的前提是(查詢條件列不可以是唯一索引和主鍵),在觸發間隙鎖后,會鎖住一定范圍內的數據,防止在這范圍內插入數據,這個機制可以在一定程度上降低發生幻讀情況2的可能。
3.2臟讀情況:不會發生
首先是開啟分別開啟兩個會話,后續會用A事務和B事務來代指這兩個會話,然后把事務隔離級別設置為RR。
RR隔離級別下的臟讀的情況和RC一樣
3.3不可重復讀情況:不會發生
A事務查詢id為30的數據:
B事務對id為30的數據進行修改:修改后提交事務
A事務再次查詢id為30的數據:與第一次讀的num數據值相同,沒有發生不可重復讀的情況
3.4幻讀情況:會發生幻讀情況2,不會發生幻讀情況1(雖然其他事務仍可能會insert數據,但是由於RR采用了快照讀,所以不會讀到insert的數據)
幻讀情況2:會發生
A事務查詢全部數據
B事務插入一條新數據:需要提交事務
A事務由於不知B事務插入過這條數據,所以也想插入這條數據:A事務會插入失敗,並且再次全部讀數據的時候,還沒有發現這條因為主鍵約束而插入失敗的數據,這樣的情況即發生了幻讀情況2。
幻讀情況2:會發生
A事務查詢全部數據
B事務刪除A事務查詢的數據中的一條:需要提交事務
A事務由於不知B事務刪除過這條數據,所以也想刪除這條數據:A事務刪除語句執行后,會發現影響的行數為0,並且再次全部讀數據的時候,還發現這條數據沒有被刪除,這樣的情況也是發生了幻讀情況2。
3.5間隙鎖發生情況:若查詢列是上有索引(且不是唯一索引)且不為主鍵,則間隙鎖會觸發一定范圍的鎖,如下面的例子;若查詢列上沒有索引且不是主鍵,那么間隙鎖會為整張表上鎖
A事務查詢全部數據:
A事務修改num=30的數據(num是普通索引),把name改為test:
B事務插入一條num=32的數據:可以插入成功
B事務插入一條num=29的數據:發現插入阻塞,造成這種現象的原因就是因為間隙鎖的作用,由於A事務以num=30的索引列為條件修改了數據,所以間隙鎖把num=30兩端的范圍數據給鎖起來了,即num = (5,30),在這個范圍內的數據都被上鎖了,注意這里都是開區間,在這個范圍外的數據可以被操作。
4.序列化(Serializable):
實現機制:
事務在讀取數據時,對整個表加讀鎖,提交或回滾事務后釋放
事務在修改數據時,對整個表加寫鎖,提交或回滾事務后釋放
這是最高的隔離級別,可以解決臟讀,不可重復讀和幻讀,但同時效率也是最差的一個。它解決這些由於事務並發帶了的問題的方法就是把這些操作變成串行操作,一旦不符合條件,就會被阻塞,所以效率特別差。
參考博客:
虛擬WORLD-er:https://blog.csdn.net/qq_37960007/article/details/90644635?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-1-90644635.nonecase
三石雨:https://www.cnblogs.com/exceptioneye/p/5450874.html
李俊陽:https://www.cnblogs.com/ljy-skill/p/10622865.html
牛初九:https://www.cnblogs.com/boboooo/p/12370770.html#4506259
Luke:https://zhuanlan.zhihu.com/p/109414420