偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

面試官:談?wù)凪ySQL鎖機制

數(shù)據(jù)庫 MySQL
從鎖的類型角度,MySQL 分為共享鎖(S 鎖,讀鎖)和排他鎖(X 鎖,寫鎖)。共享鎖允許多個事務(wù)同時讀取同一資源,但不允許寫入;排他鎖則是獨占式的,持有排他鎖的事務(wù)可以讀寫數(shù)據(jù),其他事務(wù)無法獲取任何鎖。

MySQL 的鎖機制是保證數(shù)據(jù)庫并發(fā)訪問時數(shù)據(jù)一致性和完整性的核心手段。

從鎖的粒度來說,MySQL 支持表級鎖、行級鎖和頁級鎖,其中 InnoDB 主要使用行級鎖來提高并發(fā)性能。

從鎖的類型角度,MySQL 分為共享鎖(S 鎖,讀鎖)和排他鎖(X 鎖,寫鎖)。共享鎖允許多個事務(wù)同時讀取同一資源,但不允許寫入;排他鎖則是獨占式的,持有排他鎖的事務(wù)可以讀寫數(shù)據(jù),其他事務(wù)無法獲取任何鎖。

在實際應(yīng)用中,InnoDB 引擎還實現(xiàn)了意向鎖(IS 鎖和 IX 鎖),這是表級鎖,是一個標識,用于表明事務(wù)稍后要獲取的行鎖類型,主要是為了提高加表級鎖時的效率,避免逐行檢查。

為了解決幻讀問題,InnoDB 引入了間隙鎖(Gap Lock)和臨鍵鎖(Next-Key Lock)。間隙鎖鎖定索引記錄之間的間隙,防止其他事務(wù)在該間隙插入數(shù)據(jù);臨鍵鎖是行鎖和間隙鎖的組合,鎖定記錄本身及其前面的間隙。

從鎖的實現(xiàn)層面,InnoDB 采用兩階段鎖協(xié)議,事務(wù)執(zhí)行過程中隨時可以加鎖,但所有鎖只有在事務(wù)提交或回滾時才會統(tǒng)一釋放。這個機制雖然簡單,但可能導(dǎo)致死鎖問題,MySQL 通過死鎖檢測和超時機制來處理死鎖。

另外,還有樂觀鎖和悲觀鎖的概念。悲觀鎖認為并發(fā)沖突概率高,每次操作都加鎖;樂觀鎖則相反,通過版本號或 CAS 機制在提交時檢查沖突。MySQL 的鎖機制本質(zhì)上是悲觀鎖的實現(xiàn),而樂觀鎖需要在應(yīng)用層實現(xiàn)。

一、鎖的基本概念:為什么需要鎖?

我們先理解一個根本問題:為什么數(shù)據(jù)庫需要鎖?

想象一個電商場景:雙十一零點,成千上萬的用戶同時搶購一件商品,庫存只有 100 件。如果沒有鎖機制,可能出現(xiàn)這樣糟糕的情況:

張三查詢庫存,看到還有 1 件,準備下單。與此同時,李四也查詢庫存,也看到還有 1 件。然后兩人幾乎同時提交訂單,系統(tǒng)扣減庫存。結(jié)果本來只有 1 件商品,卻賣出去了 2 件,出現(xiàn)了超賣的問題,這就是典型的并發(fā)問題。

鎖機制的本質(zhì)就是通過串行化某些操作來保證數(shù)據(jù)的一致性。當張三正在處理庫存扣減時,系統(tǒng)會給這條庫存記錄加鎖,李四只能等待,直到張三完成操作釋放鎖后,李四才能繼續(xù)。

但是,鎖的代價是降低并發(fā)性能。如果所有操作都串行化,那數(shù)據(jù)庫的并發(fā)能力就太差了。因此,MySQL 就設(shè)計了一套精細化的鎖機制,在保證數(shù)據(jù)一致性的前提下,盡可能的去提高并發(fā)性能。

二、鎖的粒度

MySQL 支持多種鎖粒度

2.1 表級鎖

表級鎖是最簡單的鎖實現(xiàn)方式,直接鎖定整張表。當一個事務(wù)獲取表級寫鎖時,其他事務(wù)無法讀寫該表;獲取表級讀鎖時,其他事務(wù)可以讀但不能寫。

