mysql死锁产生与解决
原因:两个或两个以上的事务在执行过程中,因争夺锁资源而造成的互相等待的现象
产生条件:
- 互斥:并大执行的事务为了进行必要的隔离保证执行的正确,在事务结束前,需要对修改的数据库记录持锁,保证多个事务对相同数据库记录串行修改。
- 请求与保持:已持有一个锁资源,等待另一个锁资源.死锁仅发生在请求两个或两个以上的锁对象
- 不剥夺:已经获得锁资源的事务,在未执行前,不能被强制剥夺,只能使用完是,由事务自己释放
- 环路等待:发生死锁时,必然存在一个事务与锁的环形链
mysql中的锁
- 排他锁:又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。
- 共享锁:又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
加锁方式
-
内部加锁:
- 共享锁s:select * from table lock in share mode
- 排他锁x:select * from table for update
-
外部加锁:为了实现ACID特性,由数据库系统内部自动添加,加锁规则繁琐,与sql执行计划,事务隔离级别,表索引结构有关
避免死锁的方法:
① 设置锁优先级:提前设置优先级,如果运行A和B出现死锁,优先级低的回滚,优先级高的先执行,这样即可解决死锁问题。
② 以固定顺序访问:设定一个顺序,比如先A后B,或者先B后A,保证不管在什么时候都尊重这个顺序(通常是按ID大小的顺序),这样就会减少死锁发生的概率了。
③ 设置锁超时时间set lock_timeout:尝试获取锁的时候加一个锁超时时间,超过这个时间放弃对该锁请求。比如设置A的超时时间为10毫秒,B为100毫秒,A试了10毫秒以后获取不到资源,然后会自动断开,A断开了,这时B就可以获取资源了,避免了死锁。(但是这个方法不太好的地方在于,还需要对A再提交一次,而且timeout时间需要综合很多其他因素去设置)
④ 对所使用的数据全部加锁:每一个事务一次就将所有要使用到的数据全部加锁,否则就不允许执行,比如A在给B转钱的时候,会使用到A账户转账前,A相互转账后,B账户转账前,B账户转账后,所以就算是A给B转账,也要把A.B账户所有信息都一起加锁(这样B想给A转账也不行,因为被锁住了,不过这个还是傻,效率很低,可能又会带来其他死锁问题)