事物导致的死锁问题分析

先看异常日志

代码:

@Override

@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = RuntimeException.class)

public void updateAuthStatus(Long userId, Integer authType, Integer authStatus) {

    retryNum++;

    try {

        creAuthStatusDao.updateAuthStatus(userId, authType, authStatus);

        int creditNum = creAuthStatusDao.countAuthedCreditNum(userId);

 

        ....

    } catch (Exception e) {

        log.error("updateAuthStatus: exception = {}", e);

    }

}

 

在一个事物中既有update,又有select for update(意向排它锁)

数据库中,有user_id的索引和user_id与auth_type的联合索引

session 1

session 2

begin;

 
 

begin;

update cre_auth_status SET user_id='29507138388754432',auth_type=4,auth_status=2,update_time='2018-11-21 09:32:35.414' WHERE (user_id='29507138388754432' and auth_type=2)

排它锁,因为有单独索引,将锁user_id='29507138388754432'的相关数据表单锁定。

 
 

update cre_auth_status SET user_id='29507138388754432',auth_type=9,auth_status=2,update_time='2018-11-21 09:32:35.414' WHERE (user_id='29507138388754432' and auth_type=9)

排它锁pending

select id, user_id, basic_type, auth_type, auth_status, task_id, auth_time, expire_time, create_time, update_time from cre_auth_status where user_id = '29507138388754432' AND basic_type =3 AND auth_status =2 FOR UPDATE

需要等待user_id='29507138388754432'的数据释放,锁也是处于pending状态,和session2争抢锁资源,进入死锁。

 

Select时,user_id的索引需要锁住了user_id=’ 29507138388754432’的数据,等待session2中的update释放,但是因为session2中的排它锁是pending状态,未commit,锁等待中,进入死锁

select id, user_id, basic_type, auth_type, auth_status, task_id, auth_time, expire_time, create_time, update_time from cre_auth_status where user_id = '29507138388754432' AND basic_type =3 AND auth_status =2 FOR UPDATE

死锁出现异常,事物回滚

拿到锁,正常流程往下走

解决方式:

  1. 将事物放在update里,等待update结束之后,再进行select
  2. 去掉事物,事物在这里属于画蛇添足。

@Override

public void updateAuthStatus(Long userId, Integer authType, Integer authStatus, String operatorGid, int retryNum) {

    retryNum++;

try {

        creAuthStatusDao.updateAuthStatus(userId, authType, authStatus);

        int creditNum = creAuthStatusDao.countAuthedCreditNum(userId);

 

        ....

    } catch (Exception e) {

        log.error("updateAuthStatus: exception = {}", e);

    }

}

 

 

### 数据库死锁与操作系统死锁的区别 数据库死锁特指多个事务在获取资源时形成循环依赖,导致事务无限期等待其他事务释放资源的情况。而操作系统中的死锁涉及更广泛的系统资源竞争问题,不仅限于内存或磁盘空间等硬件资源的竞争,还包括进程间通信、文件句柄等多种类型的资源争用。 #### 资源类型差异 - **数据库环境下的死锁** 主要围绕着表记录级别的锁定展开,即不同事务试图同时更新同一组数据行却互不相让的情形更为常见[^1]。 - **操作系统层面的死锁** 则可能涉及到CPU时间片分配、I/O设备使用权等多个方面的问题,其范围远超出了单一应用程序内部的数据交互范畴[^2]。 ### 解决方案对比 对于两种环境中出现的死锁现象,尽管具体实现细节有所区别,但基本思路相似——预防为主,辅以必要的检测和恢复手段: #### 预防措施 - 在数据库管理中,可以通过规定统一的事物执行路径来避免潜在的环形等待链;调整合适的隔离级别也有助于缓解因频繁读写冲突引发的僵局风险[^3]。 - 对于操作系统而言,则强调合理规划任务调度算法,确保各类请求按照既定优先级有序处理,从而最大限度地规避不必要的资源争夺局面的发生。 #### 检测机制 一旦预防措施未能奏效,及时发现并妥善处置已形成的死锁至关重要: - 大多数现代关系型数据库产品内置有自动化的死锁监测工具,在探测到异常状况后会选择牺牲某个受害方(通常是持有最少资源的那个)以便打破恶性循环,并通知应用层采取补偿行动重新提交失败的操作。 - 类似地,某些高级别的实时多任务操作系统同样具备类似的自我修复能力,不过由于应用场景更加复杂多样,因此往往还需要借助额外的日志分析或者外部监控程序来进行辅助判断。 ```sql -- 这是一个简单的SQL语句用于展示如何设置事务隔离级别以防止死锁 SET TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN TRAN; UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 'A'; WAITFOR DELAY '00:00:05'; -- Simulate delay that might cause deadlock UPDATE Accounts SET Balance = Balance + 100 WHERE AccountID = 'B'; COMMIT TRAN; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值