目 录CONTENT

文章目录

【实践】数据库异常分析

FatFish1
2025-06-10 / 0 评论 / 0 点赞 / 3 阅读 / 0 字 / 正在检测是否收录...

死锁案例

Ex in ******: org.springframework.dao.DeadlockLoserData*****#*#***** ### Error updating data*****#*#***** ql.SQLTransactionRollbackException: (conn=180366245) Deadlock found when trying to get lock; try restarting transaction[N]### The error may exist in file.....

查看告警是死锁导致,分析这段代码逻辑如下:

    @Transactional(rollbackFor = Exception.class)
    public void handleOneBatch(List<AccumulatorTmpFeeEntry> mainFeeTempList, AccumulatorFeeMatchKey batchKey) {
        // 业务操作
        doBusiness(...);

        // dao1的查询操作
        dao1.select(....);

        // 业务操作
        doBusiness(...);

        // 其他表的DML操作
        dao2.insert(....);

        // 业务操作
        doBusiness(...);

        // 其他表的DML操作
        dao2.update(....);

        // dao1的删除操作
        dao1.delete(....);
    }

异常点出现在dao1.delete(....);

初步推测是由于select操作加S锁,然而事务中存在大量的业务逻辑,导致单个事务耗时长,此时事务2也执行select操作加S锁,这是事务1执行dao1.delete()操作申请X锁,则申请不到,产生死锁

但是这个推测有一点站不住脚:

  • dao1.delete操作使用了主键索引,方法为range,应该可以使用行锁,只要两个事务处理的不是同一批数据,就应该不会导致锁竞争的问题,而方法上游是有redis加锁机制的

进一步分析,如果处理的不是同一批数据就不会竞争吗?

查阅资料发现,加锁的原理实际是对索引加锁。InnoDB 的行锁是通过给索引上的索引项加锁来实现的。

  • 如果使用的是主键索引,则直接对主键索引加锁;

  • 如果使用的是二级索引,先对二级索引加锁,然后再对主键索引加锁(因为要回表);

  • 不使用索引,则是全表加锁

且使用的是nextkey锁(行锁+GAP锁),即对一条数据及其一个范围的GAP加锁,可能导致不同数据出现冲突

但是再次查询数据库的隔离级别:READ COMMITTED,这个级别应该是使用MVCC限制读,不加nextkey锁,这一点依旧存疑

于是继续在网上找资料,偶然找到一篇wiki:https://blog.csdn.net/n88Lpo/article/details/127032963

其中分析的是在一些insert场景下,RC级别也可能加nextkey锁,因此考虑此场景是否有insert与delete并发的场景,由于目前没有拿到死锁的样本,计划下次发生死锁时执行show engine innodb status 捞取一些详细的加锁信息查看

0

评论区