表級鎖的優(yōu)點是開銷小,加鎖快,不會出現(xiàn)死鎖。但缺點也很明顯:鎖的粒度太大,并發(fā)度低。比如一個表有 100 萬條記錄,某個事務(wù)只想更新其中 1 條,卻要鎖住整張表,其他 99 萬 9999 條記錄都被"連累"了。

MySQL中的MyISAM 存儲引擎就是使用表級鎖。這也是為什么 MyISAM 不適合高并發(fā)寫入場景的根本原因。

2.2 行級鎖

行級鎖是 InnoDB 的核心特性,它只鎖定需要操作的數(shù)據(jù)行,其他行不受影響。還是剛才的例子,如果使用行級鎖,一個事務(wù)更新第 1 條記錄時,另一個事務(wù)完全可以同時更新第 2 條記錄,互不干擾。

行級鎖的并發(fā)度高,但開銷也更大。每加一個鎖都需要消耗內(nèi)存,維護鎖的數(shù)據(jù)結(jié)構(gòu)也需要額外的 CPU 開銷。而且,行級鎖可能導(dǎo)致死鎖問題。

2.3 頁級鎖

頁級鎖是 BDB 存儲引擎使用的鎖機制,鎖定的是數(shù)據(jù)頁(通常是 16KB)。它介于表級鎖和行級鎖之間,既不會像表級鎖那樣粒度過粗,也不會像行級鎖那樣開銷過大。但頁級鎖的實現(xiàn)復(fù)雜度較高,而且可能出現(xiàn)鎖沖突的概率介于二者之間。

由于 BDB 引擎已經(jīng)不再主流,BDB存儲引擎在MySQL 5.1版本后被逐步棄用。

三、鎖的類型

在確定了鎖的粒度后,我們還需要區(qū)分鎖的類型。InnoDB 主要使用兩種基本鎖類型:

3.1 共享鎖(Shared Lock,S 鎖)

共享鎖,顧名思義,是可以"共享"的鎖。當一個事務(wù)對某行數(shù)據(jù)加上共享鎖后,其他事務(wù)也可以對該行加共享鎖,大家一起讀取數(shù)據(jù)。但是,任何事務(wù)都不能對該行加排他鎖,也就是不能修改數(shù)據(jù)。

比如舉個例子:多個人同時看一本書沒問題,但如果有人要修改書的內(nèi)容,就必須等所有人都看完、放下書之后才能開始修改。

在 SQL 中,可以通過以下方式顯式加共享鎖:

執(zhí)行 SELECT ... LOCK IN SHARE MODE 語句時,會對讀取的行加共享鎖。但這種用法比較少見,通常用在需要確保讀取后到事務(wù)提交期間數(shù)據(jù)不被修改的場景。

3.2 排他鎖(Exclusive Lock,X 鎖)

排他鎖是獨占式的鎖。當一個事務(wù)對某行數(shù)據(jù)加上排他鎖后,其他事務(wù)既不能加共享鎖(不能讀),也不能加排他鎖(不能寫)。只有持有排他鎖的事務(wù)可以讀寫該行數(shù)據(jù)。

再用上面書的例子,比如當有人要修改書的內(nèi)容時,他必須獨占這本書,其他人既不能看也不能改。

執(zhí)行 UPDATE、DELETE、INSERT 語句時,MySQL 會自動對涉及的行加排他鎖。也可以通過 SELECT ... FOR UPDATE顯式加排他鎖。

3.3 鎖的兼容性矩陣

理解共享鎖和排他鎖的關(guān)鍵是掌握它們的兼容性:

  • S 鎖 + S 鎖:兼容。多個事務(wù)可以同時持有同一行的共享鎖。
  • S 鎖 + X 鎖:不兼容。如果一行已經(jīng)被加了共享鎖,其他事務(wù)無法加排他鎖;反之亦然。
  • X 鎖 + X 鎖:不兼容。一行只能被一個事務(wù)加排他鎖。

這個矩陣可以總結(jié)為:讀讀可以并行,讀寫互斥,寫寫互斥。

四、意向鎖:表級鎖與行級鎖的協(xié)調(diào)者

