自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

怎么解決MySQL死鎖問(wèn)題的?

數(shù)據(jù)庫(kù) MySQL
死鎖是并發(fā)系統(tǒng)中常見(jiàn)的問(wèn)題,同樣也會(huì)出現(xiàn)在數(shù)據(jù)庫(kù)MySQL的并發(fā)讀寫(xiě)請(qǐng)求場(chǎng)景中。當(dāng)兩個(gè)及以上的事務(wù),雙方都在等待對(duì)方釋放已經(jīng)持有的鎖或因?yàn)榧渔i順序不一致造成循環(huán)等待鎖資源,就會(huì)出現(xiàn)“死鎖”。

大家好,我是狼王,一個(gè)愛(ài)打球的程序員

咱們使用 MySQL 大概率上都會(huì)遇到死鎖問(wèn)題,這實(shí)在是個(gè)令人非常頭痛的問(wèn)題。本文將會(huì)對(duì)死鎖進(jìn)行相應(yīng)介紹,對(duì)常見(jiàn)的死鎖案例進(jìn)行相關(guān)分析與探討,以及如何去盡可能避免死鎖給出一些建議。

話(huà)不多說(shuō),開(kāi)整!

什么是死鎖

死鎖是并發(fā)系統(tǒng)中常見(jiàn)的問(wèn)題,同樣也會(huì)出現(xiàn)在數(shù)據(jù)庫(kù)MySQL的并發(fā)讀寫(xiě)請(qǐng)求場(chǎng)景中。當(dāng)兩個(gè)及以上的事務(wù),雙方都在等待對(duì)方釋放已經(jīng)持有的鎖或因?yàn)榧渔i順序不一致造成循環(huán)等待鎖資源,就會(huì)出現(xiàn)“死鎖”。常見(jiàn)的報(bào)錯(cuò)信息為 Deadlock found when trying to get lock...。

舉例來(lái)說(shuō) A 事務(wù)持有 X1 鎖 ,申請(qǐng) X2 鎖,B事務(wù)持有 X2 鎖,申請(qǐng) X1 鎖。A 和 B 事務(wù)持有鎖并且申請(qǐng)對(duì)方持有的鎖進(jìn)入循環(huán)等待,就造成了死鎖。

 

如上圖,是右側(cè)的四輛汽車(chē)資源請(qǐng)求產(chǎn)生了回路現(xiàn)象,即死循環(huán),導(dǎo)致了死鎖。

從死鎖的定義來(lái)看,MySQL 出現(xiàn)死鎖的幾個(gè)要素為:

  1. 兩個(gè)或者兩個(gè)以上事務(wù)
  2. 每個(gè)事務(wù)都已經(jīng)持有鎖并且申請(qǐng)新的鎖
  3. 鎖資源同時(shí)只能被同一個(gè)事務(wù)持有或者不兼容
  4. 事務(wù)之間因?yàn)槌钟墟i和申請(qǐng)鎖導(dǎo)致彼此循環(huán)等待

InnoDB 鎖類(lèi)型

為了分析死鎖,我們有必要對(duì) InnoDB 的鎖類(lèi)型有一個(gè)了解。

MySQL InnoDB 引擎實(shí)現(xiàn)了標(biāo)準(zhǔn)的行級(jí)別鎖:共享鎖( S lock ) 和排他鎖 ( X lock )

  1. 不同事務(wù)可以同時(shí)對(duì)同一行記錄加 S 鎖。
  2. 如果一個(gè)事務(wù)對(duì)某一行記錄加 X 鎖,其他事務(wù)就不能加 S 鎖或者 X 鎖,從而導(dǎo)致鎖等待。

如果事務(wù) T1 持有行 r 的 S 鎖,那么另一個(gè)事務(wù) T2 請(qǐng)求 r 的鎖時(shí),會(huì)做如下處理:

  1. T2 請(qǐng)求 S 鎖立即被允許,結(jié)果 T1 T2 都持有 r 行的 S 鎖
  2. T2 請(qǐng)求 X 鎖不能被立即允許

