07丨行锁功过:怎么减少行锁对性能的影响
1. 引言
??行鎖就是針對數(shù)據(jù)表中行記錄的鎖。這很好理解,比如事務(wù) A 更新了一行,而這時候事務(wù) B 也要更新同一行,則必須等事務(wù) A 的操作完成后才能進(jìn)行更新。
2. 從兩階段鎖說起
??我先給你舉個例子。在下面的操作序列中,事務(wù) B 的 update 語句執(zhí)行時會是什么現(xiàn)象呢?假設(shè)字段 id 是表 t 的主鍵。
??這個問題的結(jié)論取決于事務(wù) A 在執(zhí)行完兩條 update 語句后,持有哪些鎖,以及在什么時候釋放。你可以驗(yàn)證一下:實(shí)際上事務(wù) B 的 update 語句會被阻塞,直到事務(wù) A 執(zhí)行commit 之后,事務(wù) B 才能繼續(xù)執(zhí)行。
??在 InnoDB 事務(wù)中,行鎖是在需要的時候才加上的,但并不是不需要了就立刻釋放,而是要等到事務(wù)結(jié)束時才釋放。這個就是兩階段鎖協(xié)議。
??知道了這個設(shè)定,對我們使用事務(wù)有什么幫助呢?那就是,如果你的事務(wù)中需要鎖多個行,要把最可能造成鎖沖突、最可能影響并發(fā)度的鎖盡量往后放。
假設(shè)你負(fù)責(zé)實(shí)現(xiàn)一個電影票在線交易業(yè)務(wù),顧客 A 要在影院 B 購買電影票。我們簡化一點(diǎn),這個業(yè)務(wù)需要涉及到以下操作:
3. 死鎖和死鎖檢測
??當(dāng)并發(fā)系統(tǒng)中不同線程出現(xiàn)循環(huán)資源依賴,涉及的線程都在等待別的線程釋放資源時,就會導(dǎo)致這幾個線程都進(jìn)入無限等待的狀態(tài),稱為死鎖。這里我用數(shù)據(jù)庫中的行鎖舉個例子。
??這時候,事務(wù) A 在等待事務(wù) B 釋放 id=2 的行鎖,而事務(wù) B 在等待事務(wù) A 釋放 id=1 的行鎖。事務(wù) A 和事務(wù) B 在互相等待對方的資源釋放,就是進(jìn)入了死鎖狀態(tài)。當(dāng)出現(xiàn)死鎖以后,有兩種策略:
??正常情況下我們還是要采用第二種策略,即:主動死鎖檢測,而且innodb_deadlock_detect 的默認(rèn)值本身就是 on。主動死鎖檢測在發(fā)生死鎖的時候,是能夠快速發(fā)現(xiàn)并進(jìn)行處理的,但是它也是有額外負(fù)擔(dān)的。
??怎么解決由這種熱點(diǎn)行更新導(dǎo)致的性能問題呢?
- 如果你能確保這個業(yè)務(wù)一定不會出現(xiàn)死鎖,可以臨時把死鎖檢測關(guān)掉。但是這種操作本身帶有一定的風(fēng)險(xiǎn),因?yàn)闃I(yè)務(wù)設(shè)計(jì)的時候一般不會把死鎖當(dāng)做一個嚴(yán)重錯誤,畢竟出現(xiàn)死鎖了,就回滾,然后通過業(yè)務(wù)重試一般就沒問題了,這是業(yè)務(wù)無損的。而關(guān)掉死鎖檢測意味著可能會出現(xiàn)大量的超時,這是業(yè)務(wù)有損的。
- 另一個思路是控制并發(fā)度。根據(jù)上面的分析,你會發(fā)現(xiàn)如果并發(fā)能夠控制住,比如同一行同時最多只有 10 個線程在更新,那么死鎖檢測的成本很低,就不會出現(xiàn)這個問題。一個直接的想法就是,在客戶端做并發(fā)控制。但是,你會很快發(fā)現(xiàn)這個方法不太可行,因?yàn)榭蛻舳撕芏唷N乙娺^一個應(yīng)用,有 600 個客戶端,這樣即使每個客戶端控制到只有 5 個并發(fā)線程,匯總到數(shù)據(jù)庫服務(wù)端以后,峰值并發(fā)數(shù)也可能要達(dá)到 3000。
總結(jié)
以上是生活随笔為你收集整理的07丨行锁功过:怎么减少行锁对性能的影响的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 06 | 全局锁和表锁 : 给表加个字段
- 下一篇: 08 | 事务到底是隔离的还是不隔离的