預(yù)備知識(shí)
mysql5.5以上版本中默認(rèn)使用的存儲(chǔ)引擎是基于InnoDB,所以本文所有關(guān)機(jī)都是基于InnoDB存儲(chǔ)引擎。
鎖
-
行鎖:顧名思義,該鎖鎖住的是表中數(shù)據(jù)的某一行數(shù)據(jù),共享鎖和互斥鎖都屬于行鎖。另外行鎖可以使用的前提條件是SQL語(yǔ)句必須使用到了索引(如果不知道何為索引,可以先了解一下,當(dāng)然不了解也不影響理解后面的知識(shí)),否則不會(huì)使用行鎖,而是使用表鎖。
共享鎖(S):共享鎖也稱(chēng)為讀鎖,共享鎖和共享鎖之間是兼容的,但和排他鎖不兼容。什么意思呢?假設(shè)事務(wù)A加了一個(gè)共享鎖,那么事務(wù)B是可以申請(qǐng)共享鎖的,但是不可以申請(qǐng)排它鎖。這里要注意一點(diǎn),并不是說(shuō)一定要獲取共享鎖才能讀取數(shù)據(jù),這里得看數(shù)據(jù)庫(kù)是如何實(shí)現(xiàn)的,后面講事務(wù)隔離級(jí)別的時(shí)候會(huì)講到,先提前挖個(gè)坑。
排他鎖(X):排它鎖也稱(chēng)為寫(xiě)鎖,排他鎖與排他鎖以及共享鎖均不兼容。假設(shè)事務(wù)A加了一個(gè)排它鎖,那么其他事務(wù)既不能申請(qǐng)共享鎖,也不能申請(qǐng)排它鎖。
-
表鎖:該鎖鎖住的是表中的所有數(shù)據(jù)。意向共享鎖和意向排它鎖屬于表鎖。
意向共享鎖(IS):事務(wù)在給一個(gè)數(shù)據(jù)行加共享鎖前必須先取得該表的 IS 鎖。
意向排他鎖(IX):事務(wù)在給一個(gè)數(shù)據(jù)行加排他鎖前必須先取得該表的 IX 鎖。
鎖模式的兼容情況:

InnoDB加鎖方法:
意向鎖是 InnoDB 自動(dòng)加的, 不需用戶(hù)干預(yù)。
對(duì)于 UPDATE、 DELETE 和 INSERT 語(yǔ)句, InnoDB 會(huì)自動(dòng)給涉及數(shù)據(jù)集加排他鎖(X);
-
對(duì)于普通 SELECT 語(yǔ)句,InnoDB 不會(huì)加任何鎖(SERIALIZABLE隔離級(jí)別除外); 事務(wù)可以通過(guò)以下語(yǔ)句顯式給記錄集加共享鎖或排他鎖:
(1)SELECT ... LOCK IN SHARE MODE;
(2)SELECT ... FOR UPDATE;
MVCC
每個(gè)連接到數(shù)據(jù)庫(kù)的讀者,在某個(gè)瞬間看到的是數(shù)據(jù)庫(kù)的一個(gè)快照,寫(xiě)者寫(xiě)操作造成的變化在數(shù)據(jù)庫(kù)事務(wù)提交之前對(duì)于其他的讀者來(lái)說(shuō)是不可見(jiàn)的。
事務(wù)的隔離級(jí)別
事務(wù)有四種隔離級(jí)別,不同的隔離級(jí)別對(duì)應(yīng)的加鎖策略是不一樣的,這就導(dǎo)致不同的隔離級(jí)別,解決并發(fā)的能力有所差異。另外還有一個(gè)地方要注意的是某個(gè)事務(wù)如果申請(qǐng)成功一把行鎖,就會(huì)立即給行加上該鎖。
- READ UNCOMMITTED(未提交讀): 在該隔離級(jí)別下,事務(wù)的讀操作不申請(qǐng)共享鎖,寫(xiě)操作申請(qǐng)排它鎖,并在事務(wù)結(jié)束后釋放,導(dǎo)致一個(gè)事務(wù)中的讀操作可以讀到另一個(gè)事務(wù)中還未提交的寫(xiě)操作(臟讀)。注意在該隔離級(jí)別下,讀取未提交的更新不會(huì)被排它鎖阻止,因?yàn)樽x取操作不用申請(qǐng)任何鎖,可以直接讀取數(shù)據(jù)。

