前阿里數據庫專家總結的MySQL裏的各類鎖(下篇)

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

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

1.行鎖基礎

因爲全局鎖和表鎖對增刪改查的性能都會有較大影響,因此,咱們天然會想到,面試

只須要對有修改的行加鎖就好了,這就是行鎖。數據庫

在事務中,事務1更新了一行主鍵爲1的數據行,那麼,在這個事務釋放鎖以前,事務2是不能操做的。併發

 

前阿里數據庫專家總結的MySQL裏的各類鎖(下篇)

 

另外,有一個不少人容易混淆的概念,就是行鎖何時釋放?高併發

搞清這個事情,須要瞭解什麼叫做 兩階段鎖。性能

什麼是兩階段鎖呢?舉個例子你就明白了。優化

前阿里數據庫專家總結的MySQL裏的各類鎖(下篇)

 這裏事務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中,會產生以下的五個間隙範圍

前阿里數據庫專家總結的MySQL裏的各類鎖(下篇)

 間隙鎖就是對這五個間隙範圍加鎖,防止新的記錄插入。

注意,行鎖的衝突是行與行之間的衝突,是行鎖與行鎖之間的。與間隙鎖衝突的是往「間隙中插入數據」這個操做,間隙鎖自己不會產生衝突。

間隙鎖和行鎖合稱爲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講》

 

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

知識碎片從新梳理,構建Java知識圖譜:https://github.com/saigu/JavaKnowledgeGraph(歷史文章查閱很是方便)

掃碼關注個人公衆號「阿丸筆記」,第一時間獲取最新更新。同時能夠免費獲取海量Java技術棧電子書、各個大廠面試題。

阿丸筆記

相關文章
相關標籤/搜索