要理解意向鎖,我們先看一個問題:

假設(shè)事務(wù) A 對某個表的第 1000 行加了行級排他鎖。此時事務(wù) B 想對整張表加表級排他鎖,MySQL 如何判斷能否加鎖成功?

最直觀的方法是:遍歷表的所有行,檢查是否有行級鎖。但這個開銷太大了,如果表有幾百萬行,每次加表級鎖都要掃描一遍,性能會急劇下降。

意向鎖就是為了解決這個問題而設(shè)計的。它是一種表級鎖,分為兩種:

4.1 意向共享鎖(IS 鎖)

當一個事務(wù)想要對表中的某些行加共享鎖時,必須先對該表加意向共享鎖。意向共享鎖表達的意思是:"我打算對這張表的某些行加共享鎖"。

4.2 意向排他鎖(IX 鎖)

當一個事務(wù)想要對表中的某些行加排他鎖時,必須先對該表加意向排他鎖。意向排他鎖表達的意思是:"我打算對這張表的某些行加排他鎖"。

4.3 意向鎖的作用機制

有了意向鎖,前面的問題就簡單了:

  1. 事務(wù) A 對第 1000 行加排他鎖時,會先對該表加意向排他鎖(IX 鎖)
  2. 事務(wù) B 想對整張表加表級排他鎖時,只需檢查該表是否有意向鎖
  3. 發(fā)現(xiàn)有 IX 鎖,說明表中某些行已被加鎖,無法加表級排他鎖,事務(wù) B 等待

這樣就避免了逐行掃描的開銷。意向鎖之間是兼容的,因為它們只是"意向",真正的沖突在行級鎖層面解決。

意向鎖的兼容性:

  • IS 鎖與 IS 鎖、IX 鎖都兼容
  • IX 鎖與 IS 鎖、IX 鎖都兼容
  • 表級 S 鎖與 IS 鎖兼容,但與 IX 鎖不兼容
  • 表級 X 鎖與任何意向鎖都不兼容

意向鎖對開發(fā)者是透明的,由 MySQL 自動管理,我們通常不需要顯式操作。但理解它的機制有助于我們更好地理解 InnoDB 的鎖體系。

五、記錄鎖、間隙鎖與臨鍵鎖:解決幻讀的利器

行級鎖聽起來很簡單,但 InnoDB 的行級鎖實際上包含多種細分類型。這些鎖的設(shè)計主要是為了在不同隔離級別下解決不同的并發(fā)問題,特別是幻讀問題。

5.1 記錄鎖(Record Lock)

記錄鎖是最基礎(chǔ)的行級鎖,它鎖定的是索引記錄本身。注意,這里強調(diào)"索引記錄",因為 InnoDB 的鎖是加在索引上的,而不是直接加在數(shù)據(jù)行上。

如果查詢條件沒有使用索引,InnoDB 會使用隱藏的主鍵索引(聚簇索引)來加鎖。如果是通過二級索引查詢,會先鎖定二級索引記錄,然后再鎖定對應(yīng)的主鍵索引記錄。

舉個例子:表中有一條記錄 id=10,當執(zhí)行 UPDATE users SET name='張三' WHERE id=10 時,會對 id=10 這條索引記錄加記錄鎖。

5.2 間隙鎖(Gap Lock)

間隙鎖是 InnoDB 在可重復(fù)讀隔離級別下為了防止幻讀而引入的鎖機制。它鎖定的不是記錄本身,而是索引記錄之間的"間隙"。

假設(shè)表中有三條記錄,id 分別是 10、20、30。那么存在以下間隙:

  • 負無窮到 10 之前  (-∞,10)
  • 10 到 20 之間   (10,20)
  • 20 到 30 之間    (20,30)
  • 30 到正無窮之后  (30,+∞)

當執(zhí)行 SELECT * FROM users WHERE id > 10 AND id < 20 FOR UPDATE 時,即使范圍內(nèi)沒有記錄,InnoDB 也會對這個間隙加鎖,防止其他事務(wù)在這個范圍內(nèi)插入新記錄。

