问题场景:

生产环境下,在进行业务过程中,以相应业务的流水作为唯一索引,进数据库进行更新操作。在高并发情况下会产生死锁。

数据库为Mysql数据库,隔离级别RR,避免幻读,自带间隙锁,并发场景

进行更新语句的时候是 update tableName set A=**,B=** where seqNo=***** AND status=** ;

此时的条件: 流水号 在数据库设计中为唯一索引,status为普通索引

由于数据库是RR隔离级别,在进行更新操作的时候,会根据唯一索引去找到这条数据,这种情况下,此时其实用了间隙锁,也就意味着相邻的数据会被暂时锁住,然后根据唯一索引去寻找此次更新数据的主键,然后进行更新。

在并发情况下,如果有两个事务,就会存在A B事务互相持有锁资源,都在等待对方释放的情况,产生了死锁问题,同时,MySQL会选择相对小的事务(undo较少的)进行回滚。

如何尽量避免死锁问题:

1.不同应用访问同一组表的时候,尽量约定以相同顺序访问各表,比如:A B事务 都是 先访问a表再访问b表,事务A先拿到a表的锁,事务B再去拿a的锁,如果锁冲突,那就回等待A事务释放锁,自然事务B不会拿到b表的锁,就不会堵塞事务A拿到b的锁。

2.再主键等值更新的时候,尽量先查询看数据库中有没有满足条件的数据,如果不存在就不用更新,存在才更新。如果去更新一条不存在的数据,一样会产生间隙锁。

例如:数据库只有 主键为1和5的数据,但是要更新一个主键为3的数据,但是数据库不存在,就会产生一个 (1,5)的间隙锁,但其实这个间隙锁是多余的

3.尽量使用主键更新数据,因为主键是唯一索引,在等值查询能查到数据的情况下只会产生行锁,不会产生间隙锁,这样死锁概率降低。当然,如果范围查询,一样会产生间隙锁

4.避免长事务,小事务发送锁冲突的概率也小。

5.在允许幻读和不可重复读的情况下,尽量使用RC的隔离级别,避免gap lock造成的死锁,因为产生死锁经常都跟间隙锁有关,间隙锁本来也是在RR级别来解决幻读的一种措施。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