开发者社区 问答 正文

MySQL RC隔离级别 单表更新导致的死锁

数据库版本:阿里云RDS 5.7 隔离级别:RC

表结构:t (
id bigint(20) NOT NULL AUTO_INCREMENT,
a datetime,
b bigint(20),
c varchar(255),
d bigint(20),
e varchar(50),
f int(11),
PRIMARY KEY (id)
);

产生死锁的业务代码(仅贴出伪代码):
@Transactional
public void update(String s) {
for (int i = 1; i < 6; i++) {
update t set ... where e = #{s} and f = #{i};
}
}

该事务在并发场景下发生死锁,求大神解释一下为什么会死锁?

本问题来自阿里云开发者社区的【11大垂直技术领域开发者社群】。 https://developer.aliyun.com/article/706511 点击链接欢迎加入感兴趣的技术领域群

展开
收起
茶什i 2019-08-30 14:31:22 707 分享 版权
阿里云 AI 助理回答

在您提供的场景中,死锁发生的原因可能涉及到事务并发执行时对相同资源的锁定顺序不一致。具体到您的代码和数据库操作,可以分析如下:

  1. 事务隔离级别为RC(Read Committed,读已提交):这意味着在一个事务中,只能看到已经提交的修改。虽然RC降低了可重复读的保证,但在这个场景下,它不是直接导致死锁的因素。

  2. 循环中的更新操作:在update(String s)方法中,您有一个循环,每次循环都会尝试更新表t中满足条件a = s的记录的列b。这里的关键在于,每次循环都是基于同一个查询条件s,并且是在一个事务中连续执行多次更新。

  3. 死锁产生的原因

    • 假设有两个线程A和B同时调用了update方法,并且分别传入了不同的参数s1s2,使得它们各自锁定并尝试更新不同的行。
    • 线程A首先执行,获取到了满足条件s1的行的锁,并开始更新。此时,线程B也执行,获取到了满足条件s2的行的锁。
    • 由于并发执行,当线程A尝试进行第二次迭代(或之后的迭代),如果此时线程B正在尝试更新之前被线程A锁定的行(即满足条件s1的行),而线程A又试图更新线程B锁定的行(即满足条件s2的行),就会形成一个相互等待的循环,即死锁。
    • 每个线程都在等待另一个线程释放锁,从而导致了死锁状态。
  4. 解决办法

    • 优化事务处理:尽量减少事务中的操作数量,避免在一个长事务中包含大量类似的更新操作。
    • 使用更细粒度的锁:如果数据库支持,可以尝试使用行级锁而非表级锁,减少锁的竞争。
    • 调整更新逻辑:避免循环内频繁地执行更新操作,或者确保循环内的更新操作按某种确定的顺序进行,以减少死锁发生的概率。
    • 设置超时机制:在数据库或应用程序层面设置事务的超时时间,一旦达到超时时间就回滚事务,可以作为一种死锁预防措施。
    • 利用数据库的死锁检测与处理机制:大多数数据库系统包括阿里云RDS都内置了死锁检测机制,当检测到死锁时会自动选择一个事务进行回滚,以解除死锁状态。

综上所述,死锁是由于并发事务按照不同顺序请求相同的锁资源,且每个事务都已经持有了对方需要的锁,而又都不释放自己的锁所造成的。通过调整事务策略、优化SQL执行逻辑等方法可以有效避免此类问题。

有帮助
无帮助
AI 助理回答生成答案可能存在不准确,仅供参考
0 条回答
写回答
取消 提交回答