間隙鎖的特點:

  1. 只在可重復(fù)讀隔離級別下生效。在讀已提交隔離級別下,間隙鎖會被禁用,只使用記錄鎖。
  2. 間隙鎖之間不沖突。多個事務(wù)可以同時持有同一個間隙的間隙鎖,因為間隙鎖的唯一目的是防止插入,而不是防止讀寫已有記錄。
  3. 間隙鎖會降低并發(fā)性能。它鎖定的范圍可能很大,會阻塞其他事務(wù)的插入操作。

5.3 臨鍵鎖(Next-Key Lock)

臨鍵鎖是記錄鎖和間隙鎖的組合,它鎖定的是一個左開右閉的區(qū)間。這是 InnoDB 在可重復(fù)讀隔離級別下的默認行級鎖。

還是剛才的例子,表中有 id 為 10、20、30 的記錄。當執(zhí)行范圍查詢時,可能會加上以下臨鍵鎖:

  • (-∞, 10]
  • (10, 20]
  • (20, 30]
  • (30, +∞]

臨鍵鎖既鎖定記錄本身,又鎖定記錄前面的間隙,能夠同時防止幻讀和不可重復(fù)讀。

5.4 為什么需要這么復(fù)雜的鎖?

這些鎖的設(shè)計都是為了在不同場景下平衡性能和一致性:

場景一:點查詢

執(zhí)行 SELECT * FROM users WHERE id = 10 FOR UPDATE,只需要記錄鎖,鎖定 id=10 這一條記錄即可。

場景二:范圍查詢

執(zhí)行 SELECT * FROM users WHERE id >= 10 AND id <= 20 FOR UPDATE,需要使用臨鍵鎖,鎖定區(qū)間內(nèi)的所有記錄以及區(qū)間本身,防止幻讀。

場景三:不存在的記錄

執(zhí)行 SELECT * FROM users WHERE id = 15 FOR UPDATE,如果 id=15 不存在,會對(10, 20)這個間隙加間隙鎖,防止其他事務(wù)插入 id=15 的記錄。

六、鎖與事務(wù)隔離級別的關(guān)系

MySQL 的鎖機制與事務(wù)隔離級別緊密相關(guān)。不同的隔離級別使用不同的鎖策略來保證數(shù)據(jù)一致性。

6.1 讀未提交(Read Uncommitted)

這是最低的隔離級別,幾乎不使用鎖。讀操作不加鎖,可以讀到其他事務(wù)未提交的數(shù)據(jù)(臟讀)。寫操作會加排他鎖,但只鎖定記錄本身。

這個級別性能最好,但一致性最差,實際應(yīng)用中幾乎不使用。

6.2 讀已提交(Read Committed)

在這個級別下,讀操作使用快照讀(MVCC),不加鎖。寫操作會加記錄鎖,但不會使用間隙鎖。

這意味著可以避免臟讀,但無法避免不可重復(fù)讀和幻讀。比如:

  • 事務(wù) A 第一次查詢 id=10 的記錄,看到 name='張三'
  • 事務(wù) B 更新這條記錄為 name='李四'并提交
  • 事務(wù) A 第二次查詢 id=10 的記錄,看到 name='李四'

同一個事務(wù)中兩次讀取的結(jié)果不一致,這就是不可重復(fù)讀。

6.3 可重復(fù)讀(Repeatable Read)

這是 InnoDB 的默認隔離級別。在這個級別下:

  • 讀操作使用快照讀(MVCC),讀取的是事務(wù)開始時的數(shù)據(jù)快照
  • 寫操作使用臨鍵鎖,既鎖定記錄本身,也鎖定間隙

通過 MVCC,同一個事務(wù)中多次讀取同一條記錄,看到的結(jié)果是一致的,解決了不可重復(fù)讀問題。通過間隙鎖,防止其他事務(wù)在范圍內(nèi)插入新記錄,解決了幻讀問題。

但需要注意,快照讀只能防止部分幻讀。如果使用當前讀(如 SELECT ... FOR UPDATE),仍可能出現(xiàn)幻讀,此時就需要依靠間隙鎖來解決。

6.4 串行化(Serializable)

這是最高的隔離級別,所有讀操作都會加共享鎖,寫操作加排他鎖,事務(wù)之間完全串行執(zhí)行。這個級別保證了最強的一致性,但性能最差,實際應(yīng)用中很少使用。

