mysql乐观锁重试_乐观锁加重试,并发更新数据库一条记录导致:Lock wait timeout exceeded...

这篇博客探讨了在MySQL数据库中使用乐观锁进行并发更新时遇到的Lock wait timeout exceeded异常。通过分析并发场景,解释了由于Repeatable Read事务隔离级别的原因导致线程无法读取最新版本号的问题。解决方案是将事务隔离级别改为READ_COMMITTED,从而减少重试次数和锁等待超时。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景:

mysql数据库,用户余额表有一个version(版本号)字段,作为乐观锁。

更新方法有事务控制:

@Transactional(rollbackFor = Exception.class)

更新时,比对版本号,如果版本号不一致,则更新失败。

有重试机制,如果更新失败,则查询最新版本号,再次更新,重试超过5次,报错退出。

更新的核心方法:

public boolean updateUserAccount(Long userId, int amount) {

boolean retryable;

int attemptNumber = 0;

do {

// 查询最新版本号

UserAccount userAccount = accountMapper.selectByPrimaryKey(userId);

long oldVersion = userAccount.getVersion();

// 更新

boolean success = accountMapper.updateBalance(amount, new Date(), userId, oldVersion) > 0;

if (success) {

return true;

} else {

attemptNumber++;

retryable = attemptNumber < 5;

if (attemptNumber == 5) {

log.error("超过最大重试次数");

break;

}

try {

Thread.sleep(300);

} catch (InterruptedException e) {

log.error(e);

}

}

} while (retryable);

return false;

}

更新语句:

UPDATE user_account

SET

balance = balance - #{amount,jdbcType=INTEGER},

update_time = #{updateTime,jdbcType=TIMESTAMP},

version = #{version,jdbcType=BIGINT} + 1

WHERE balance > #{amount,jdbcType=INTEGER}

AND user_id = #{userId,jdbcType=BIGINT}

AND version = #{version,jdbcType=BIGINT};

在并发更新时,报异常:Lock wait timeout exceeded

分析:

根据日志分析出:

线程a、b几乎同时到达

线程a查询版本号:856

线程a更新数据库:成功

数据库当前版本号:857

线程b查询到的版本号:856(实际已不是最新)

线程b更新数据库:失败

线程b重试,查询版本号:856

线程b更新数据库:失败

。。。

线程b超过重试次数,退出

线程b重试的过程中,又有其他线程到来,比如c,d,e

线程c查询版本号:857

线程c更新数据库:阻塞,因为b拿到锁一直在重试

线程d查询版本号:857

线程d更新数据库:阻塞,因为b拿到锁一直在重试

线程b超次数退出后,c,d,e争抢锁

d拿到锁,更新数据库:成功

数据库当前版本号:858

线程c查询到的版本号:857(实际已不是最新)

线程c更新数据库:失败

线程c重试,查询版本号:857

线程c更新数据库:失败

。。。

线程c超过重试次数,退出

重试次数过多,事务执行时间超过mysql默认的锁等待时间(50s),就会报出:Lock wait timeout exceeded

为什么线程读不到最新的版本号呢?原来是用到了事务,且mysql默认事务隔离级别Repeatable Read,把隔离级别改为READ_COMMITTED,问题解决:

@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)

分析了这么多,解决问题其实只需要一行代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值