【redisson】分布式锁与数据库事务

网友投稿 935 2023-04-07

【redisson】分布式锁与数据库事务

【redisson】分布式锁与数据库事务

场景:  用户消耗积分兑换商品。

user_point(用户积分):

idpoint
12000

point_item(积分商品):

idpointnum
10120010

传统的controller、service、dao三层架构,数据库事务控制在service层(数据库MYSQL)。

@RestController@RequestMapping(value = {"point"})public class UserPointController{ @Autowired private UserPointService userPointService; @RequestMapping("/exchange") public boolean exchange(HttpServletRequest request, Long userId, Long itemId){ return userPointService.exchange(userId, itemId); }}

@Servicepublic class UserPointService { @Resource private RedissonClient redissonClient; @Transaction public boolean exchange(Long userId, Long itemId) throws Exception { RLock lock = redissonClient.getLock("lock:" + itemId); try { boolean bool = lock.tryLock(10, 30, TimeUnit.SECONDS); if (!bool){ throw new Exception("操作失败,请稍后重试"); } UserPoint user = "select * from user_point where id = :userId"; PointItem item = "select * from point_item where id = :itemId"; if(user.point - item.point > 0 && item.num > 0){ // 扣减积分 >> update user_point set point = point - :item.point where id = :userId; // 扣减库存 >> update point_item set num = num - 1 where id = :itemId; return true; } return false; } catch (Exception e) { throw e; } finally { if(lock != null && lock.isHeldByCurrentThread()){ lock.unlock(); } } }}

观察以上代码思考:

lock是什么时候释放的?  调用lock.unlock()就是释放redisson-lock。事务是什么时候提交的?  事务的提交是在方法UserPointService#exchange()执行完成后。所以,示例代码中其实会先释放lock,再提交事务。事务是什么时候提交完成的?  事务提交也需要花费一定的时间

由于先释放lock,再提交事务。并且由于mysql默认的事务隔离级别为 repetable-read,这导致的问题就是:假设现在有2个并发请求{"userId": 1, "itemId": 101},user剩余积分201。假设A请求先获得lock,此时B请求等待获取锁。A请求得到的数据信息是user_point#point=201,此时允许兑换执行扣减,返回true。在返回true前,会先释放lock,再提交事务。

释放lock后,B请求可以马上获取到锁,查询user可能得到剩余积分: 201(正确的应该是剩余积分: 1),因为A请求的事务可能未提交完成造成!

解决方案:暂时是将lock改写到controller层,保证在事务提交成功后才释放锁!

(画图苦手,时序图有缘再见)

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:分布式数据库的数据备份/恢复,这些你一定要了解
下一篇:[转] 遇见 TiDB - 分布式关系数据库
相关文章