6.5 隔離級別選擇的權(quán)衡

  • 讀未提交:性能好,一致性差,幾乎不用
  • 讀已提交:Oracle、SQL Server 的默認級別,適合對一致性要求不高的場景
  • 可重復(fù)讀:MySQL 的默認級別,平衡了性能和一致性,是最常用的選擇
  • 串行化:一致性最強,性能最差,只在特殊場景使用

七、兩階段鎖協(xié)議:鎖的生命周期

InnoDB 采用兩階段鎖協(xié)議(Two-Phase Locking,2PL)來管理鎖的生命周期。

7.1 什么是兩階段鎖協(xié)議?

兩階段鎖協(xié)議將事務(wù)分為兩個階段:

加鎖階段(Growing Phase):事務(wù)可以獲取鎖,但不能釋放鎖。在這個階段,事務(wù)隨著執(zhí)行逐步獲取需要的各種鎖。

釋放鎖階段(Shrinking Phase):事務(wù)可以釋放鎖,但不能再獲取新鎖。一旦事務(wù)開始釋放任何一個鎖,就進入這個階段。

在 InnoDB 中,實現(xiàn)相對簡化:事務(wù)執(zhí)行過程中隨時可以加鎖,但所有鎖只在事務(wù)提交或回滾時才統(tǒng)一釋放。這保證了事務(wù)的隔離性。

7.2 鎖的持有時間與性能

兩階段鎖協(xié)議有一個重要的性能含義:鎖的持有時間從加鎖開始,一直到事務(wù)結(jié)束。

假設(shè)一個事務(wù)需要執(zhí)行以下操作:

  1. 更新用戶表中的記錄(加鎖)
  2. 執(zhí)行一些復(fù)雜的業(yè)務(wù)邏輯計算
  3. 更新訂單表中的記錄(加鎖)
  4. 提交事務(wù)(釋放所有鎖)

在這個過程中,第 1 步獲取的鎖會一直持有到第 4 步,即使第 2 步和第 3 步并不需要這個鎖。

這啟發(fā)我們一個重要的優(yōu)化原則:盡量縮短事務(wù)的執(zhí)行時間,減少鎖的持有時間,避免長事務(wù)。具體做法包括:

  • 不要在事務(wù)中執(zhí)行復(fù)雜的業(yè)務(wù)邏輯計算
  • 不要在事務(wù)中進行遠程調(diào)用
  • 將不需要事務(wù)保護的操作移到事務(wù)外面

八、死鎖

8.1 什么是死鎖?

死鎖是指兩個或多個事務(wù)互相等待對方釋放鎖,導(dǎo)致所有事務(wù)都無法繼續(xù)執(zhí)行的狀態(tài)。

經(jīng)典的死鎖場景:

  1. 事務(wù) A 鎖定了資源 1,準備鎖定資源 2
  2. 事務(wù) B 鎖定了資源 2,準備鎖定資源 1
  3. 事務(wù) A 等待事務(wù) B 釋放資源 2,事務(wù) B 等待事務(wù) A 釋放資源 1
  4. 雙方都在等待,誰也無法繼續(xù)

8.2 死鎖的四個必要條件

死鎖的發(fā)生需要同時滿足四個條件:

互斥條件:資源只能被一個事務(wù)占用,其他事務(wù)必須等待。這是鎖的本質(zhì)特性,無法消除。

持有并等待:事務(wù)持有至少一個資源,并等待獲取其他資源。這也是兩階段鎖協(xié)議的必然結(jié)果。

不可剝奪:資源不能被強行奪走,只能由持有者主動釋放。MySQL 的鎖機制也具有這個特性。

循環(huán)等待:存在一個資源等待環(huán)路,每個事務(wù)都在等待環(huán)路中下一個事務(wù)持有的資源。這是死鎖的關(guān)鍵特征。

只有同時滿足這四個條件,才會發(fā)生死鎖。破壞任何一個條件,就能避免死鎖。

8.3 如何避免死鎖?

雖然 MySQL 可以自動檢測和處理死鎖,但頻繁的死鎖會嚴重影響性能。我們應(yīng)該從設(shè)計層面就盡量避免死鎖:

