介紹下InnoDB的鎖機(jī)制?你學(xué)會了嗎?
在InnoDB中,鎖可以分為兩種級別,一種是共享鎖(S鎖),另一種是排他鎖(X鎖)。
共享鎖&排他鎖
共享鎖又稱為讀鎖,由讀取操作創(chuàng)建。其他用戶可以并發(fā)讀取數(shù)據(jù),但直到所有共享鎖都被釋放之前,任何事務(wù)都無法對數(shù)據(jù)進(jìn)行修改(獲得數(shù)據(jù)上的排他鎖)。
如果事務(wù)T對數(shù)據(jù)A加上共享鎖后,其他事務(wù)只能對A再加共享鎖,而不能加排他鎖。獲得共享鎖的事務(wù)只能讀取數(shù)據(jù),而不能修改數(shù)據(jù)。
SELECT ... LOCK IN SHARE MODE;
在查詢語句后添加LOCK IN SHARE MODE,MySQL會為查詢結(jié)果中的每行加上共享鎖。只有當(dāng)沒有其他線程對查詢結(jié)果集中的任意行使用排他鎖時,才能成功獲取共享鎖;否則將被阻塞。其他線程可以讀取已經(jīng)被加了共享鎖的表,且這些線程將讀取相同版本的數(shù)據(jù)。
排他鎖又稱為寫鎖,一旦事務(wù)T對數(shù)據(jù)A加上排他鎖,其他事務(wù)就無法再對A加任何類型的鎖。獲得排他鎖的事務(wù)既可讀取數(shù)據(jù),又可修改數(shù)據(jù)。
SELECT ... FOR UPDATE;
除了S鎖和X鎖之外,InnoDB還有另外兩種鎖,分別是IX鎖和IS鎖,這里的"I"代表著"Intention",即意向鎖。IX即意向排他鎖,IS即意向共享鎖。
在查詢語句后添加FOR UPDATE,MySQL會對查詢命中的每條記錄都加排他鎖(如果有索引,則通過索引加鎖;如果沒有索引,則會鎖定整個表)。只有當(dāng)沒有其他線程對查詢結(jié)果集中的任何一行使用排他鎖時,才能成功申請排他鎖;否則將被阻塞。
意向鎖
在MySQL的InnoDB引擎中,支持多種鎖級別,包括行級鎖和表級鎖。當(dāng)多個事務(wù)需要訪問共享資源時,如果每個事務(wù)都直接請求鎖,可能會導(dǎo)致彼此相互阻塞,甚至引發(fā)死鎖。
舉個例子:
事務(wù)A對表Table1中的某一行加上了行級鎖,這導(dǎo)致該行只能讀取而不能修改。與此同時,事務(wù)B試圖申請對Table1的表級鎖。如果事務(wù)B成功獲取表級鎖,那么它就能修改表中的任意一行記錄,從而引發(fā)沖突。
為解決這一問題,事務(wù)B在申請Table1的表級鎖時,需要先檢查是否有其他事務(wù)已經(jīng)加了行級鎖。然而,事務(wù)B無法簡單地遍歷表中所有數(shù)據(jù)逐行判斷是否已被鎖定,這樣效率太低了。
為了解決這一問題,MySQL引入了意向鎖機(jī)制。意向鎖作為一種鎖機(jī)制,在數(shù)據(jù)庫管理系統(tǒng)中旨在協(xié)調(diào)不同鎖粒度(如行級鎖和表級鎖)之間的并發(fā)問題。(對于同一鎖粒度內(nèi)的并發(fā)問題,如多個行級鎖之間的沖突,則通過行級互斥鎖來解決。)
注意:
- 意向鎖并非直接鎖定資源,而是用于通知其他事務(wù),以防止它們在相同資源上設(shè)置不兼容的鎖。
- 意向鎖不是由用戶直接請求的,而是由MySQL系統(tǒng)管理的。
當(dāng)一個事務(wù)請求獲取行級鎖或表級鎖時,MySQL會自動獲取相應(yīng)表的意向鎖。這樣一來,其他事務(wù)在請求表鎖時,可以先通過該意向鎖探知是否有已經(jīng)加鎖,并根據(jù)意向鎖的類型(意向共享鎖/意向排它鎖)判斷自身是否可獲取鎖。這種方式在不阻塞其他事務(wù)的情況下,為當(dāng)前事務(wù)鎖定資源。
意向鎖有兩種類型:意向共享鎖和意向排它鎖。
- 意向共享鎖:代表事務(wù)打算對資源設(shè)置共享鎖(讀鎖)。通常用于暗示事務(wù)打算讀取資源,不希望在讀取時有其他事務(wù)設(shè)置排它鎖。
- 意向排它鎖:代表事務(wù)打算對資源設(shè)置排它鎖(寫鎖)。這表明事務(wù)計(jì)劃修改資源,不希望其他事務(wù)同時設(shè)置共享或排它鎖。
意向鎖是表級鎖,在觸發(fā)意向鎖的事務(wù)提交或回滾后會釋放。
以下是MySQL官網(wǎng)上給出的這幾種鎖之間的沖突關(guān)系:
★
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
圖片
記錄鎖
記錄鎖(Record Lock)是一種加在索引記錄上的鎖,用于保護(hù)特定行數(shù)據(jù)的完整性。例如,對于語句 SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;,將會對滿足條件 c1=10 的記錄加鎖,以防止其他事務(wù)對該行進(jìn)行插入、更新或刪除操作。
盡管記錄鎖通常被稱為行級鎖,但需要特別注意的是,它實(shí)際上鎖定的是索引記錄而非數(shù)據(jù)行本身。此外,記錄鎖僅限于鎖定索引。
當(dāng)表中不存在索引時該如何處理?InnoDB 引擎會自動創(chuàng)建一個隱藏的聚簇索引,并使用該索引進(jìn)行記錄鎖定。
★
若表中未定義主鍵,MySQL會默認(rèn)選擇一個唯一的非空索引作為聚簇索引。若不存在適用的非空唯一索引,則會創(chuàng)建一個隱藏的主鍵(row_id)作為聚簇索引。
關(guān)于記錄鎖的加鎖原則。感興趣的小伙伴一鍵三連。后續(xù)可以出一片文章。
插入記錄鎖
插入意向鎖是一種由插入操作在行插入之前設(shè)置的間隙鎖。這種鎖表明了插入的意圖,以這樣一種方式,如果多個事務(wù)嘗試插入到同一索引間隙但不在間隙內(nèi)的相同位置,則它們不需要相互等待。
舉例來說,假設(shè)存在索引記錄的值為4和7。當(dāng)不同事務(wù)分別嘗試插入值為5和6時,它們會在獲取插入行的獨(dú)占鎖之前,各自使用插入意向鎖鎖定4和7之間的間隙。由于它們插入的行并不沖突,因此它們不會相互阻塞。然而,如果它們都試圖插入6,那么就會發(fā)生阻塞情況。
AUTO-INC 鎖
AUTO-INC 鎖是一種特殊的表級鎖,由向包含 AUTO_INCREMENT 列的表插入數(shù)據(jù)的事務(wù)所獲取。在最簡單的情況下,如果一個事務(wù)正在向表中插入值,其他任何事務(wù)都必須等待,以便執(zhí)行它們自己的插入操作,這樣第一個事務(wù)插入的行就會接收到連續(xù)的主鍵值。
innodb_autoinc_lock_mode 變量控制用于自增鎖定的算法。它允許你在可預(yù)測的自增值序列和插入操作的最大并發(fā)性之間進(jìn)行權(quán)衡。