說透MySQL裏的各種鎖(下篇)

在上篇中,我們介紹了MySQL中的全局鎖和表鎖。

今天,我們專注於介紹一下行鎖,這個在日常開發和麪試中常常困擾我們的問題。

1.行鎖基礎

由於全局鎖和表鎖對增刪改查的性能都會有較大影響,所以,我們自然會想到,

只需要對有修改的行加鎖就行了,這就是行鎖。

在事務中,事務1更新了一行主鍵爲1的數據行,那麼,在這個事務釋放鎖之前,事務2是不能操作的。
在這裏插入圖片描述
另外,有一個很多人容易混淆的概念,就是行鎖什麼時候釋放?

搞清這個事情,需要了解什麼叫作 兩階段鎖。

什麼是兩階段鎖呢?舉個例子你就明白了。
在這裏插入圖片描述
這裏事務2執行後會是什麼結果呢?

如果你明白了兩階段鎖的含義,你就會知道,事務2的updat語句會阻塞,直到事務1提交以後才能繼續執行。

所以,這裏再次強調一下,兩階段鎖的含義。

在 InnoDB 事務中,行鎖是在需要的時候才加上的,但並不是語句執行完了了就立刻釋放, 而是要等到事務結束時才釋放。

注意,除了update語句能加寫鎖外,另外,還有一種對select語句加寫鎖的方式,就是

當前讀:Select …. for update

2.行鎖進階

2.1 什麼是幻讀

面試的時候,面試官經常會喜歡問數據庫的事務隔離級別。

大家要能回答出四種隔離級別,四種隔離級別的含義。

再多問一點,會問你什麼是髒讀,什麼是幻讀,哪個隔離級別會解決什麼問題。

首先明確一下,什麼是幻讀?

同樣是一個事務,在事務中前後兩次查詢,出現了不同的結果。

不同之處在於,髒讀是針對update,也就是同一行的數據出現了不一致。

注意,幻讀出現的場景

第一:事務的隔離級別爲可重複讀,且是當前讀

第二:幻讀僅專指新插入的行,在範圍查詢中,後一次查詢出現了新的數據行。

2.2 怎麼解決幻讀

這些如果你都能答上,面試官可能會繼續追問,幻讀是怎麼產生的,又是怎麼被解決的。

即使我們給所有update涉及的行都加上了行鎖,還是無法解決新插入的記錄,因爲這些記錄原本不存在,自然無法加上行鎖。

那怎麼辦呢?爲了解決這個問題,innodb只好引入新的鎖,間隙鎖(Gap Lock)。

“間隙鎖,鎖的是兩個值之間的空隙”。

舉個例子:

在四條記錄,ID=0,10,20,30中,會產生如下的五個間隙範圍
在這裏插入圖片描述
間隙鎖就是對這五個間隙範圍加鎖,防止新的記錄插入。

注意,行鎖的衝突是行與行之間的衝突,是行鎖與行鎖之間的。與間隙鎖衝突的是往“間隙中插入數據”這個操作,間隙鎖本身不會產生衝突。

間隙鎖和行鎖合稱爲next-key lock。

每個next-key lock是前開後閉的。間隙鎖本身是前開後開的。

Tips

標準的事務隔離級別中,可重複讀只解決髒讀問題,無法解決幻讀問題。但是在innodb中,用next-key lock解決了幻讀的問題。

3.關於行鎖的優化應用

3.1 兩階段鎖的優化應用

上面的基礎知識中,解釋了什麼是 兩階段鎖。

那麼,對我們業務開發中有什麼借鑑意義呢?

既然我們知道了,行鎖必須在整個事務完全提交後纔會釋放,那麼,如果我們的事物中需要鎖住多行,就要把最可能造成鎖衝突,或者是鎖住最多行的語句儘可能地往後放。

舉個例子,小A在線上購買了商家B的一個產品,這個購買的動作可以簡化爲3個操作:

1)小A的銀行賬戶餘額扣款x;

2)商家B的銀行賬戶餘額增加x;

3)添加一條交易記錄;

這裏,涉及到兩個update操作,和一個insert操作。爲了保證交易的原子性,將三個動作放在了一個事務中。

那怎麼安排三個語句的先後順序呢?如果不仔細考慮,那麼就可能是隨意選個123或者213的順序了。

仔細想想呢?

顯然,這裏最容易造成衝突的是步驟2),可能同時有多個用戶購買商家B的產品,然後需要給商家B的餘額做update操作。

另外,步驟3)是insert操作,最不容易出現鎖衝突。

所以,最好的步驟順序是3)-> 1) -> 2),將最容易產生衝突的操作放在最後執行,那麼會比2)->1) ->3)的順序,大大提高併發度。

3.2 間隙鎖的問題與優化

間隙鎖的引入也帶來了一些新的問題,比如:降低併發度,可能導致死鎖。

因爲間隙鎖的引入,可能會導致同樣的語句鎖住了更大的範圍。

那怎麼辦呢?

注意,間隙鎖在可重複讀級別下才是有效的。

所以,只要我們的業務不需要可重複讀的保證,我們就可以把隔離級別設置爲讀提交(也是阿里雲rds數據庫的默認隔離級別),就沒有間隙鎖了。

然後,爲了解決可能的數據和日誌不一致的問題,需要把binlog格式設置爲row。

讀提交級別 + binlog的row格式,也是一般公司數據庫的標準配置。

現在,你知道原因了吧:)

參考:

丁奇《MySQL 實戰45講》

【MySQL系列相關】
1.聊一聊關於MySQL的count(*)

2.爲什麼MySQL分庫分表後總存儲大小變大了?

3.說透MySQL裏的各種鎖(上篇)

看到這裏了,原創不易,點個贊吧,你最好看了~

知識碎片重新梳理,構建Java知識圖譜:github.com/saigu/JavaK… (歷史文章查閱非常方便)

掃碼關注我的公衆號“阿丸筆記”,第一時間獲取最新更新。同時可以免費獲取海量Java技術棧電子書、各個大廠面試題。
阿丸筆記

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章