說透MySQL裏面的各種鎖

在上篇中,我們介紹了MySQL中的全局鎖和表鎖。今天,我們專注於介紹一下行鎖,這個在日常開發和麪試中常常困擾我們的問題。1.行鎖基礎由於全局鎖和表鎖對增刪改查的性能都會有較大影響,所以,我們自然會想到,只需要對有修改的行加鎖就行了,這就是行鎖。在事務中,事務1更新了一行主鍵爲1的數據行,那麼,在這個事務釋放鎖之前,事務2是不能操作的。另外,有一個很多人容易混淆的概念,就是行鎖什麼時候釋放?搞清這個事情,需要了解什麼叫作 兩階段鎖。什麼是兩階段鎖呢?舉個例子你就明白了。這裏事務2執行後會是什麼結果呢?如果你明白了兩階段鎖的含義,你就會知道,事務2的updat語句會阻塞,直到事務1提交以後才能繼續執行。所以,這裏再次強調一下,兩階段鎖的含義。在 InnoDB 事務中,行鎖是在需要的時候才加上的,但並不是語句執行完了了就立刻釋放, 而是要等到事務結束時才釋放。注意,除了update語句能加寫鎖外,另外,還有一種對select語句加寫鎖的方式,就是當前讀:Select …. for update2.行鎖進階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格式,也是一般公司數據庫的標準配置。現在,你知道原因了吧:)

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