如果 T1 持有 r 的 X 鎖,那么 T2 請(qǐng)求 r 的 X、S 鎖都不能被立即允許,T2 必須等待 T1 釋放 X 鎖才可以,因?yàn)?X 鎖與任何的鎖都不兼容。共享鎖和排他鎖的兼容性如下所示:

間隙鎖( gap lock )

間隙鎖鎖住一個(gè)間隙以防止插入。假設(shè)索引列有2, 4, 8 三個(gè)值,如果對(duì) 4 加鎖,那么也會(huì)同時(shí)對(duì)(2,4)和(4,8)這兩個(gè)間隙加鎖。其他事務(wù)無(wú)法插入索引值在這兩個(gè)間隙之間的記錄。但是,間隙鎖有個(gè)例外:

  • 如果索引列是唯一索引,那么只會(huì)鎖住這條記錄(只加行鎖),而不會(huì)鎖住間隙。
  • 對(duì)于聯(lián)合索引且是唯一索引,如果 where 條件只包括聯(lián)合索引的一部分,那么依然會(huì)加間隙鎖。

next-key lock

next-key lock 實(shí)際上就是 行鎖+這條記錄前面的 gap lock 的組合。假設(shè)有索引值10,11,13和 20,那么可能的 next-key lock 包括:

(負(fù)無(wú)窮,10],(10,11],(11,13],(13,20],(20,正無(wú)窮)

在 RR 隔離級(jí)別下,InnoDB 使用 next-key lock 主要是防止幻讀問(wèn)題產(chǎn)生。

意向鎖( Intention lock )

InnoDB 為了支持多粒度的加鎖,允許行鎖和表鎖同時(shí)存在。為了支持在不同粒度上的加鎖操作,InnoDB 支持了額外的一種鎖方式,稱(chēng)之為意向鎖( Intention Lock )。意向鎖是將鎖定的對(duì)象分為多個(gè)層次,意向鎖意味著事務(wù)希望在更細(xì)粒度上進(jìn)行加鎖。意向鎖分為兩種:

  1. 意向共享鎖( IS ):事務(wù)有意向?qū)Ρ碇械哪承┬屑庸蚕礞i
  2. 意向排他鎖( IX ):事務(wù)有意向?qū)Ρ碇械哪承┬屑优潘i

由于 InnoDB 存儲(chǔ)引擎支持的是行級(jí)別的鎖,因此意向鎖其實(shí)不會(huì)阻塞除全表掃描以外的任何請(qǐng)求。表級(jí)意向鎖與行級(jí)鎖的兼容性如下所示:

插入意向鎖( Insert Intention lock )

插入意向鎖是在插入一行記錄操作之前設(shè)置的一種間隙鎖,這個(gè)鎖釋放了一種插入方式的信號(hào),即多個(gè)事務(wù)在相同的索引間隙插入時(shí)如果不是插入間隙中相同的位置就不需要互相等待。假設(shè)某列有索引值2,6,只要兩個(gè)事務(wù)插入位置不同(如事務(wù) A 插入3,事務(wù) B 插入4),那么就可以同時(shí)插入。

鎖模式兼容矩陣

橫向是已持有鎖,縱向是正在請(qǐng)求的鎖:

閱讀死鎖日志

在進(jìn)行具體案例分析之前,咱們先了解下如何去讀懂死鎖日志,盡可能地使用死鎖日志里面的信息來(lái)幫助我們來(lái)解決死鎖問(wèn)題。

后面測(cè)試用例的數(shù)據(jù)庫(kù)場(chǎng)景如下:MySQL 5.7 事務(wù)隔離級(jí)別為 RR

表結(jié)構(gòu)和數(shù)據(jù)如下:

 

 

測(cè)試用例如下:

 

 

 

通過(guò)執(zhí)行show engine innodb status 可以查看到最近一次死鎖的日志。

日志分析如下:

***** (1) TRANSACTION: TRANSACTION 2322, ACTIVE 6 sec starting index read

事務(wù)號(hào)為2322,活躍 6秒,starting index read 表示事務(wù)狀態(tài)為根據(jù)索引讀取數(shù)據(jù)。常見(jiàn)的其他狀態(tài)有:

