记录项目中 spring 事务与分布式锁、mq消息发送使用不当的 bug

记录项目中 spring 事务与分布式锁、mq消息发送使用不当的 bug

薛定谔的汪 Lv5

问题一

在当前项目中做了一个电子钱包功能,在操作电子钱包的业务中,要更新多张表数据,且存在并发操作钱包金额的情况,为了保证数据一致性且并发操作钱包金额都能成功,采用了事务+分布式锁的方式。

大致代码如下:

1
2
3
4
5
6
7
8
9
10
11
@Transactional
public void operateEwallet() {
// 加锁
lock.lock();
try {
// 执行操作电子钱包的业务代码 先查询后更新
...
} finally {
lock.unlock();
}
}

但是这样的代码在经过 jmeter 压测后,发现金额总是对不上。

问题分析

在上述代码中,先开启事务,再在事务中使用了 redis 分布式锁,防止并发问题,仔细分析后发现这是有问题的,在事务内开启锁,并发的请求会阻塞在事务内部,假如 A,B 两个请求同时过来,A 先开启事务获得锁,查询数据再操作完数据后,释放锁,但此时事务还没提交,B 获得锁,B 再查询的数据是 A 未提交的数据,所以数据肯定不一致。

解决方法

将开启分布式锁的代码放在事务外部,比如放在 controller 层

再次用 jmeter 多次压测,发现数据一致,问题解决。

问题二:

在事务的方法末尾发送mq, 消费者却消费不到这条数据,代码示例如下:

1
2
3
4
5
6
7
@Transactional
public void doAndSendMq() {
// insert一条数据,返回新插入数据住建
int id = service.save(..);
// 发送mq
mqTemplate.send(id);
}

产生这样的原因和上述类似,事务的提交是要执行完整个事务方法的,事务还没提交就发送消息,消费端立即消费自然找不到这条数据。

解决方法:

  1. 发送延迟消息,如延迟3秒后消费
  2. 将发送消息的代码抽到事务方法外面,等整个事务提交后再发送mq
  • Title: 记录项目中 spring 事务与分布式锁、mq消息发送使用不当的 bug
  • Author: 薛定谔的汪
  • Created at : 2019-11-11 14:48:33
  • Updated at : 2026-03-18 15:58:26
  • Link: https://www.zhengyk.cn/2019/11/11/project/tx-distributedLock/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
记录项目中 spring 事务与分布式锁、mq消息发送使用不当的 bug