侧边栏壁纸
博主头像
析木之林博主等级

山不让尘,川不辞盈。

  • 累计撰写 63 篇文章
  • 累计创建 59 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

事务中使用分布式锁失效问题

析木
2024-01-17 / 0 评论 / 0 点赞 / 41 阅读 / 5562 字 / 正在检测是否收录...

项目场景

场景:监听订单中心支付完成消息,生成对应的用户订单,生成服务。加了分布式锁,但是还是有重复订单落库。


问题描述

集群模式下,采用分布式锁控制,保证只有一个服务能够得到执行。在获取到分布式锁之后,首先根据订单号查询有没有入库过,如果没有入库才能继续执行。因为要落多张表,所以在service层加了@Transactional控制事务。伪代码如下

	@Transactional(rollbackFor = Exception.class)
	public void createOrder(String orderId) {
		// 使用redission获取分布式锁,获取不到锁时进行等待
		lock.lock();
		try {
			// 获取到锁之后,先查询订单有没有入库,如果入库则忽略
			OrderDO existOrder = orderMapper.get(orderId);
			if (existOrder != null) {
				return;
			}
			// 以下落order表和orderService表
			OrderDO newOrder = new OrderDO();
			newOrder.setOrderId(orderId);
			orderMapper.insert(newOrder);
			OrderServiceDO orderService = new OrderServiceDO();
			orderServiceMapper.insert(orderService);
		} finally {
			lock.unLock();
		}
	}

原因分析:

分布式锁是在事务里面,假如有多个服务同时执行到了获取锁这一步,只会有一个服务能获取到锁,其他服务会等待锁的释放(redission是使用订阅的方式,由redis-server通知client锁的释放事件)。待方法业务逻辑执行完成之后,锁就进行了释放,但是事务还没有提交。其他服务这时获取到了锁,虽然在执行前有进行重复检查,但是因为前一个服务的事务还没有提交,这里是获取不到结果的(数据库隔离级别为可重复读),还是能正常执行下去。这就导致了重复数据入库。


解决方案:

1.分布式锁加事务的场景下,将分布式锁放在事务外面,当事务提交完成之后,才进行锁的释放。
2.数据库层面加唯一索引,防止重复的订单号入库。

0

评论区