mysql tables in use 1 說(shuō)明當(dāng)前的事務(wù)使用一個(gè)表。

locked 1 表示表上有一個(gè)表鎖,對(duì)于 DML 語(yǔ)句為 LOCK_IX

LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)

LOCK WAIT 表示正在等待鎖,2 lock struct(s) 表示 trx->trx_locks 鎖鏈表的長(zhǎng)度為2,每個(gè)鏈表節(jié)點(diǎn)代表該事務(wù)持有的一個(gè)鎖結(jié)構(gòu),包括表鎖,記錄鎖以及自增鎖等。本用例中 2locks 表示 IX 鎖和lock_mode X (Next-key lock)

1 row lock(s) 表示當(dāng)前事務(wù)持有的行記錄鎖/ gap 鎖的個(gè)數(shù)。

MySQL thread id 37, OS thread handle 140445500716800, query id 1234 127.0.0.1 root updating

MySQL thread id 37 表示執(zhí)行該事務(wù)的線(xiàn)程 ID 為 37 (即 show processlist; 展示的 ID )

delete from student where stuno=5 表示事務(wù)1正在執(zhí)行的 sql,比較難受的事情是 show engine innodb status 是查看不到完整的 sql 的,通常顯示當(dāng)前正在等待鎖的 sql。

***** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2322 lock_mode X waiting

RECORD LOCKS 表示記錄鎖, 此條內(nèi)容表示事務(wù) 1 正在等待表 student 上的 idx_stuno 的 X 鎖,本案例中其實(shí)是 Next-Key Lock 。

事務(wù)2的 log 和上面分析類(lèi)似:

***** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2321 lock_mode X

顯示事務(wù) 2 的 insert into student(stuno,score) values(2,10) 持有了 a=5 的 Lock mode X

| LOCK_gap,不過(guò)我們從日志里面看不到事務(wù)2執(zhí)行的 delete from student where stuno=5;

這點(diǎn)也是造成 DBA 僅僅根據(jù)日志難以分析死鎖的問(wèn)題的根本原因。

***** (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2321 lock_mode X locks gap before rec insert intention waiting

表示事務(wù) 2 的 insert 語(yǔ)句正在等待插入意向鎖 lock_mode X locks gap before rec insert intention waiting ( LOCK_X + LOCK_REC_gap )

經(jīng)典案例分析

案例一:事務(wù)并發(fā) insert 唯一鍵沖突

表結(jié)構(gòu)和數(shù)據(jù)如下所示:

 

 

測(cè)試用例如下:

 

 

日志分析如下:

1.事務(wù) T2 insert into t7(id,a) values (26,10) 語(yǔ)句 insert 成功,持有 a=10 的 排他行鎖( Xlocks rec but no gap )

2.事務(wù) T1 insert into t7(id,a) values (30,10), 因?yàn)門(mén)2的第一條 insert 已經(jīng)插入 a=10 的記錄,事務(wù) T1 insert a=10 則發(fā)生唯一鍵沖突,需要申請(qǐng)對(duì)沖突的唯一索引加上S Next-key Lock( 即 lock mode S waiting ) 這是一個(gè)間隙鎖會(huì)申請(qǐng)鎖住(,10],(10,20]之間的 gap 區(qū)域。

3.事務(wù) T2 insert into t7(id,a) values (40,9)該語(yǔ)句插入的 a=9 的值在事務(wù) T1 申請(qǐng)的 gap 鎖4-10之間, 故需事務(wù) T2 的第二條 insert 語(yǔ)句要等待事務(wù) T1 的 S-Next-key Lock 鎖釋放,在日志中顯示 lock_mode X locks gap before rec insert intention waiting 。

案例一:先 update 再 insert 的并發(fā)死鎖問(wèn)題

表結(jié)構(gòu)如下,無(wú)數(shù)據(jù):

 

 

測(cè)試用例如下:

 

 

死鎖分析:

