吗。说明:(1)为了测试方便,以下测试使用test测试表,其表结构为:Table:testCreateTable:CREATETABLE`test`(`id`bigint(20)NOTNULL,`oid`bigint(20)NOTNULL,`status`tinyint(2)DEFAULT'0',PRIMARYKEY(`id`),KEY`idx_oid`(`oid`))ENGINE=InnoDBDEFAULTCHARSET=utf8(2)隔离级别下面的东西就是RR1.InsertIntentionLock1.1理解InsertIntentionLock官方介绍:InsertIntentionLocksinsertsomedata如下mysql>select*fromtest;+----+-----+--------+|编号|对象|状态|+----+-----+------+|1|1|0||2|2|0||5|5|0||10|10|0|+----+-----+--------+(1)执行两件事:timetrx_atrx_btime_1开始交易;time_2开始交易;time_3select*fromtestwhereoid=5forupdate;time_4插入测试集id=3,oid=3;(锁定等待,插入意图锁定)(2)显示引擎innodb状态:-------------TRANSACTIONS------------Trxidcounter57450Purgedonefortrx'sn:o<57448undon:o<0state:runningbutidleHistorylistlength716LISTOFTRANSACTIONSFOREACHSESSION:---TRANSACTION281479621176896,notstarted0lockstruct(s),heapsize1136,0rowlock(s)---TRANSACTION57449,ACTIVE4secinsertingmysqltablesinuse1,locked1LOCKWAIT2锁结构,heapsize1136,1rowlock(s),undologentries1MySQLthreadid3,OSthreadhandle123145316274176,queryid277localhostrootupdateinsertintotestsetid=3,oid=3------TRXHASBEENWAITING4SECFORTHISLOCKTOBEGRANTED:RECORDLOCKSspaceid1089pageno4nbits80indexidx_oidoftable`test`.`test`trxid57449lock_modeXlocksgapbeforerecinsertintentionwaiting//插入意向锁记录锁,堆号6PHYSICALRECORD:n_fields2;紧凑格式;信息位00:len8;十六进制8000000000000005;升序;;1:长度8;十六进制8000000000000004;asc;;--------------------TRANSACTION57448,ACTIVE8sec4lockstruct(s),heapsize1136,5rowlock(s)MySQL线程id4,OSthreadhandle123145316552704,queryid276localhostrootcleaningup其中Gapbeforerecinsertintentionwaitingisinsertintentionlock1.2insertintentionlock的特点和作用关于MySqlInnoDB如何通过insertintentionlock控制并发插入介绍insertintentionlock的主要作用:提高并发插入能力Insertintentionlock本质上是visible它变成了一个GapLock。普通GapLock不允许在(上一条记录,本条记录)范围内插入数据,插入意向锁。GapLock允许在(前一条记录,这条记录)范围内插入数据。为了提高并发插入的性能,多个事务同时向同一个索引范围(区间)写入不同的数据,不需要等待其他事务完成,也不会出现锁等待。包含意向锁三个字,但不属于意向锁,属于间隙锁,因为意向锁是表锁,插入意向锁是行锁。1.3insert意向锁与其他锁的兼容性Mysql锁详解(行锁、表锁、意向锁、间隙锁、insert意向锁)给出了锁的介绍、锁的兼容性以及常见的insert意向锁冲突。注1:兼容性与加锁顺序有关,因此兼容性表不是对称的注2:该表的读取方式为逐列读取;是否兼容gapinsertintentionrecordnext-keygapyesyesinsertintentionnoyesrecordyesno是否兼容next-key从图中可以看出,如果前一个事务持有gap锁或者next-key锁,则后面的事务如果要持有insert意向锁就会不兼容,就会出现锁等待。2、死锁分析从1.3节可以看出,锁等待插入意向case1有两种情况:事务a获得gap锁;事务b插入意图等待case2:事务a获得next-key锁;transactionbwaitsforinsertintent;2.1死锁重现最近网上出现死锁,原因是基于case2,和mysql并发insert死锁问题-gap,insertintent锁冲突有点类似,不过我遇到的是,update和insert两个事务并发时死锁的操作,具体如下:事务开始前,数据情况如下:mysql>select*fromtest;+----+-----+-------+|编号|对象|状态|+----+-----+------+|1|1|0||2|2|0||5|5|0||10|10|0|+----+-----+--------+按照时间顺序进行操作,出现死锁,如下:timetrx_atrx_btime_1starttransaction;time_2开始事务;time_3更新测试集status=1whereoid=5;time_4更新测试集status=1whereoid=5;(锁等待)time_5insertintotestsetid=4,oid=5;ERROR1213(40001):尝试获取锁时发现死锁;tryrestarttransactionhere发生死锁时,截图如下:deadlock1.png执行showengineinnodbstatus,找到对应的LATESTDETECTEDDEADLOCK如下(附部分分析):------------------------最新检测到的死锁-------------------------2019-07-0819:55:230x700000d95000***(1)TRANSACTION:TRANSACTION57432,ACTIVE44secstartingindexreadmysqltablesinuse1,locked1LOCKWAIT2lockstruct(s),heapsize1136,1rowlock(s)MySQLthreadid3,OSthreadhandle123145316274176,queryid176localhostrootupdatingupdatetestsetstatus=1whereoid=5//transactionb操作语句***(1)WAITINGFORTHISLOCKTOBEGRANTED:RECORDLOCKSspaceid1089pageno4nbits72indexidx_oidoftable`test`.`test`trxid57432lock_modeXwaiting//RECORDLOCKS表示记录锁,空间id为1089,页号4,n位72表示聚集索引记录锁结构还剩72位//表示事务1在表test上等待idx_oid的X锁;在这种情况下,它实际上是Next-KeylockRecordlock,heapno4PHYSICALRECORD:n_fields2;紧凑格式;信息位00:len8;十六进制8000000000000005;升序;;1:长度8;十六进制8000000000000005;asc;;***(2)TRANSACTION:TRANSACTION57431,ACTIVE59secinsertlockingmysql,ed中的表使用15个锁结构,堆大小1136,4行锁,撤消日志条目2MySQLthreadid4,OSthreadhandle123145316552704,queryid177localhostrootupdateinsertintotestsetid=4,oid=5//事务操作asql***(2)HOLDSTHELOCK(S):RECORDLOCKSspaceid1089pageno4nbits72indexidx_oidoftable`test`.`test`trxid57431lock_modeX//showtransaction2insertintotestsetid=4,oid=5holdsLockmodeXwitha=5|LOCK_GAP记录锁,堆4物理记录:n_fields2;紧凑格式;信息位00:len8;十六进制8000000000000005;升序;;1:长度8;十六进制8000000000000005;asc;;***(2)WAITINGFORTHISLOCKTOBEGRANTED:RECORDLOCKSspaceid1089pageno4nbits72indexidx_oidoftable`test`.`test`trxid57431lock_modeXlocksgapbeforerecinsert意图waiting//表示事务2的insert语句正在等待Insertintentlocklock_modeXlocksgapbeforerecinsertintentionwaiting(LOCK_X+LOCK_REC_GAP)这里需要注意的是锁组合,类似于lock_modeXwaiting,lock_modeX,lock_modeXlocksgapbeforerecinsert意图等待是我们死锁分析重点的核心记录锁,堆号4PHYSICALRECORD:n_fields2;紧凑格式;信息位00:len8;十六进制8000000000000005;升序;;1:长度8;十六进制8000000000000005;asc;;***WEROLLBACKTRANSACTION(1)Howtoreadthedeadlocklog关于如何解读死锁日志有比较详细的介绍;重点分析如下:(1)transactionbanalysisupdatetestsetstatus=1whereoid=5//transactionboperationstatement*(1)WAITINGFORTHISLOCKTOBEGRANTED:RECORDLOCKSspaceid1089pageno4nbits72indexidx_oidoftabletest.testtrxid57432lock_modeXwaiting//RECORDLOCKS表示记录锁,空间id为1089,页码4,nbits72表示聚簇索引记录锁结构还剩72位//表示事务b正在等待表test上idx_oid的X锁;在这种情况下,它实际上是Next-Keylock(2)transactionaparsesinsertintotestsetid=4,oid=5//事务a的操作sql*(2)HOLDSTHELOCK(S):RECORDLOCKSspaceid1089pageno4nbits72indexidx_oidoftabletest.testtrxid57431lock_modeX//displayTransaction2insertintotestsetid=4,oid=5holdsLockmodeXwitha=5|LOCK_GAP记录锁,堆号4物理记录:n_fields2;紧凑格式;信息位00:len8;十六进制8000000000000005;升序;;1:长度8;十六进制80000000000000005;asc;;*(2)WAITINGFORTHISLOCKTOBEGRANTED:RECORDLOCKSspaceid1089pageno4nbits72indexidx_oidoftabletest.testtrxid57431lock_modeXlocksgbeforerecinsertintentionwaiting//表示事务2的insert语句等待插入意向锁lock_modeXlocksgapbeforerecinsertintentionwaiting(LOCK_X+LOCK_REC_GAP)这里大家要注意锁的组合,类似lock_modeXwaiting,lock_modeX,lock_modeXlocksgapbeforerecinsert意图等待是我们死锁分析的核心点。具体分析如下:timetrx_atrx_btime_1starttransaction;time_2开始交易;time_3update测试集status=1whereoid=5;由于存在oid=5数据,申请next-key锁,申请成功;time_4更新测试集status=1whereoid=5;(锁等待)由于存在oid=5的数据,申请next-key锁,但是此时已经有next-key锁,所以在等待;time_5插入测试集id=4,oid=5;申请要插入的意向锁,但是已经有next-key锁,所以在等待;请参考1.3兼容性ERROR1213(40001):Deadlockfoundwhentryingtogetlock;尝试重新启动事务因此相互等待,并在一个环中等待。2.2注意上面序列中的两个并发操作不一定是死锁的。例如:事务开始前,数据状态如下:mysql>select*fromtest;+----+-----+--------+|编号|对象|状态|+----+-----+--------+|1|1|0||2|2|0||5|5|0||10|10|0|+----+-----+--------+按照时间顺序进行操作,不会死锁,如下:timetrx_atrx_btime_1starttransaction;time_2开始事务;time_3更新测试集status=1whereoid=5;time_4更新测试集status=1whereoid=5;(锁等待)time_5insertintotestsetid=6,oid=5;//(注意此时id=6,大于5)查询OK,1行影响原因,插入的主键id大于oid对应的主键id;(以下属于个人理解)因为next-keylock中的gaplock可以分解成两部分,(pre,5)和(5,next),pre和next分别是oid索引的数据和之前的数据为5时的下一个数据;等待锁时,先等待锁(pre,5),oid=5时对应的主键id小于数据的主键id5;所以当事务1插入主键id大于5的数据时,不会造成锁等待;另一个意向锁的插入实际上是一个间隙锁。当主键id大于5时,此时的gap间隔为(5,next),此时事务b正在等待加锁(pre,5),所以不会有加锁等待,所以执行成功。3.总结当死锁发生时,正是提升数据库能力的好时机。总结一下解决思路:(1)根据showengineinnodbstatus,找出对应的deadlockSQL,进而找出对应事务的完整执行;(2))特别注意在gap/next-key已经存在的情况下申请insert意图导致的锁等待;(3)借助INNODB_LOCKS、INNODB_LOCK_WAITS等表数据进一步分析。四、参考【1】如何读取死锁日志【2】Mysql并发insert死锁问题-gap、insertintent锁冲突【3】mysql锁详解(行锁、表锁、intent锁、Gap锁、insertintent锁)
