文章摘要:在線上環(huán)境遇到數(shù)據(jù)庫死鎖問題該如何分析并解決問題呢?
雖然很多童鞋在學(xué)數(shù)據(jù)庫課程時都了解數(shù)據(jù)庫隔離級別、死鎖和事務(wù)等概念,但在測試/線上環(huán)境遇到死鎖卻不一定能夠及時分析并解決這類問題。本文主要以作者在測試環(huán)境中遇到的一個死鎖Case說起,首先還原出現(xiàn)死鎖的現(xiàn)場和條件,并結(jié)合排查業(yè)務(wù)應(yīng)用工程日志、MySQL數(shù)據(jù)庫狀態(tài)信息等方式,同時給出MySQL鎖的基本概念,再通過閱讀日志深入定位并分析出現(xiàn)死鎖的原因,最后講下MySQL InnoDB的加鎖原理以及如降低死鎖發(fā)生的機率。
一、出現(xiàn)死鎖的當(dāng)前場景
在測試環(huán)境上做業(yè)務(wù)流程的聯(lián)調(diào)驗證自測,在跑自測用例時,突然發(fā)現(xiàn)在多線程并發(fā)情況下有數(shù)據(jù)未從業(yè)務(wù)表中刪除完成,通過Spring Boot工程打印出的Log日志中可以看到出現(xiàn)了死鎖問題。下面將先給大家還原下死鎖的當(dāng)前場景,然后逐步分析和使用正確方法排查死鎖的原因。
1、數(shù)據(jù)庫表結(jié)構(gòu)
CREATETABLE `hw_band_width_bill_record` (
`ID` bigint(20) unsigned NOT NULLAUTO_INCREMENT COMMENT '主鍵id,自增',
`CUSTOMER_ID` varchar(50) NOT NULL COMMENT ……,
`USER_ID` varchar(50) NOT NULL COMMENT ……,
`CLOUD_SER_TYPE_CODE` varchar(50) NOT NULL COMMENT……,
`RES_TYPE_CODE` varchar(50) NOT NULL COMMENT ……,
`RES_SPEC_CODE` varchar(50) NOT NULL COMMENT ……,
`RES_INSTANCE_ID` varchar(50) DEFAULT NULL COMMENT……,
`RES_ATTR_VALUES` varchar(50) DEFAULT NULLCOMMENT ……,
(限于篇幅問題這里省略該數(shù)據(jù)表的其他字段)
PRIMARY KEY (`ID`),
KEY`custId_product_res_type_spec_index`(`CUSTOMER_ID`,`RES_TYPE_CODE`,`RES_SPEC_CODE`,`RES_ATTR_VALUES`) USING BTREE)
ENGINE=InnoDBAUTO_INCREMENT=54 DEFAULT CHARSET=utf8 COMMENT='……'
其中,`ID`為主鍵索引,`CUSTOMER_ID`,`RES_TYPE_CODE`,`RES_SPEC_CODE`,`RES_ATTR_VALUES`等字段組成了非唯一的普通BTREE索引。
2、業(yè)務(wù)庫的事務(wù)隔離級別
可以通過“SELECT @@tx_isolation”的SQL來查詢當(dāng)前數(shù)據(jù)庫的事務(wù)隔離級別。
mysql>SELECT @@tx_isolation;
+-----------------+
|@@tx_isolation|
+-----------------+
|REPEATABLE-READ |
+-----------------+
1row in set (0.00 sec)
3、業(yè)務(wù)應(yīng)用工程的Log日志
當(dāng)業(yè)務(wù)應(yīng)用工程出現(xiàn)異常或者報錯時,絕大部分童鞋的第一反應(yīng)肯定都是去工程對應(yīng)的Log日志里面去排查定位問題。對應(yīng)于該死鎖問題Case的工程Log日志如下:org.springframework.dao.DeadlockLoserDataAccessException:###Error updating database.Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:Deadlock found when trying to get lock; try restarting transaction###The error may involveHwBandWidthBillRecordMapper.deleteResBwBillsByIdAndSpecValues-Inline
###The error occurred while setting parameters
###SQL: delete from hw_band_width_bill_recordwhere CUSTOMER_ID = ?and CLOUD_SER_TYPE_CODE = ?and RES_TYPE_CODE = ?and RES_SPEC_CODE = ?and RES_ATTR_VALUES = ?
###Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:Deadlock found when trying to get lock; try restarting transaction; SQL []; Deadlock
found when trying to get lock; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock;try restarting transaction?………(限于篇幅問題這里省略了部分日志) ?Caused by:com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlockfound when trying to get lock; try restarting transaction
從以上打印的堆棧日志里面可以清楚的發(fā)現(xiàn)業(yè)務(wù)工程代碼在多線程并發(fā)的環(huán)境下執(zhí)行了Delete SQL語句后出現(xiàn)了死鎖異常。不過,僅僅通過上述日志還不足以清楚地分析和查明出現(xiàn)死鎖異常的根本原因,那怎么辦?下面將通過MySQL的InnoDB的狀態(tài)日志進行進一步的深入分析。
4、MySQL數(shù)據(jù)庫死鎖日志信息
可能很多做過開發(fā)的童鞋都沒有自己登錄過數(shù)據(jù)庫服務(wù)器,排查過MySQL的InnoDB Status的狀態(tài)日志信息來深入分析死鎖問題。這里,我們可以先進入測試/線上環(huán)境數(shù)據(jù)庫虛擬機的數(shù)據(jù)庫安裝bin目錄下,通過“mysql-h localhost -P 3306 -u test -p”命令來連接登錄。然后使用“SHOW ENGINE?INNODB STATUS”命令查詢數(shù)據(jù)庫的最近一次死鎖日志信息。這里需要注意的是,該命令只能查看到最近一條死鎖日志信息,如果想看到多條歷史死鎖可以在MySQL中把死鎖信息打印到錯誤日志里,開啟如下變量即可:
set global innodb_print_all_deadlocks= 1;
本Case中的死鎖日志信息如下:
------------------------
LATESTDETECTED DEADLOCK
------------------------
2017-12-0519:46:52 7f6d3e588700
***(1) TRANSACTION: TRANSACTION10375675, ACTIVE 0 sec fetching rows mysqltables in use 1, locked 1 LOCKWAIT 4 lock struct(s), heap size 1184, 2 row lock(s), undo log entries 4
MySQLthread id 550477, OS thread handle 0x7f6db6ab8700, query id 33896336 10.129.3.1test updating
deletefrom hw_band_width_bill_record where CUSTOMER_ID ='0314e4814d014eaabf4ab09f7fa97fed' and CLOUD_SER_TYPE_CODE ='hws.service.type.vpc' and RES_TYPE_CODE ='hws.resource.type.bandwidth' and RES_SPEC_CODE = '19_bgp' and RES_ATTR_VALUES ='{"specSize":1}'?
***(1) WAITING FOR THIS LOCK TO BE GRANTED: RECORDLOCKS space id 1369 page no 3 n bits 104 index `PRIMARY` of table`res_hw_cloud_bill`.`hw_band_width_bill_record` trx id 10375675 lock_mode Xlocks rec but not gap waiting
***(2) TRANSACTION:
TRANSACTION10375676, ACTIVE 0 sec starting index read, thread declared inside InnoDB 5000 mysqltables in use 1, locked 1 4lock struct(s), heap size 1184, 3 row lock(s), undo log entries 5 MySQLthread id 550478, OS thread handle 0x7f6d3e588700, query id 33896338 10.129.3.1test updating?
deletefrom hw_band_width_bill_record where CUSTOMER_ID = '0314e4814d014eaabf4ab09f7fa97fed' and CLOUD_SER_TYPE_CODE ='hws.service.type.vpc' and RES_TYPE_CODE ='hws.resource.type.bandwidth' and RES_SPEC_CODE = '19_bgp' and RES_ATTR_VALUES ='{"specSize":6}'
***(2) HOLDS THE LOCK(S):
RECORDLOCKS space id 1369 page no 3 n bits 104 index `PRIMARY` of table`res_hw_cloud_bill`.`hw_band_width_bill_record` trx id 10375676 lock_mode Xlocks rec but not gap
***(2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORDLOCKS space id 1369 page no 3 n bits 104 index `PRIMARY` of table`res_hw_cloud_bill`.`hw_band_width_bill_record` trx id 10375676 lock_mode Xlocks rec but not gap waiting?
***WE ROLL BACK TRANSACTION (1)
------------
TRANSACTIONS
------------
從上面打印的這段日志中,我們可以進行一定的初步分析。從這段日志里可以看到,TRANSACTION 1和TRANSACTION 2分別持有一定數(shù)量的行鎖,然后又等待對方的鎖,最后MySQL檢測到Deadlock,然后選擇回滾了TRANSACTION 1:InnoDB目前處理死鎖的方法是將持有最少行級排他鎖的事務(wù)進行回滾。
二、對于業(yè)務(wù)庫死鎖的深入分析
在進一步深入分析MySQL的死鎖日志之前有必要先了解下MySQL數(shù)據(jù)庫的MVCC機制、鎖的概念和事務(wù)隔離級別。
1、MySQL InnoDB的MVCC機制與鎖的模型概念
MySQL InnoDB存儲引擎,實現(xiàn)了基于多版本的并發(fā)控制協(xié)議—MVCC (Multi-Version Concurrency Control)。InnoDB存儲引擎MVCC機制的優(yōu)點可以總結(jié)為,“讀不加鎖,讀寫不沖突”。這在讀多寫少的業(yè)務(wù)應(yīng)用中,讀寫不沖突是非常重要的,極大的增加了系統(tǒng)的并發(fā)度和解決各種性能問題。在InnoDB中常見的幾種鎖模型如下:
(1)LOCK_ORDINARY[next_key_lock],默認(rèn)是LOCK_ORDINARY,即next-keylock,鎖住行及其前面的間隙,其為行級記錄鎖和間隙鎖的結(jié)合,用于解決幻讀的問題。
(2)LOCK_GAP:間隙鎖,鎖住行以前的間隙,不鎖住本行。
(3)LOCK_REC_NOT_GAP:行級鎖,鎖住行而不鎖住任何間隙。
(4)LOCK_INSERT_INTENTION:插入意向鎖,如果插入的記錄在某個已經(jīng)鎖定的間隙內(nèi)為這個鎖。
因此在InnoDB中,讀操作大致可以概括為兩類:快照讀(snapshot read)與當(dāng)前讀(current read)??煺兆x,讀取的是記錄的可見版本(有可能是歷史版本),不用加鎖。當(dāng)前讀,讀取的是記錄的最新版本,并當(dāng)前讀返回的記錄,都會加上鎖,保證其他事務(wù)不會再并發(fā)修改這條記錄。
(a)快照讀
一般來說,簡單的Select SQL語句都屬于快照讀,例如“select * from where……”
(b)當(dāng)前讀
對于“insert/delete/update”等增刪改的SQL語句,屬于當(dāng)前讀,需要加鎖。例如如下語句:
select * from tablewhere ? lock in share mode;
select * from tablewhere ? for update;
insert into tablevalues (…);
update table set ?where ?;
delete from tablewhere ?;
2、說說數(shù)據(jù)庫的隔離級別
數(shù)據(jù)庫的事務(wù)隔離級別—Isolation Level,是數(shù)據(jù)庫的一個關(guān)鍵特性。相信對數(shù)據(jù)庫原理有所了解的朋友,一定都對4種隔離級別:Read Uncommited,Read Committed,Repeatable Read,Serializable有了比較深入的認(rèn)識。這里就不再對這4種隔離級別的定義進行詳細的闡述了,而是主要跟大家介紹下在MySQL InnoDB存儲引擎中對于上述的“當(dāng)前讀”,在這四種不同的隔離級別情況下加鎖情況有何區(qū)別?(一般“快照讀”可以忽略,基本一樣的)
(1)Read Uncommited(未提交讀)
在該級別下,可以讀取未提交記錄。此隔離級別,一般不太會使用。
(2)Read Committed(提交讀)
在該級別下,針對“當(dāng)前讀”,RC隔離級別保證對讀取到的記錄加鎖(記錄鎖),而不會在記錄之間加間隙鎖,允許新的記錄插入到被鎖定記錄的附近,所以再多次使用查詢語句時,可能得到不同的結(jié)果,允許不可重復(fù)讀。
(3)Repeatable Read(可重復(fù)讀)
在該級別下,針對“當(dāng)前讀”,RR隔離級別保證對讀取到的記錄加鎖(記錄鎖),同時保證對讀取的范圍加鎖,新的滿足查詢條件的記錄不能夠插入(間隙鎖,但是在唯一索引和非唯一索引條件下還是有一定區(qū)別的),解決了不可重復(fù)讀的問題,但可能存在幻讀(幻讀可以通過Next-Key鎖解決)。
(4)Serializable(序列化)
在該級別下,InnoDB隱式將全部讀操作視為“當(dāng)前讀”,并且要求事務(wù)序列化一個接一個執(zhí)行。因此,并發(fā)度急劇下降,一般情況下也不太會使用該隔離級別。
3、分析死鎖日志信息與降低死鎖的方法
通過上文的初步分析和對MySQL InnoDB死鎖的基本模型/DB事務(wù)隔離級別的介紹,現(xiàn)在再回過頭來看下本Case中的死鎖日志信息,應(yīng)該就會有一些相對深刻的理解了。下面將進一步給大家做更深入的分析,在篇幅的最后給出自己總結(jié)的一些降低死鎖發(fā)生頻率的大致方法。
(1)死鎖日志中信息提取
TRX1:10375675(出發(fā)死鎖權(quán)重回滾)
????LOCK HOLD:沒有提供該事務(wù)獲取到的鎖
????LOCKWAIT:
????????表:hw_band_width_bill_record
????????索引:`PRIMARY`
????????鎖模式:LOCKX|LOCK_REC_NOT_GAP
????????記錄:space id1369 page no 3 n bits 104
????????鎖的信息:該事務(wù)總共有2個行鎖,持有1個行鎖,另外一個1鎖處于鎖等待狀態(tài)
? 當(dāng)前發(fā)生死鎖的SQL語句:delete from hw_band_width_bill_record where CUSTOMER_ID = '0314e4814d014eaabf4ab09f7fa97fed' andCLOUD_SER_TYPE_CODE = 'hws.service.type.vpc' andRES_TYPE_CODE = 'hws.resource.type.bandwidth' andRES_SPEC_CODE = '19_bgp' andRES_ATTR_VALUES = '{"specSize":1}'
TRX2:10375676
????LOCK HOLD:該事務(wù)持有鎖的大致信息(鎖的模式為:LOCK X|LOCK_REC_NOT_GAP)LOCKWAIT:
????LOCKWAIT:
????????表:hw_band_width_bill_record
????????索引:`PRIMARY`
????????鎖模式:LOCKX|LOCK_REC_NOT_GAP
????????記錄:space id 1369 pageno 3 n bits 104
? ? ? ? 鎖的信息:該事務(wù)總共有3個行鎖,持有2個行鎖,另外一個行鎖處于鎖等待狀態(tài)
? 當(dāng)前發(fā)生死鎖的SQL語句:deletefrom hw_band_width_bill_record where CUSTOMER_ID ='0314e4814d014eaabf4ab09f7fa97fed' and CLOUD_SER_TYPE_CODE ='hws.service.type.vpc' and RES_TYPE_CODE ='hws.resource.type.bandwidth' and RES_SPEC_CODE = '19_bgp' and RES_ATTR_VALUES ='{"specSize":6}'、
從以上MySQL InnoDB死鎖日志的提取信息中即可看到,事務(wù)1和事務(wù)2有分別在等待對方的鎖釋放,形成了一個環(huán),因此產(chǎn)生了數(shù)據(jù)庫的死鎖。
(2)InnoDB行鎖難道鎖的不只是一行?
由于本Case中所建的數(shù)據(jù)庫表是用InnoDB引擎的,InnoDB支持行鎖和表鎖。而InnoDB行鎖的原理是通過給索引上的索引項加鎖來實現(xiàn)的。而這一點MySQL與Oracle數(shù)據(jù)庫有差別,后者是通過在數(shù)據(jù)塊中對相應(yīng)數(shù)據(jù)行加鎖來實現(xiàn)的。InnoDB這種行鎖實現(xiàn)特點表示:只有通過索引條件檢索數(shù)據(jù),InnoDB才使用行級鎖。如果未走到索引上,InnoDB將使用表鎖,會把執(zhí)行SQL語句中所有掃描過的行都鎖定(這里需要注意的是,如果在RR事務(wù)隔離級別下且索引為非唯一索引,不僅會對數(shù)據(jù)表中的每一行加上LOCK_REC_NOT_GAP的行鎖,而且還會兩數(shù)據(jù)行的間隙加上LOCK_GAP間隙鎖)。在實際的業(yè)務(wù)應(yīng)用開發(fā)中,要特別注意InnoDB行鎖的這一特性,否則可能導(dǎo)致大量的鎖沖突,從而影響系統(tǒng)并發(fā)性能。由于MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖。所以雖然是訪問不同行的記錄,但是如果是使用相同的索引鍵,也同樣會出現(xiàn)鎖沖突的。當(dāng)我們用范圍條件而不是相等條件檢索數(shù)據(jù),并請求共享或排他鎖時,InnoDB會給符合條件的已有數(shù)據(jù)記錄的索引項加鎖。
下面可以先看下在該Case中,我們業(yè)務(wù)表索引的情況。如下圖可以看到執(zhí)行的Delete SQL語句走的是范圍掃描,未正確走到建立的索引上(對于如何正確建立索引的問題可以看下之間寫的《大型分布式業(yè)務(wù)平臺數(shù)據(jù)庫常用優(yōu)化方法(上)》篇,該篇幅介紹索引的原理和如何正確使用索引)

了解MySQL InnoDB的加鎖原理和如何正確加索引后,只要調(diào)整下創(chuàng)建索引的字段(即為創(chuàng)建索引使用上圖中的where條件的5個字段,然后執(zhí)行Delete SQL語句即可實現(xiàn)覆蓋索引,MySQL InnoDB加的鎖為對應(yīng)的行鎖和行之間的GAP鎖)即可讓咱們的Delete SQL語句精確走到索引以緩解死鎖的問題。實際上,我再更新索引后,死鎖問題也確實得到了解決。
(3)MySQL InnoDB鎖與索引/隔離級別的關(guān)系
從上述篇幅中可以得到的結(jié)論是,“InnoDB行鎖的原理是通過給索引上的索引項加鎖來實現(xiàn)”,我們知道InnoDB對于主鍵使用了聚簇索引,這是一種數(shù)據(jù)存儲方式,表數(shù)據(jù)是和主鍵一起存儲,主鍵索引的葉結(jié)點存儲行數(shù)據(jù)。對于普通索引,其葉子節(jié)點存儲的是主鍵值。相信仔細閱讀了上面篇幅的同學(xué),對執(zhí)行未正確落到索引的“當(dāng)前讀”SQL,InnoDB引擎都會加表鎖的這一行為比較熟悉,因此這里不再贅述業(yè)務(wù)表無索引這一情況。這一節(jié)將通過2個小例子,來進一步闡述大家,在InnoDB引擎中可能不太會被關(guān)注到的鎖與索引/隔離級別的兩種關(guān)系。
1.非唯一索引+RC隔離級別
在下面第一個的實例中,假設(shè)數(shù)據(jù)庫的隔離級別為Read Committed隔離級別,表為table1(表字段由“id”、“token”和“message”組成,其中id字段為自增的主鍵,在token字段上建了一個非唯一索引),如果此時執(zhí)行“delete from table1 where token = ‘a(chǎn)sd’”,那么加鎖的情況會怎么樣呢?先來看下面這幅圖:

可以看到,由于token列上有非唯一索引,那么對應(yīng)的所有滿足SQL查詢條件的記錄,都會被加鎖。同時,這些記錄在主鍵索引上的記錄,也會被加鎖。
2.非唯一索引+RR隔離級別
在接下來的第二個實例中,假設(shè)將原來數(shù)據(jù)庫的隔離級別為由上面的RC級別改為,Repeatable Read隔離級別,表table1字段和索引均不變(表字段由“id”、“token”和“message”組成,其中id字段為自增的主鍵,在token字段上建了一個非唯一索引),如果此時仍然執(zhí)行上面這句SQL—“delete from table1 where token = ‘a(chǎn)sd’”,那么最后的加鎖行為會怎么樣的呢?可以先來看下下面這幅圖:

在上圖中,相對于前面的[Read Committed級別下token非唯一索引條件]看似相同,其實卻有很大的區(qū)別。主要區(qū)別在于,這幅圖中多了一個GAP鎖,而且GAP鎖看起來不是加在記錄上的,是加載兩條記錄之間的位置。這里的GAP鎖,就是在RR隔離級別下,相對于RC隔離級別,不會出現(xiàn)的不可重復(fù)讀的關(guān)鍵。確實,這個GAP鎖,鎖住的位置,也不是記錄本身,而是兩條記錄之間的間隙。所謂不可重復(fù)讀,就是同一個事務(wù),連續(xù)做兩次當(dāng)前讀 (例如:select * from table1 where token = ‘a(chǎn)sd’ for update;),那么這兩次當(dāng)前讀返回的是完全相同的記錄 (記錄數(shù)量一致,記錄本身也一致),第二次的當(dāng)前讀,不會比第一次返回更多的記錄 。然而在RR級別下,并不能解決幻影讀的問題。在標(biāo)準(zhǔn)的數(shù)據(jù)庫事務(wù)隔離級別中,幻讀是由更高的隔離級別?SERIALIZABLE?解決的,但是它也可以通過上文提到過的MySQL的 Next-Key 鎖解決(限于篇幅問題,這里就不展開介紹Next-Key鎖了)。
因此,在RR隔離級別下,token列上有一個非唯一索引,對應(yīng)SQL:delete from table1 where token = ‘a(chǎn)sd’;首先,通過token索引定位到第一條滿足查詢條件的記錄,先在記錄上加X鎖,在數(shù)據(jù)行之間的間隙加上GAP鎖,然后加主鍵聚簇索引上的記錄X鎖,然后返回;然后讀取下一條,重復(fù)進行。直至進行到第一條不滿足條件的記錄為止,不需要加記錄X鎖,但是仍舊需要加GAP鎖,最后返回結(jié)束。
(4)如何降低發(fā)生MySQL InnoDB死鎖?
DB死鎖在行鎖及事務(wù)場景下很難完全消除,但可以通過表設(shè)計和SQL調(diào)整等措施減少鎖沖突和死鎖,下面列舉了一些降低死鎖發(fā)生的主要方法:
a.盡量使用較低的隔離級別,比如如果發(fā)生了GAP間隙鎖,可以嘗試把DB的事務(wù)隔離級別調(diào)整成為RC(read committed)級別來避免。當(dāng)然在RC的隔離級別下需要考慮業(yè)務(wù)是否能夠接受“不可重復(fù)讀”的問題;
b.在業(yè)務(wù)上線之前精心設(shè)計并核查下業(yè)務(wù)表上創(chuàng)建的索引。業(yè)務(wù)工程DAO層中的SQL語句盡量使用索引訪問數(shù)據(jù)(如果對于自己的業(yè)務(wù)SQL不確定,可以使用“Explain”關(guān)鍵字來查看對應(yīng)的執(zhí)行計劃是怎么樣的),使加鎖更精確,從而減少鎖沖突的機會;
c.選擇合理的事務(wù)大小,小事務(wù)發(fā)生鎖沖突的概率一般也更?。粚τ谑褂肧pring Transaction注解的同學(xué),也可以考慮使用其編程式聲明Spring事務(wù)模板的方式來將類或者方法級別的事務(wù)劃分給代碼塊更小級別的事務(wù)。
d.在不同線程中去訪問一組DB的數(shù)據(jù)表時,盡量約定以相同的順序進行訪問;對于同一個單表而言,盡可能以固定的順序存取表中的行。這樣可以大大減少死鎖的機會;
e.對于一些特定的業(yè)務(wù)流程,可以使用提升DB鎖粒度的方式(在業(yè)務(wù)允許的情況下降低一定的并發(fā)度),比如表鎖,來減少出現(xiàn)死鎖的可能。
本文從一次測試環(huán)境的DB死鎖Case出發(fā),首先還原了發(fā)生死鎖的當(dāng)前場景,給出包括數(shù)據(jù)表結(jié)構(gòu)、業(yè)務(wù)庫的事務(wù)隔離級別、工程日志和數(shù)據(jù)庫死鎖日志在內(nèi)的信息;然后從這些信息中逐步分析,先介紹了InnoDB的鎖模型和MVCC機制,以及在四種不同隔離級別下當(dāng)前讀的不同區(qū)別;最后根據(jù)從死鎖日志出提取的信息出發(fā)分析了死鎖的根本原因,并給出降低死鎖產(chǎn)生幾率的一般方法。限于筆者的才疏學(xué)淺,對MySQL InnoDB的死鎖可能還有理解不到位的地方,如有闡述不合理之處還望留言一起探討。