转自:https://segmentfault.com/a/1190000004447459
线上某服务时不时报出如下异常(大约一天二十多次):“Deadlock found when trying to get lock;”。
图1 应用日志
1 .死锁是怎么被发现的?
1.1 死锁成因&&检测方法
左图那两辆车造成死锁了吗?不是!右图四辆车造成死锁了吗?是!
1.2 wait-for graph原理
图3 wait for graph
2. innodb隔离级别、索引与锁
2.1 锁与索引的关系
1)delete from msg where id=2;
2)delete from msg where token=’ cvs’;
图6
3)delete from msg where message=订单号是多少’;
图7
2.2 锁与隔离级别的关系
图8
innodb的RR隔离级别可以避免幻读发生,怎么实现?当然需要借助于锁了!
图9
3.死锁成因
3.1不同表相同记录行锁冲突
图10
3.2相同表记录行锁冲突
图11
3.3不同索引锁冲突
图12
3.4 gap锁冲突
图13
4.如何尽可能避免死锁
1)以固定的顺序访问表和行。比如对第2节两个job批量更新的情形,简单方法是对id列表先排序,后执行,这样就避免了交叉等待锁的情形;又比如对于3.1节的情形,将两个事务的sql顺序调整为一致,也能避免死锁。
2)大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
3)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
4)降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
5)为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。
5.如何定位死锁成因
1)通过应用业务日志定位到问题代码,找到相应的事务对应的sql;
start tran
1 deleteHeartCheckDOByToken
2 updateSessionUser
...
commit
2)确定数据库隔离级别。
3)找DBA执行下show InnoDB STATUS看看最近死锁的日志。
start tran
1 updateSessionUser
2 deleteHeartCheckDOByToken
...
commit
这不就是图10描述的死锁嘛!
关于阿里百川
阿里百川(baichuan.taobao.com)是阿里巴巴集团“云”+“端”的核心战略是阿里巴巴集团无线开放平台,基于世界级的后端服务和成熟的商业组件,通过“技术、商业及大数据”的开放,为移动创业者提供可快速搭建App、商业化APP并提升用户体验的解决方案;同时提供多元化的创业服务-物理空间、孵化运营、创业投资等,为移动创业者提供全面保障。