-
READ COMMITTED(已提交讀):在該隔離級(jí)別下,事務(wù)的讀操作不申請(qǐng)共享鎖,但是讀取的數(shù)據(jù)是最新的一份快照數(shù)據(jù),寫(xiě)操作申請(qǐng)排它鎖,并在事務(wù)結(jié)束后釋放,導(dǎo)致在一個(gè)事務(wù)中讀取相同的數(shù)據(jù)可能得到不一樣的結(jié)果(不可重復(fù)讀)。
-
REPEATABLE READ(可重復(fù)讀):InnoDB默認(rèn)采取該隔離級(jí)別,在該隔離級(jí)別下,事務(wù)的讀操作不申請(qǐng)共享鎖,但是讀取的數(shù)據(jù)是事務(wù)開(kāi)始時(shí)的快照數(shù)據(jù)(這里可以和READ COMMITTED對(duì)比來(lái)看),寫(xiě)操作申請(qǐng)排它鎖,并在事務(wù)結(jié)束后釋放,該隔離級(jí)別解決了不可重復(fù)讀的問(wèn)題,但是沒(méi)有解決幻讀的問(wèn)題。這里解釋一下幻讀,一個(gè)事務(wù)在前后兩次查詢(xún)同一范圍的數(shù)據(jù)時(shí),得到了不同的結(jié)果(由于其他事務(wù)插入或刪除了該范圍的某些數(shù)據(jù)),這就是幻讀。
-
SERIALIAZBLE(串行化):在該隔離級(jí)別下,事務(wù)的讀操作申請(qǐng)一個(gè)共享鎖,事務(wù)的寫(xiě)操作申請(qǐng)一個(gè)排它鎖。當(dāng)我們用范圍條件而不是相等條件檢索數(shù)據(jù),并請(qǐng)求共享或排他鎖時(shí),InnoDB會(huì)給符合條件的已有數(shù)據(jù)記錄的索引項(xiàng)加鎖;對(duì)于鍵值在條件范圍內(nèi)但并不存在的記錄,叫做“間隙(GAP)”,InnoDB也會(huì)對(duì)這個(gè)“間隙”加鎖,這種鎖機(jī)制就是所謂的間隙鎖(Next-Key鎖),通過(guò)間隙鎖可以解決幻讀的問(wèn)題。
下面列出每個(gè)隔離級(jí)別對(duì)應(yīng)存在的問(wèn)題:
| 隔離級(jí)別 | 臟讀 | 不可重復(fù)讀 | 幻讀 |
|---|---|---|---|
| READ UNCOMMITED | √ | √ | √ |
| READ COMMITTED | × | √ | √ |
| REPEATABLE READ | × | × | √ |
| SERIALIZABLE | × | × | × |
打臉
我用MySQL5.7測(cè)了一下子,發(fā)現(xiàn)InnoDB 存儲(chǔ)引擎在REPEATABLE READ隔離級(jí)別下也解決了幻讀的問(wèn)題,于是上網(wǎng)查了以下,說(shuō)是原理和SERIALIAZBLE一樣,在REPEATABLE READ隔離級(jí)別也是使用next-key鎖對(duì)范圍讀操作進(jìn)行加間隙鎖,但是如果使用間隙鎖,按道理事務(wù)A范圍查詢(xún)的時(shí)候,事務(wù)B是不能進(jìn)行delete和insert操作的,測(cè)試發(fā)現(xiàn)REPEATABLE READ隔離級(jí)別下是可以的,具體原因不得而知,如果有知道的大佬請(qǐng)留言,謝謝。以上均為自己對(duì)于事務(wù)隔離級(jí)別的一些理解,一定會(huì)存在一些錯(cuò)誤,歡迎大家拿出證據(jù)板磚伺候。
如果不知道如何測(cè)試,可以參考以下鏈接:https://zhuanlan.zhihu.com/p/36060546