按相同順序訪問資源:如果所有事務(wù)都按照相同的順序獲取鎖,就不會形成循環(huán)等待。比如,總是先鎖用戶表,再鎖訂單表,而不是有時先鎖訂單表。

縮短事務(wù)持續(xù)時間:事務(wù)執(zhí)行時間越短,持有鎖的時間就越短,發(fā)生死鎖的概率就越低。

降低事務(wù)隔離級別:如果業(yè)務(wù)允許,可以使用讀已提交隔離級別,避免使用間隙鎖,減少鎖沖突。

合理設(shè)計索引:確保查詢都能走索引,避免全表掃描導(dǎo)致大量加鎖。

大事務(wù)拆分為小事務(wù):不要在一個事務(wù)中處理過多數(shù)據(jù),可以分批處理,減少鎖的持有時間。

圖片圖片

九、悲觀鎖與樂觀鎖思想

除了 MySQL 內(nèi)置的鎖機制,還有兩種重要的并發(fā)控制思想:悲觀鎖和樂觀鎖。這兩種思想代表了對待并發(fā)沖突的不同態(tài)度。

9.1 悲觀鎖:先鎖后用

悲觀鎖的思想是:假設(shè)并發(fā)沖突經(jīng)常發(fā)生,所以每次操作數(shù)據(jù)時都先加鎖,確保只有自己能訪問,操作完成后再釋放鎖。

MySQL 的鎖機制本質(zhì)上就是悲觀鎖的實現(xiàn)。當執(zhí)行 SELECT ... FOR UPDATE 時,就是在使用悲觀鎖:先把數(shù)據(jù)鎖住,確保別人不能修改,然后再進行操作。

悲觀鎖的特點:

  • 實現(xiàn)簡單,由數(shù)據(jù)庫保證并發(fā)安全
  • 性能開銷較大,會阻塞其他事務(wù)
  • 適合并發(fā)沖突頻繁的場景
  • 可能導(dǎo)致死鎖

9.2 樂觀鎖:先用后驗

樂觀鎖的思想是:假設(shè)并發(fā)沖突很少發(fā)生,所以不加鎖,直接操作數(shù)據(jù)。但在提交時檢查是否有沖突,如果有沖突就放棄操作并重試。

樂觀鎖通常通過版本號或時間戳來實現(xiàn)。每條記錄包含一個版本號字段,每次更新時:

  1. 讀取記錄及其版本號,比如版本號為 5
  2. 在本地完成業(yè)務(wù)邏輯計算
  3. 提交更新時,檢查版本號是否仍為 5
  4. 如果仍為 5,說明沒有沖突,更新數(shù)據(jù)并將版本號改為 6
  5. 如果不是 5,說明有其他事務(wù)已經(jīng)修改過,本次更新失敗,需要重試

樂觀鎖的特點:

  • 不需要數(shù)據(jù)庫鎖,不會阻塞其他事務(wù)
  • 性能好,適合讀多寫少的場景
  • 實現(xiàn)相對復(fù)雜,需要應(yīng)用層處理沖突
  • 需要額外的版本號字段

9.3 如何選擇?

選擇悲觀鎖還是樂觀鎖,取決于具體的業(yè)務(wù)場景:

使用悲觀鎖的場景:

  • 寫操作頻繁,沖突概率高
  • 每次沖突的代價很高(比如涉及復(fù)雜計算)
  • 需要強一致性保證
  • 典型場景:秒殺、庫存扣減、賬戶余額更新

使用樂觀鎖的場景:

  • 讀操作遠多于寫操作
  • 沖突概率低
  • 可以容忍偶爾的重試
  • 典型場景:用戶信息修改、文章編輯、商品詳情更新

實際項目中,往往是兩者結(jié)合使用。對于核心的高沖突資源使用悲觀鎖,對于邊緣的低沖突資源使用樂觀鎖。

十、鎖優(yōu)化

最后我們來聊聊實際開發(fā)中如何優(yōu)化鎖的使用,避免鎖相關(guān)的性能問題。

10.1 索引優(yōu)化:減少鎖的范圍