可以看到兩個(gè)事務(wù) update 不存在的記錄,先后獲得間隙鎖( gap 鎖),gap 鎖之間是兼容的所以在update環(huán)節(jié)不會(huì)阻塞。兩者都持有 gap 鎖,然后去競(jìng)爭(zhēng)插入意向鎖。當(dāng)存在其他會(huì)話(huà)持有 gap 鎖的時(shí)候,當(dāng)前會(huì)話(huà)申請(qǐng)不了插入意向鎖,導(dǎo)致死鎖。

如何盡可能避免死鎖

  1. 合理的設(shè)計(jì)索引,區(qū)分度高的列放到組合索引前面,使業(yè)務(wù) SQL 盡可能通過(guò)索引定位更少的行,減少鎖競(jìng)爭(zhēng)。
  2. 調(diào)整業(yè)務(wù)邏輯 SQL 執(zhí)行順序, 避免 update/delete 長(zhǎng)時(shí)間持有鎖的 SQL 在事務(wù)前面。
  3. 避免大事務(wù),盡量將大事務(wù)拆成多個(gè)小事務(wù)來(lái)處理,小事務(wù)發(fā)生鎖沖突的幾率也更小。
  4. 以固定的順序訪(fǎng)問(wèn)表和行。比如兩個(gè)更新數(shù)據(jù)的事務(wù),事務(wù) A 更新數(shù)據(jù)的順序?yàn)?1,2;事務(wù) B 更新數(shù)據(jù)的順序?yàn)?2,1。這樣更可能會(huì)造成死鎖。
  5. 在并發(fā)比較高的系統(tǒng)中,不要顯式加鎖,特別是是在事務(wù)里顯式加鎖。如 select … for update 語(yǔ)句,如果是在事務(wù)里(運(yùn)行了 start transaction 或設(shè)置了autocommit 等于0),那么就會(huì)鎖定所查找到的記錄。
  6. 盡量按主鍵/索引去查找記錄,范圍查找增加了鎖沖突的可能性,也不要利用數(shù)據(jù)庫(kù)做一些額外額度計(jì)算工作。比如有的程序會(huì)用到 “select … where … order by rand();”這樣的語(yǔ)句,由于類(lèi)似這樣的語(yǔ)句用不到索引,因此將導(dǎo)致整個(gè)表的數(shù)據(jù)都被鎖住。
  7. 優(yōu)化 SQL 和表設(shè)計(jì),減少同時(shí)占用太多資源的情況。比如說(shuō),減少連接的表,將復(fù)雜 SQL 分解為多個(gè)簡(jiǎn)單的 SQL。

 好了。今天就說(shuō)到這了,我還會(huì)不斷分享自己的所學(xué)所想,希望我們一起走在成功的道路上!

本文轉(zhuǎn)載自微信公眾號(hào)「狼王編程」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系狼王編程公眾號(hào)。

 

責(zé)任編輯:姜華 來(lái)源: 狼王編程
相關(guān)推薦

2021-10-20 20:27:55

MySQL死鎖并發(fā)

2011-08-24 17:41:16

MySQL死鎖

2010-04-29 17:46:31

Oracle死鎖

2017-06-14 22:11:57

數(shù)據(jù)庫(kù)MySQL死鎖

2010-06-30 14:15:08

SQL Server死

2017-05-03 16:26:24

MySQL并發(fā)死鎖

2024-01-10 09:44:11

MySQL死鎖

2016-10-20 15:27:18

MySQLredo死鎖

2024-03-18 09:10:00

死鎖日志binlog

2022-07-05 11:48:47

MySQL死鎖表鎖

2021-03-26 10:40:16

MySQL鎖等待死鎖

2025-02-13 07:49:18

2023-02-08 07:04:20

死鎖面試官單元

2025-01-20 13:20:00

MySQL數(shù)據(jù)遷移數(shù)據(jù)庫(kù)

2020-04-14 10:20:12

MySQL數(shù)據(jù)庫(kù)死鎖

2024-10-16 11:40:47

2010-05-06 13:39:28

Oracle死鎖

2009-11-06 16:13:45

WCF回調(diào)

2017-06-07 16:10:24

Mysql死鎖死鎖日志

2018-05-29 11:44:22

數(shù)據(jù)庫(kù)MySQL死鎖
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)