07 | 行鎖功過:怎么減少行鎖對性能的影響?

InnoDB 行鎖,如何減少鎖,提升并發(fā)度。

概要:1兩階段鎖(1鎖協(xié)議? ?2多行鎖沖突、影響并發(fā)度往后放)2死鎖檢測(設(shè)置超時(shí)、控制并發(fā)度)

一、兩階段鎖

1、兩階段鎖協(xié)議:

InnoDB?事務(wù)中,行鎖要時(shí)加上,結(jié)束釋放(不是不需要釋放)

id 是主鍵,事務(wù)B 被阻塞,直到A 兩個(gè)行鎖,都commit 時(shí)釋放

2、多行,最可能鎖沖突、影響并發(fā)度的鎖往后放

顧客 A 要在影院 B 購買電影票:

1.? 從顧客 A 余額中扣除票價(jià);

2.? 給影院 B 的余額增加票價(jià);

3.? 記錄一條交易日志。

兩條update ,?一條insert,保證原子性,放一個(gè)事務(wù)。順序:

不論怎樣排,行鎖都提交才釋放。如 3、1、2 ,影院余額這行鎖時(shí)間最少。減少事務(wù)之間鎖等待,提升并發(fā)度。

問題并沒完全解決

活動低價(jià)預(yù)售一年內(nèi)所有影票,只做一天?;顒訒r(shí)間開始時(shí), MySQL 掛了。登服務(wù)器看,CPU 消耗接近 100%,整個(gè)數(shù)據(jù)庫每秒就執(zhí)行不到 100 個(gè)事務(wù)。什么原因呢?

二、死鎖和死鎖檢測

1、死鎖:不同線程循環(huán)資源依賴,都在等待其他線程釋放資源時(shí),導(dǎo)致幾個(gè)線程無限等待

A 等B 放 id=2 行鎖, B 等 A 放 id=1 的行鎖。 互相等待死鎖。策略:

(1)innodb_lock_wait_timeout 設(shè)置超時(shí)時(shí)間

(2)死鎖檢測,發(fā)現(xiàn)死鎖后,回滾死鎖鏈條中的某個(gè)事務(wù),其他執(zhí)行。innodb_deadlock_detect 設(shè)置為 on(默認(rèn))。

正常情況用死鎖檢測(即主動死鎖檢測)innodb_lock_wait_timeout?默認(rèn)50s,設(shè)置太短,會誤傷,但是死鎖檢測也有額外負(fù)擔(dān):

事務(wù)被鎖,就要看它依賴線程有沒有被別人鎖住,如此循環(huán),造成死鎖。

2、所有事務(wù)更新同一行場景:

新線程被堵住,判斷是否由于自己加入導(dǎo)致死鎖,時(shí)間復(fù)雜度是 O(n)1000個(gè)并發(fā)線程同時(shí)更新同一行,死鎖檢測100 萬量級。檢測結(jié)果沒死鎖,但消耗大量CPU 資源

(1)確保一定不會出現(xiàn)死鎖,關(guān)掉死鎖檢測。

有風(fēng)險(xiǎn),出現(xiàn)死鎖回滾,重試,業(yè)務(wù)無損。關(guān)掉可能會大量超時(shí),業(yè)務(wù)有損。

(2)控制并發(fā)度。同一行最多只有 10 個(gè)線程更新,死鎖檢測成本低。但客戶端多,如600 個(gè),每個(gè)5 個(gè)并發(fā)線程,匯總服務(wù)端峰值3000。

思路:相同行更新,進(jìn)入引擎之前排隊(duì)。InnoDB 內(nèi)部沒有大量的死鎖檢測工作。

因此,這個(gè)并發(fā)控制要在數(shù)據(jù)庫服務(wù)端?;蛟?b>1.中間件實(shí)現(xiàn),2.修改 MySQL 源碼3.設(shè)計(jì)上優(yōu)化:

一行改成多行減少鎖沖突。10 個(gè)記錄,賬戶總額等于10 個(gè)記錄總和。每次給影院加金額隨機(jī)選。沖突概率為1/10,減少鎖等待死鎖檢測CPU 消耗。

看上無損,但要詳細(xì)設(shè)計(jì)。余額可能減少,比如退票,考慮變成 0 時(shí)特殊處理。

小結(jié)

MySQL 行鎖,兩階段鎖協(xié)議死鎖和死鎖檢測

兩階段協(xié)議,事務(wù)中語句順序。鎖多行時(shí),最可能造成鎖沖突、影響并發(fā)度的鎖申請往后放。

不能完全避免死鎖。所以引入死鎖和死鎖檢測,三個(gè)方案。減少死鎖主要方向,控制并發(fā)量。

問題:刪除一個(gè)表里前 10000 行數(shù)據(jù),選擇哪一種?

1、直接delete from T limit? ? 10000;

2、連接中循環(huán)20 次delete from T limit? ? 500;

3、20 同時(shí)執(zhí)行 delete from T limit 500。

答:2 ok? ??1鎖時(shí)間長;事務(wù)主從延遲? ? ????3人為造成鎖沖突,加劇并發(fā)

2串行執(zhí)行,每次時(shí)間相對短,等待時(shí)間也短。將資源分片,提高并發(fā)性。

ps:innodb行鎖,如何加鎖(依賴的隔離級別,是否有索引,是否是唯一索引,SQL的執(zhí)行計(jì)劃),特別是在RR隔離級別下的GAP鎖,對于innodb,RR級別是可以防止幻讀的。

如加上特定條件,將10000 行天然分開,可以考慮第三種。實(shí)際操作盡量拿到 ID 再刪除。

評論1

update列沒建索引,update一條記錄會鎖定整張表嗎?update t set t.name='abc' where t.name='cde'; name字段無索引。為何innodb不優(yōu)化一下,只鎖定name='cde'的列?

是的。如果update limit 1, 會怎么鎖?Innodb支持行鎖,沒有“列鎖”?

評論2

死鎖檢測innodb_deadlock_detect每條事務(wù)執(zhí)行前都會進(jìn)行檢測嗎?即使簡單更新單個(gè)表,并發(fā)量高,也消耗大量資源用于死鎖檢測?

加鎖訪問行上有鎖,才檢測

1. 一致性讀不會加鎖,不做死鎖檢測;

2. 不是每次死鎖檢測都掃所有事務(wù)。某個(gè)時(shí)刻,事務(wù)等待狀態(tài):

???B在等A,

???D在等C,

? ?來了E,要等D,E判斷D、C是否形成死鎖,檢測不管B、A

評論3

表鎖同表同時(shí)刻只有一個(gè)更新

MDL鎖:dml語句產(chǎn)生MDL讀鎖,而MDL讀鎖不是互斥,一張表同時(shí)多個(gè)dml語句操作。有點(diǎn)矛盾?

MDL鎖和表鎖不同結(jié)構(gòu)。如:

myisam 更新一行,加MDL讀鎖和表的寫鎖;

另線程要更新這表上另一行,也加MDL讀鎖表寫鎖。

第二個(gè)線程要等第一個(gè)線程執(zhí)行完成。

評論4

兩個(gè)update同時(shí)更新一條數(shù)據(jù)是互斥的。因?yàn)槎喾N鎖同時(shí)存在時(shí),以粒度最小的鎖為準(zhǔn)的原因么?

不是,多種鎖,必須“全部不互斥”才并行,有互斥就等

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容