InnoDB 的鎖是加在索引上的,如果沒有合適的索引,查詢會走全表掃描,導(dǎo)致鎖定大量無關(guān)的記錄,甚至退化成表級鎖。

反例:沒有索引的更新

UPDATE users SET status = 1 WHERE name = '張三';

如果 name 字段沒有索引,這條 SQL 會掃描全表,鎖定所有記錄。

正例:有索引的更新

-- 在name字段上建立索引
CREATE INDEX idx_name ON users(name);

UPDATE users SET status = 1 WHERE name = '張三';

此時只會鎖定 name='張三'的記錄。

10.2 盡量使用主鍵或唯一索引

使用主鍵或唯一索引進行查詢,可以精確定位記錄,避免加不必要的間隙鎖。

如果 WHERE 條件是非唯一索引,可能會鎖定多條記錄以及它們之間的間隙。如果 WHERE 條件是主鍵或唯一索引,通常只鎖定一條記錄,不會加間隙鎖。

10.3 減小事務(wù)范圍

事務(wù)越大,持有鎖的時間越長,發(fā)生鎖沖突的概率越高。應(yīng)該:

  • 只在真正需要事務(wù)保護的地方使用事務(wù)
  • 不要在事務(wù)中執(zhí)行耗時的操作(如遠程調(diào)用、復(fù)雜計算)
  • 將大事務(wù)拆分為多個小事務(wù)

10.4 避免在循環(huán)中逐條操作

反例:在循環(huán)中逐條更新

-- 偽代碼
for each userId in userList:
    UPDATE users SET score = score + 1 WHERE id = userId;

每次更新都是一個單獨的事務(wù),效率低,而且容易導(dǎo)致死鎖。

正例:批量更新

UPDATE users SET score = score + 1 WHERE id IN (1, 2, 3, 4, 5);

一次更新多條記錄,減少事務(wù)數(shù)量和鎖等待時間。

10.5 合理設(shè)置事務(wù)隔離級別

不是所有場景都需要可重復(fù)讀隔離級別。如果業(yè)務(wù)可以容忍不可重復(fù)讀,可以使用讀已提交隔離級別,避免間隙鎖,提高并發(fā)性能。

特別是在高并發(fā)的互聯(lián)網(wǎng)應(yīng)用中,很多公司都選擇讀已提交作為默認隔離級別。

10.6 監(jiān)控和診斷鎖問題

當遇到性能問題時,可以使用以下方法診斷鎖相關(guān)問題:

查看當前鎖等待情況:

SELECT * FROM information_schema.innodb_locks;
SELECT * FROM information_schema.innodb_lock_waits;

查看最近的死鎖信息:

SHOW ENGINE INNODB STATUS;

這個命令會顯示最近一次死鎖的詳細信息,包括涉及的事務(wù)、持有的鎖、等待的鎖等,對于分析死鎖原因非常有幫助。

責任編輯:武曉燕 來源: 玄武后端技術(shù)棧
相關(guān)推薦

2024-08-26 14:52:58

JavaScript循環(huán)機制

2024-06-13 08:01:19

2018-10-22 14:28:26

面試官數(shù)據(jù)公司

2025-02-19 00:00:00

RabbitMQTTL插件

2024-10-12 16:25:12

2025-08-04 00:00:00

樂觀讀鎖并發(fā)編程共享讀鎖

2024-08-12 17:36:54

2024-09-27 15:43:52

零拷貝DMAIO

2025-02-21 15:25:54

虛擬線程輕量級

2024-10-24 16:14:43

數(shù)據(jù)傳輸CPU零拷貝

2025-08-18 00:00:00

零拷貝系統(tǒng)調(diào)用函數(shù)

2025-03-21 00:00:05

Reactor設(shè)計模式I/O 機制

2022-03-21 09:05:18

volatileCPUJava

2018-05-10 15:48:47

面試面試官Java

2022-04-19 07:31:28

事務(wù)隔離機制數(shù)據(jù)庫

2021-12-16 18:38:13

面試Synchronize

2018-04-27 14:46:07

面試簡歷程序員

2023-07-11 08:50:34

2021-05-17 07:08:28

大數(shù)據(jù)采集MySQL

2025-04-09 00:00:00

點贊
收藏

51CTO技術(shù)棧公眾號