以上不是重點(diǎn),重點(diǎn)是 對(duì)事務(wù)控制語(yǔ)句不熟悉。
創(chuàng)新互聯(lián)網(wǎng)絡(luò)公司擁有10年的成都網(wǎng)站開發(fā)建設(shè)經(jīng)驗(yàn),上1000+客戶的共同信賴。提供網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)、網(wǎng)站開發(fā)、網(wǎng)站定制、賣友情鏈接、建網(wǎng)站、網(wǎng)站搭建、響應(yīng)式網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)師打造企業(yè)風(fēng)格,提供周到的售前咨詢和貼心的售后服務(wù)
SAVEPOINT identifier : 在事務(wù)中 創(chuàng)建保存點(diǎn)。一個(gè)事務(wù)中 允許有多個(gè)保存點(diǎn)。
RELEASE SAVEPOINT identifier : 刪除保存點(diǎn)。當(dāng)事務(wù)中 沒(méi)有指定的 保存點(diǎn),執(zhí)行該語(yǔ)句 會(huì)拋異常。
ROLLBACK TO identifier : 把事務(wù)回滾到 保存點(diǎn)。
Say you have a table T with a column C with one row in it, say it has the value '1'. And consider you have a simple task like following:
That is a simple task that issue two reads from table T, with a delay of 1 minute between them.
If you follow the logic above you can quickly realize that SERIALIZABLE transactions, while they may make life easy for you, are always completely blocking every possible concurrent operation, since they require that nobody can modify, delete nor insert any row. The default transaction isolation level of the .Net System.Transactions scope is serializable, and this usually explains the abysmal performance that results.
在Repeatable Read隔離級(jí)別下,一個(gè)事務(wù)可能會(huì)遇到幻讀(Phantom Read)的問(wèn)題。
幻讀是指,在一個(gè)事務(wù)中,第一次查詢某條記錄,發(fā)現(xiàn)沒(méi)有,但是,當(dāng)試圖更新這條不存在的記錄時(shí),竟然能成功,并且,再次讀取同一條記錄,它就神奇地出現(xiàn)了。
我們?nèi)匀幌葴?zhǔn)備好students表的數(shù)據(jù):
然后,分別開啟兩個(gè)MySQL客戶端連接,按順序依次執(zhí)行事務(wù)A和事務(wù)B:
事務(wù)B在第3步第一次讀取id=99的記錄時(shí),讀到的記錄為空,說(shuō)明不存在id=99的記錄。隨后,事務(wù)A在第4步插入了一條id=99的記錄并提交。事務(wù)B在第6步再次讀取id=99的記錄時(shí),讀到的記錄仍然為空,但是,事務(wù)B在第7步試圖更新這條不存在的記錄時(shí),竟然成功了,并且,事務(wù)B在第8步再次讀取id=99的記錄時(shí),記錄出現(xiàn)了。
可見,幻讀就是沒(méi)有讀到的記錄,以為不存在,但其實(shí)是可以更新成功的,并且,更新成功后,再次讀取,就出現(xiàn)了。
在沖突較少的情況下,使用樂(lè)觀鎖。樂(lè)觀鎖 因?yàn)闆](méi)有 加鎖 釋放鎖,也減少了 加鎖 釋放鎖的開銷。
沖突較多時(shí),如果使用樂(lè)觀鎖 需要不停地嘗試,所以 使用悲觀鎖。
如果樂(lè)觀鎖 進(jìn)行嘗試時(shí) 的花銷較大,也是使用悲觀鎖。
加鎖情況與死鎖原因分析
為方便大家復(fù)現(xiàn),完整表結(jié)構(gòu)和數(shù)據(jù)如下:
CREATE TABLE `t3` (
`c1` int(11) NOT NULL AUTO_INCREMENT,
`c2` int(11) DEFAULT NULL,
PRIMARY KEY (`c1`),
UNIQUE KEY `c2` (`c2`)
) ENGINE=InnoDB
insert into t3 values(1,1),(15,15),(20,20);
在 session1 執(zhí)行 commit 的瞬間,我們會(huì)看到 session2、session3 的其中一個(gè)報(bào)死鎖。這個(gè)死鎖是這樣產(chǎn)生的:
1.?session1 執(zhí)行 delete ?會(huì)在唯一索引 c2 的 c2 = 15 這一記錄上加 X lock(也就是在MySQL 內(nèi)部觀測(cè)到的:X Lock but not gap);
2.?session2 和 session3 在執(zhí)行 insert 的時(shí)候,由于唯一約束檢測(cè)發(fā)生唯一沖突,會(huì)加 S Next-Key Lock,即對(duì) (1,15] 這個(gè)區(qū)間加鎖包括間隙,并且被 seesion1 的 X Lock 阻塞,進(jìn)入等待;
3.?session1 在執(zhí)行 commit 后,會(huì)釋放 X Lock,session2 和 session3 都獲得 S Next-Key Lock;
4.?session2 和 session3 繼續(xù)執(zhí)行插入操作,這個(gè)時(shí)候 INSERT INTENTION LOCK(插入意向鎖)出現(xiàn)了,并且由于插入意向鎖會(huì)被 gap 鎖阻塞,所以 session2 和 session3 互相等待,造成死鎖。
死鎖日志如下:
請(qǐng)點(diǎn)擊輸入圖片描述
INSERT INTENTION LOCK
在之前的死鎖分析第四點(diǎn),如果不分析插入意向鎖,也是會(huì)造成死鎖的,因?yàn)椴迦胱罱K還是要對(duì)記錄加 X Lock 的,session2 和 session3 還是會(huì)互相阻塞互相等待。
但是插入意向鎖是客觀存在的,我們可以在官方手冊(cè)中查到,不可忽略:
Prior to inserting the row, a type of gap lock called an insert intention gap lock is set. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap.
插入意向鎖其實(shí)是一種特殊的 gap lock,但是它不會(huì)阻塞其他鎖。假設(shè)存在值為 4 和 7 的索引記錄,嘗試插入值 5 和 6 的兩個(gè)事務(wù)在獲取插入行上的排它鎖之前使用插入意向鎖鎖定間隙,即在(4,7)上加 gap lock,但是這兩個(gè)事務(wù)不會(huì)互相沖突等待。
當(dāng)插入一條記錄時(shí),會(huì)去檢查當(dāng)前插入位置的下一條記錄上是否存在鎖對(duì)象,如果下一條記錄上存在鎖對(duì)象,就需要判斷該鎖對(duì)象是否鎖住了 gap。如果 gap 被鎖住了,則插入意向鎖與之沖突,進(jìn)入等待狀態(tài)(插入意向鎖之間并不互斥)??偨Y(jié)一下這把鎖的屬性:
1. 它不會(huì)阻塞其他任何鎖;
2. 它本身僅會(huì)被 gap lock 阻塞。
在學(xué)習(xí) MySQL 過(guò)程中,一般只有在它被阻塞的時(shí)候才能觀察到,所以這也是它常常被忽略的原因吧...
GAP LOCK
在此例中,另外一個(gè)重要的點(diǎn)就是 gap lock,通常情況下我們說(shuō)到 gap lock 都只會(huì)聯(lián)想到 REPEATABLE-READ 隔離級(jí)別利用其解決幻讀。但實(shí)際上在 READ-COMMITTED 隔離級(jí)別,也會(huì)存在 gap lock ,只發(fā)生在:唯一約束檢查到有唯一沖突的時(shí)候,會(huì)加 S Next-key Lock,即對(duì)記錄以及與和上一條記錄之間的間隙加共享鎖。
通過(guò)下面這個(gè)例子就能驗(yàn)證:
請(qǐng)點(diǎn)擊輸入圖片描述
這里 session1 插入數(shù)據(jù)遇到唯一沖突,雖然報(bào)錯(cuò),但是對(duì) (15,20] 加的 S Next-Key Lock 并不會(huì)馬上釋放,所以 session2 被阻塞。另外一種情況就是本文開始的例子,當(dāng) session2 插入遇到唯一沖突但是因?yàn)楸?X Lock 阻塞,并不會(huì)立刻報(bào)錯(cuò) “Duplicate key”,但是依然要等待獲取 S Next-Key Lock 。
有個(gè)困惑很久的疑問(wèn):出現(xiàn)唯一沖突需要加 S Next-Key Lock 是事實(shí),但是加鎖的意義是什么?還是說(shuō)是通過(guò) S Next-Key Lock 來(lái)實(shí)現(xiàn)的唯一約束檢查,但是這樣意味著在插入沒(méi)有遇到唯一沖突的時(shí)候,這個(gè)鎖會(huì)立刻釋放,這不符合二階段鎖原則。這點(diǎn)希望能與大家一起討論得到好的解釋。
如果是在 REPEATABLE-READ,除以上所說(shuō)的唯一約束沖突外,gap lock 的存在是這樣的:
普通索引(非唯一索引)的S/X Lock,都帶 gap 屬性,會(huì)鎖住記錄以及前1條記錄到后1條記錄的左閉右開區(qū)間,比如有[4,6,8]記錄,delete 6,則會(huì)鎖住[4,8)整個(gè)區(qū)間。
對(duì)于 gap lock,相信 DBA 們的心情是一樣一樣的,所以我的建議是:
1. 在絕大部分的業(yè)務(wù)場(chǎng)景下,都可以把 MySQL 的隔離界別設(shè)置為 READ-COMMITTED;
2. 在業(yè)務(wù)方便控制字段值唯一的情況下,盡量減少表中唯一索引的數(shù)量。
鎖沖突矩陣
前面我們說(shuō)的 GAP LOCK 其實(shí)是鎖的屬性,另外我們知道 InnoDB 常規(guī)鎖模式有:S 和 X,即共享鎖和排他鎖。鎖模式和鎖屬性是可以隨意組合的,組合之后的沖突矩陣如下,這對(duì)我們分析死鎖很有幫助:
請(qǐng)點(diǎn)擊輸入圖片描述
SQL標(biāo)準(zhǔn)定義了4類隔離級(jí)別,包括了一些具體規(guī)則,用來(lái)限定事務(wù)內(nèi)外的哪些改變是可見的,哪些是不可見的。低級(jí)別的隔離級(jí)一般支持更高的并發(fā)處理,并擁有更低的系統(tǒng)開銷。
Read Uncommitted(讀取未提交內(nèi)容)
在該隔離級(jí)別,所有事務(wù)都可以看到其他未提交事務(wù)的執(zhí)行結(jié)果。本隔離級(jí)別很少用于實(shí)際應(yīng)用,因?yàn)樗男阅芤膊槐绕渌?jí)別好多少。讀取未提交的數(shù)據(jù),也被稱之為臟讀(Dirty Read)。
Read Committed(讀取提交內(nèi)容)
這是大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)的默認(rèn)隔離級(jí)別(但不是MySQL默認(rèn)的)。它滿足了隔離的簡(jiǎn)單定義:一個(gè)事務(wù)只能看見已經(jīng)提交事務(wù)所做的改變。這種隔離級(jí)別 也支持所謂的不可重復(fù)讀(Nonrepeatable Read),因?yàn)橥皇聞?wù)的其他實(shí)例在該實(shí)例處理其間可能會(huì)有新的commit,所以同一select可能返回不同結(jié)果。
Repeatable Read(可重讀)
這是MySQL的默認(rèn)事務(wù)隔離級(jí)別,它確保同一事務(wù)的多個(gè)實(shí)例在并發(fā)讀取數(shù)據(jù)時(shí),會(huì)看到同樣的數(shù)據(jù)行。不過(guò)理論上,這會(huì)導(dǎo)致另一個(gè)棘手的問(wèn)題:幻讀 (Phantom Read)。簡(jiǎn)單的說(shuō),幻讀指當(dāng)用戶讀取某一范圍的數(shù)據(jù)行時(shí),另一個(gè)事務(wù)又在該范圍內(nèi)插入了新行,當(dāng)用戶再讀取該范圍的數(shù)據(jù)行時(shí),會(huì)發(fā)現(xiàn)有新的“幻影” 行。InnoDB和Falcon存儲(chǔ)引擎通過(guò)多版本并發(fā)控制(MVCC,Multiversion Concurrency Control)機(jī)制解決了該問(wèn)題。
Serializable(可串行化)
這是最高的隔離級(jí)別,它通過(guò)強(qiáng)制事務(wù)排序,使之不可能相互沖突,從而解決幻讀問(wèn)題。簡(jiǎn)言之,它是在每個(gè)讀的數(shù)據(jù)行上加上共享鎖。在這個(gè)級(jí)別,可能導(dǎo)致大量的超時(shí)現(xiàn)象和鎖競(jìng)爭(zhēng)。
這四種隔離級(jí)別采取不同的鎖類型來(lái)實(shí)現(xiàn),若讀取的是同一個(gè)數(shù)據(jù)的話,就容易發(fā)生問(wèn)題。例如:
臟讀(Drity Read):某個(gè)事務(wù)已更新一份數(shù)據(jù),另一個(gè)事務(wù)在此時(shí)讀取了同一份數(shù)據(jù),由于某些原因,前一個(gè)RollBack了操作,則后一個(gè)事務(wù)所讀取的數(shù)據(jù)就會(huì)是不正確的。
不可重復(fù)讀(Non-repeatable read):在一個(gè)事務(wù)的兩次查詢之中數(shù)據(jù)不一致,這可能是兩次查詢過(guò)程中間插入了一個(gè)事務(wù)更新的原有的數(shù)據(jù)。
幻讀(Phantom Read):在一個(gè)事務(wù)的兩次查詢中數(shù)據(jù)筆數(shù)不一致,例如有一個(gè)事務(wù)查詢了幾列(Row)數(shù)據(jù),而另一個(gè)事務(wù)卻在此時(shí)插入了新的幾列數(shù)據(jù),先前的事務(wù)在接下來(lái)的查詢中,就會(huì)發(fā)現(xiàn)有幾列數(shù)據(jù)是它先前所沒(méi)有的。
在MySQL中,實(shí)現(xiàn)了這四種隔離級(jí)別,分別有可能產(chǎn)生問(wèn)題如下所示:
術(shù)式之后皆為邏輯,一切皆為需求和實(shí)現(xiàn)。希望此文能從需求、現(xiàn)狀和解決方式的角度幫大家理解隔離級(jí)別。
隔離級(jí)別的產(chǎn)生
在串型執(zhí)行的條件下,數(shù)據(jù)修改的順序是固定的、可預(yù)期的結(jié)果,但是并發(fā)執(zhí)行的情況下,數(shù)據(jù)的修改是不可預(yù)期的,也不固定,為了實(shí)現(xiàn)數(shù)據(jù)修改在并發(fā)執(zhí)行的情況下得到一個(gè)固定、可預(yù)期的結(jié)果,由此產(chǎn)生了隔離級(jí)別。
所以隔離級(jí)別的作用是用來(lái)平衡數(shù)據(jù)庫(kù)并發(fā)訪問(wèn)與數(shù)據(jù)一致性的方法。
事務(wù)的4種隔離級(jí)別
READ UNCOMMITTED ? ? ? 未提交讀,可以讀取未提交的數(shù)據(jù)。READ COMMITTED ? ? ? ? 已提交讀,對(duì)于鎖定讀(select with for update 或者 for share)、update 和 delete 語(yǔ)句, ? ? ? ? ? ? ? ? ? ? ? InnoDB 僅鎖定索引記錄,而不鎖定它們之間的間隙,因此允許在鎖定的記錄旁邊自由插入新記錄。 ? ? ? ? ? ? ? ? ? ? ? Gap locking 僅用于外鍵約束檢查和重復(fù)鍵檢查。REPEATABLE READ ? ? ? ?可重復(fù)讀,事務(wù)中的一致性讀取讀取的是事務(wù)第一次讀取所建立的快照。SERIALIZABLE ? ? ? ? ? 序列化
在了解了 4 種隔離級(jí)別的需求后,在采用鎖控制隔離級(jí)別的基礎(chǔ)上,我們需要了解加鎖的對(duì)象(數(shù)據(jù)本身間隙),以及了解整個(gè)數(shù)據(jù)范圍的全集組成。
數(shù)據(jù)范圍全集組成
SQL 語(yǔ)句根據(jù)條件判斷不需要掃描的數(shù)據(jù)范圍(不加鎖);
SQL 語(yǔ)句根據(jù)條件掃描到的可能需要加鎖的數(shù)據(jù)范圍;
以單個(gè)數(shù)據(jù)范圍為例,數(shù)據(jù)范圍全集包含:(數(shù)據(jù)范圍不一定是連續(xù)的值,也可能是間隔的值組成)
1. 數(shù)據(jù)已經(jīng)填充了整個(gè)數(shù)據(jù)范圍:(被完全填充的數(shù)據(jù)范圍,不存在數(shù)據(jù)間隙)
整形,對(duì)值具有唯一約束條件的數(shù)據(jù)范圍 1~5 ,
已有數(shù)據(jù)1、2、3、4、5,此時(shí)數(shù)據(jù)范圍已被完全填充;
整形,對(duì)值具有唯一約束條件的數(shù)據(jù)范圍 1 和 5 ,
已有數(shù)據(jù)1、5,此時(shí)數(shù)據(jù)范圍已被完全填充;
2. 數(shù)據(jù)填充了部分?jǐn)?shù)據(jù)范圍:(未被完全填充的數(shù)據(jù)范圍,是存在數(shù)據(jù)間隙)
整形的數(shù)據(jù)范圍 1~5 ,
已有數(shù)據(jù) 1、2、3、4、5,但是因?yàn)闆](méi)有唯一約束,
所以數(shù)據(jù)范圍可以繼續(xù)被 1~5 的數(shù)據(jù)重復(fù)填充;
整形,具有唯一約束條件的數(shù)據(jù)范圍 1~5 ,
已有數(shù)據(jù) 2,5,此時(shí)數(shù)據(jù)范圍未被完全填充,還可以填充 1、3、4 ;
3. 數(shù)據(jù)范圍內(nèi)沒(méi)有任何數(shù)據(jù)(存在間隙)
如下:
整形的數(shù)據(jù)范圍 1~5 ,數(shù)據(jù)范圍內(nèi)當(dāng)前沒(méi)有任何數(shù)據(jù)。
在了解了數(shù)據(jù)全集的組成后,我們?cè)賮?lái)看看事務(wù)并發(fā)時(shí),會(huì)帶來(lái)的問(wèn)題。
無(wú)控制的并發(fā)所帶來(lái)的問(wèn)題
并發(fā)事務(wù)如果不加以控制的話會(huì)帶來(lái)一些問(wèn)題,主要包括以下幾種情況。
1. 范圍內(nèi)已有數(shù)據(jù)更改導(dǎo)致的:
更新丟失:當(dāng)多個(gè)事務(wù)選擇了同一行,然后基于最初選定的值更新該行時(shí),
由于每個(gè)事物不知道其他事務(wù)的存在,最后的更新就會(huì)覆蓋其他事務(wù)所做的更新;
臟讀: 一個(gè)事務(wù)正在對(duì)一條記錄做修改,這個(gè)事務(wù)完成并提交前,這條記錄就處于不一致狀態(tài)。
這時(shí),另外一個(gè)事務(wù)也來(lái)讀取同一條記錄,如果不加控制,
第二個(gè)事務(wù)讀取了這些“臟”數(shù)據(jù),并據(jù)此做了進(jìn)一步的處理,就會(huì)產(chǎn)生提交的數(shù)據(jù)依賴關(guān)系。
這種現(xiàn)象就叫“臟讀”。
2. 范圍內(nèi)數(shù)據(jù)量發(fā)生了變化導(dǎo)致:
不可重復(fù)讀:一個(gè)事務(wù)在讀取某些數(shù)據(jù)后的某個(gè)時(shí)間,再次讀取以前讀過(guò)的數(shù)據(jù),
卻發(fā)現(xiàn)其讀出的數(shù)據(jù)已經(jīng)發(fā)生了改變,或者某些記錄已經(jīng)被刪除了。
這種現(xiàn)象就叫“不可重復(fù)讀”。
幻讀:一個(gè)事務(wù)按相同的查詢條件重新讀取以前檢索過(guò)的數(shù)據(jù),
卻發(fā)現(xiàn)其他事務(wù)插入了滿足其查詢條件的新數(shù)據(jù),這種現(xiàn)象稱為“幻讀”。
可以簡(jiǎn)單的認(rèn)為滿足條件的數(shù)據(jù)量變化了。
因?yàn)闊o(wú)控制的并發(fā)會(huì)帶來(lái)一系列的問(wèn)題,這些問(wèn)題會(huì)導(dǎo)致無(wú)法滿足我們所需要的結(jié)果。因此我們需要控制并發(fā),以實(shí)現(xiàn)我們所期望的結(jié)果(隔離級(jí)別)。
MySQL 隔離級(jí)別的實(shí)現(xiàn)
InnoDB 通過(guò)加鎖的策略來(lái)支持這些隔離級(jí)別。
行鎖包含:
Record Locks
索引記錄鎖,索引記錄鎖始終鎖定索引記錄,即使表中未定義索引,
這種情況下,InnoDB 創(chuàng)建一個(gè)隱藏的聚簇索引,并使用該索引進(jìn)行記錄鎖定。
Gap Locks
間隙鎖是索引記錄之間的間隙上的鎖,或者對(duì)第一條記錄之前或者最后一條記錄之后的鎖。
間隙鎖是性能和并發(fā)之間權(quán)衡的一部分。
對(duì)于無(wú)間隙的數(shù)據(jù)范圍不需要間隙鎖,因?yàn)闆](méi)有間隙。
Next-Key Locks
索引記錄上的記錄鎖和索引記錄之前的 gap lock 的組合。
假設(shè)索引包含 10、11、13 和 20。
可能的next-key locks包括以下間隔,其中圓括號(hào)表示不包含間隔端點(diǎn),方括號(hào)表示包含端點(diǎn):
(負(fù)無(wú)窮大, 10] ? ?(10, 11] ? ?(11, 13] ? ?(13, 20] ? ?(20, 正無(wú)窮大) ? ? ? ?對(duì)于最后一個(gè)間隔,next-key將會(huì)鎖定索引中最大值的上方,
左右滑動(dòng)進(jìn)行查看
"上確界"偽記錄的值高于索引中任何實(shí)際值。
上確界不是一個(gè)真正的索引記錄,因此,實(shí)際上,這個(gè) next-key 只鎖定最大索引值之后的間隙。
基于此,當(dāng)獲取的數(shù)據(jù)范圍中,數(shù)據(jù)已填充了所有的數(shù)據(jù)范圍,那么此時(shí)是不存在間隙的,也就不需要 gap lock。
對(duì)于數(shù)據(jù)范圍內(nèi)存在間隙的,需要根據(jù)隔離級(jí)別確認(rèn)是否對(duì)間隙加鎖。
默認(rèn)的 REPEATABLE READ 隔離級(jí)別,為了保證可重復(fù)讀,除了對(duì)數(shù)據(jù)本身加鎖以外,還需要對(duì)數(shù)據(jù)間隙加鎖。
READ COMMITTED 已提交讀,不匹配行的記錄鎖在 MySQL 評(píng)估了 where 條件后釋放。
對(duì)于 update 語(yǔ)句,InnoDB 執(zhí)行 "semi-consistent" 讀取,這樣它會(huì)將最新提交的版本返回到 MySQL,
以便 MySQL 可以確定該行是否與 update 的 where 條件相匹配。
總結(jié)延展:
唯一索引存在唯一約束,所以變更后的數(shù)據(jù)若違反了唯一約束的原則,則會(huì)失敗。
當(dāng) where 條件使用二級(jí)索引篩選數(shù)據(jù)時(shí),會(huì)對(duì)二級(jí)索引命中的條目和對(duì)應(yīng)的聚簇索引都加鎖;所以其他事務(wù)變更命中加鎖的聚簇索引時(shí),都會(huì)等待鎖。
行鎖的增加是一行一行增加的,所以可能導(dǎo)致并發(fā)情況下死鎖的發(fā)生。
例如,
在 session A 對(duì)符合條件的某聚簇索引加鎖時(shí),可能 session B 已持有該聚簇索引的 Record Locks,而 session B 正在等待 session A 已持有的某聚簇索引的 Record Locks。
session A 和 session B 是通過(guò)兩個(gè)不相干的二級(jí)索引定位到的聚簇索引。
session A 通過(guò)索引 idA,session B通過(guò)索引 idB 。
當(dāng) where 條件獲取的數(shù)據(jù)無(wú)間隙時(shí),無(wú)論隔離級(jí)別為 rc 或 rr,都不會(huì)存在間隙鎖。
比如通過(guò)唯一索引獲取到了已完全填充的數(shù)據(jù)范圍,此時(shí)不需要間隙鎖。
間隙鎖的目的在于阻止數(shù)據(jù)插入間隙,所以無(wú)論是通過(guò) insert 或 update 變更導(dǎo)致的間隙內(nèi)數(shù)據(jù)的存在,都會(huì)被阻止。
rc 隔離級(jí)別模式下,查詢和索引掃描將禁用 gap locking,此時(shí) gap locking 僅用于外鍵約束檢查和重復(fù)鍵檢查(主要是唯一性檢查)。
rr 模式下,為了防止幻讀,會(huì)加上 Gap Locks。
事務(wù)中,SQL 開始則加鎖,事務(wù)結(jié)束才釋放鎖。
就鎖類型而言,應(yīng)該有優(yōu)化鎖,鎖升級(jí)等,例如rr模式未使用索引查詢的情況下,是否可以直接升級(jí)為表鎖。
就鎖的應(yīng)用場(chǎng)景而言,在回放場(chǎng)景中,如果確定事務(wù)可并發(fā),則可以考慮不加鎖,加快回放速度。
鎖只是并發(fā)控制的一種粒度,只是一個(gè)很小的部分:
從不同場(chǎng)景下是否需要控制并發(fā),(已知無(wú)交集且有序的數(shù)據(jù)的變更,MySQL 的 MTS 相同前置事務(wù)的多事務(wù)并發(fā)回放)
并發(fā)控制的粒度,(鎖是一種邏輯粒度,可能還存在物理層和其他邏輯粒度或方式)
相同粒度下的優(yōu)化,(鎖本身存在優(yōu)化,如IX、IS類型的優(yōu)化鎖)
粒度加載的安全性能(如獲取行鎖前,先獲取頁(yè)鎖,頁(yè)鎖在執(zhí)行獲取行鎖操作后即釋放,無(wú)論是否獲取成功)等多個(gè)層次去思考并發(fā)這玩意。