死锁是指当两个或多个竞争事务彼此等待对方释放锁,从而导致事务永远无法停止的情况,在行锁级别,死锁是无法%避免的。
InnoDB引擎有内部的死锁探测器,当器发现有死锁的时候,太会回滚其中一个事务,并会报告立即可见的错误,当设计应用程序的时候,我们需要对这种情况准备合理的处理回滚。关于死锁的信息我们可以通过showengineinnodbstatus命令获取,解决死锁问题最简单的一种方法是超时,即当两个事物互相等待时,当一个等待时间超过了设置的某一个阈值时,其中一个事务进行回滚,另一个继续进行,在innodb中设置参数innodb_lock_wait_timeout来设置超时时间。超时机制虽然简单,但是其仅仅通过超时后对事务进行回滚的方式来处理,或者说其是根据FIFO的顺序选择回滚对象,但若超时的事务所占权重比较大,比如事务处理了很多更新,占用了很多undolog,这个时候采用简单的FIFO的方式就不太合适了,因为回滚这个事务的时间相对另一个事务所占用的时间可能会多很多。因此除了超时机制,当前数据库还都普遍采用wait-forgrap(等待图)的方式进行死锁检测,比超时的解决方案,这个是一种更加主动的死锁检测方式,innoDB存储引擎也采用这种方案。wait-forgraph要求数据库保存一下两种信息:1.锁的信息链表2.事务等待链表通过上述链表可以构造一张图,而在这个图中如果存在回路,就代表有死锁,因此资源相互发生等待,如上图,在TransactionwaitList中有4个事务t1,t2,t3,t4,t2占用row1的x排他锁,而t1想获取row1的s锁,这个时候有箭头从t1指向row1,在row2上t1和t4占有了s共享锁,而t3和t4想占用row2的排他锁。根据上图我们可以描绘wait-forgraph如下通过上图我们发现t1和t2之间有相互依赖情况,会出现死锁,通常来说Innodb存储引擎选择回滚undo代价小的事务。例子如下:createtablet(aintauto_increment,primarykey(a));insertintotvalues(1),(2),(3),(4);启动事务A和事务B分别插入一条数据事务A:begin;insertintotvalues(null);select*fromt;事务B:begin;insertintotvalues(null);select*fromt;在事务A和事务B分别将自己的id更新为对方的id事务A:updatetseta=6wherea=5;事务B:updatetseta=5wherea=6;我们会发现事务A执行后,sql处于等待状态,而执行事务B后马上就发现死锁产生ErrorCode:.Deadlockfoundwhentryingtogetlock;tryrestartingtransaction,同时我们发现事务A等待结束,并成功提交了。我们现在可以使用showengineinnoDBstatus命令来输出死锁信息showengineinnodbstatus;
LATESTDETECTEDDEADLOCK-------------------------07-:10:x***(1)TRANSACTION:TRANSACTION,ACTIVEsecupdatingordeletingmysqltablesinuse1,locked1LOCKWAIT3lockstruct(s),heapsize,2rowlock(s),undologentries2MySQLthreadid5,OSthreadhandle,queryidlocalhost::1goodhopeupdatingupdatetseta=6wherea=5***(1)WAITINGFORTHISLOCKTOBEGRANTED:RECORDLOCKSspaceidpageno3nbits80indexPRIMARYoftable`test`.`t`trxidlockmodeSlocksrecbutnotgapwaitingRecordlock,heapno7PHYSICALRECORD:n_fields3;