事务中使用分布式锁问题

项目场景:

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


问题描述

集群模式下,采用分布式锁控制,保证只有一个服务能够得到执行。在获取到分布式锁之后,首先根据订单号查询有没有入库过,如果没有入库才能继续执行。因为要落多张表,所以在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.数据库层面加唯一索引,防止重复的订单号入库。

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Redis分布式锁是一种常见的并发控制机制,用于在分布式系统解决多个请求并发访问资源时的互斥问题。当多个客户端同时尝试获取同一锁时,只有一个客户端能够成功获取,其他客户端则需要等待,直到锁被释放。这通常通过在Redis设置一个过期时间的键来实现,例如使用`SETNX`命令设置一个唯一的锁标识,如果该键不存在则设置并返回true,表示获得锁。 数据库事务则是数据库操作的一种执行方式,它保证了对数据库的一组操作要么全部成功,要么全部回滚,从而保持数据的一致性。在一个事务,所有相关的操作被视为一个原子操作,这有助于避免并发修改导致的数据不一致问题。 当Redis分布式锁和数据库事务同时使用时,通常会在以下几个场景: 1. **分布式事务管理**:如果系统需要支持跨数据库的操作,可以先尝试获取Redis锁,成功后开始一个数据库事务。只有在事务内完成所有操作并提交时,才释放锁。如果事务失败(如部分操作出错),锁也会被自动释放,防止数据不一致。 2. **限流与熔断**:在高并发场景下,可能需要使用分布式锁来实现限流或熔断机制。先获取锁,如果获取成功,再检查是否超过阈值,如果在事务范围内完成这些操作后,释放锁。 3. **临时存储**:在需要临时存储数据,等待其他服务处理完成后进行持久化的情况,可以先在Redis获取锁,然后在事务进行数据写入,事务完成后,如果一切正常,释放锁。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值