后端|一个分布式锁「失效」的案例分析

开发 前端
在日常的开发过程中,如果涉及到并发和事务,一定要多留几个心眼,考虑周全,确认以下要点是否都正确实现。

小猿最近很苦恼:明明加了分布式锁,为什么并发还是会出问题呢?

故事从接到需求开始说起。

接到需求

小猿前一阵接到一个小任务,里面有一个功能对应的场景如下:

  • 封装一个对账户余额进行加减操作的方法;
  • 所属服务部署了多个实例;
  • 这个方法可能会有并发调用。

注:实际业务场景比较复杂,已做简化。

小猿略作思考,就抓住了关键点:余额操作——要注意事务,多实例——要注意并发。

小猿的原始代码如下:

@Override
@Lock(key = "#accountNo")
@Transactional(rollbackFor = Exception.class)
public void updateBalance(String accountNo, AmountOperateParam param) {
    // do something
}

可以看到,这个方法上通过注解方式加了分布式锁和事务,锁的 key 是 accountNo,也就是账户业务主键。

自测和测试也没发现啥问题,就高高兴兴发完版回家了。

出问题了

第二天一早,就接到少量用户反馈,说自己的账户余额不对了。

小猿的第一反应是:我这块自测和测试都没问题,其它功能导致的吧?本地又是一通自测,也没有复现问题。但谨慎起见,还是往代码里加了一些日志,来确认是不是自己的方法引发的。

当又有用户反馈时,小猿根据日志的情况确认了:还真是自己方法的问题,对同一个账户的余额操作,多个并发请求会同时执行到方法体里面。

也就是说……分布式锁没锁住?

冥思苦想了好久,又在本地做了大量的测试,终于让小猿找到了问题所在:AOP 执行顺序问题。

小猿设计的时序:

但实际的时序:

也就是说期望是这样的执行顺序:

但实际的执行顺序:

分布式锁和事务,都是通过 AOP 来实现的,而 AOP 的执行顺序是根据切面的优先级来的,而小猿的分布式锁切面的优先级比事务切面的优先级低,所以就出现了上面的时序问题。

于是通过给分布式锁的切面指定 Order 的方式,让它的优先级高于事务切面(注:Order 值越小,执行优先级越高),验证完没问题后,就又高高兴兴地更新完版本,修复好历史问题数据后回家了。

还有问题

谁知道第二天一早,还是有极少量的用户反馈账户余额不对的问题。

这次小猿就有点懵了,为什么还会出现这种情况呢?

经过一番艰苦卓绝的排查,终于找到了问题所在:事务嵌套。

从前文中的示例代码中可以看到,小猿的方法上加了事务注解 @Transactional(rollbackFor = Exception.class) 里,没有指定事务的传播行为,默认是 Propagation.REQUIRED,也就是说如果当前没有事务,就新建一个事务;如果当前有事务,就加入到当前事务中。

小猿自己写的代码里没有在事务方法里嵌套调用这个方法的情况,但是同事写的代码里有,这样就会导致前文的时序问题再次发生。

找到问题就好办了,小猿将自己的方法上的事务传播行为改成了 Propagation.REQUIRES_NEW,也就是说如果当前没有事务,就新建一个事务;如果当前有事务,就将当前事务挂起,新建一个事务。

这次更新完版本后,小猿就再也没有收到用户反馈了,终于可以安心回家睡觉了。

小结

在日常的开发过程中,如果涉及到并发和事务,一定要多留几个心眼,考虑周全,确认以下要点是否都正确实现:

  • 是否做了必要的并发控制?
  • 事务的传播行为是否符合预期?
  • AOP 的执行顺序是否符合预期?
  • 对并发的场景是否做了充分的测试?
  • 对于比较关键的操作,是否打印了必要的日志?
责任编辑:赵宁宁 来源: 闷骚的程序员
相关推荐

2020-07-30 09:35:09

Redis分布式锁数据库

2022-04-14 07:56:30

公平锁Java线程

2024-02-19 00:00:00

Redis分布式

2021-11-01 12:25:56

Redis分布式

2023-03-06 08:14:48

MySQLRedis场景

2019-06-19 15:40:06

分布式锁RedisJava

2022-11-11 08:19:03

redis分布式

2022-09-22 13:28:34

Redis分布式锁

2022-09-29 08:28:57

SpringRedis分布式

2022-06-27 08:36:27

分布式事务XA规范

2022-12-18 20:07:55

Redis分布式

2013-09-11 16:02:00

Spark分布式计算系统

2017-10-24 11:28:23

Zookeeper分布式锁架构

2021-07-16 07:57:34

ZooKeeperCurator源码

2019-02-26 09:51:52

分布式锁RedisZookeeper

2023-09-04 08:45:07

分布式配置中心Zookeeper

2018-07-17 08:14:22

分布式分布式锁方位

2022-08-04 08:45:50

Redisson分布式锁工具

2018-11-27 16:17:13

分布式Tomcat

2021-11-26 06:43:19

Java分布式
点赞
收藏

51CTO技术栈公众号