问题
有一天客户告知,用户的功能使用有问题,然后去线上正式环境排查发现日志记录了异常:
DeadlockLoserDataAccessException
死锁日志查询
show engine innodb status \G;
具体异常错误信息为:
Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.
MySQLTransactionRollbackException:
Deadlock found when trying to get lock; try restarting transaction
简单而言就是死锁了,死锁造成的原因
排它锁(X锁)和共享锁(S锁).
所谓X锁,是事务T对数据A加上X锁时,只允许事务T读取和修改数据A
所谓S锁,是事务T对数据A加上S锁时,其他事务只能再对数据A加S锁,而不能加X锁,直到T释放A上的S锁
若事务T对数据对象A加了S锁,则T就可以对A进行读取,但不能进行更新(S锁因此又称为读锁),在T释放A上的S锁以前,其他事务可以再对A加S锁,但不能加X锁,从而可以读取A,但不能更新A.
其实,我的理解很简单,就是在有一定并发的情况下,多个线程对同一条数据进行查询与修改同时进行造成资源不足。或者是表没有索引,频繁的查询以及修改,修改没有索引的字段会造成全局锁,导致死锁。
重现问题
打开两个窗口依次执行执行
START TRANSACTION; update t_sysorderhead set ohid='1' WHERE gfsh ='123' ;
在没有索引的情况,会造成第二次更新操作等待超时
标准处理方式
- 分析sql语句 使所有sql子查询语句都走唯一索引、索引等 不要全表扫描导致产生表级别的锁
执行explain sql语句 ,把ALL类型的全表扫描的执行sql都优化成走索引执行 - 降低事务等级为 ISOLATION_READ_COMMITTED ,缩小事务 及时commit
READ_COMMITTED 不会锁表,是Oracle的默认事务等级,MySql默认使用的REPEATABLE READ ,READ UNCOMMITTED更快 更不易锁表。 - 分解service impl中的方法中作为整体事务的容易出错的代码,单独进行异常的捕获,防止其出现IP错误等导致数据库回滚
- 其他业务语句涉及到同一张表的 要使用索引 避免全表扫描的update 。