场景:

  用户消耗积分兑换商品。

user_point(用户积分):

id point
1 2000

point_item(积分商品):

id point num
101 200 10

传统的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);
}
}
@Service
public 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();
}
}
} }

观察以上代码思考:

  1. lock是什么时候释放的?

      调用lock.unlock()就是释放redisson-lock。

  2. 事务是什么时候提交的?

      事务的提交是在方法UserPointService#exchange()执行完成后。所以,示例代码中其实会先释放lock,再提交事务

  3. 事务是什么时候提交完成的?

      事务提交也需要花费一定的时间

由于先释放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层,保证在事务提交成功后才释放锁!

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

最新文章

  1. 为什么不能访问django自带的索引页
  2. caffe学习系列(3):数据层介绍
  3. 在PHP项目中使用Standford Moss代码查重系统
  4. WEB标准系列-HTML元素嵌套
  5. js获取光标位置例子
  6. 如何判断CPU的位数
  7. js正则验证两位小数 验证数字最简单正则表达式大全
  8. SVG 学习(一)
  9. 使用Pechkin将HTML网页转换为PDF
  10. CentOS 7 服务器配置--安装Mysql
  11. 比特币区块结构Merkle树及简单支付验证分析
  12. numpy教程:逻辑函数Logic functions
  13. CVE-2018-8120 分析
  14. Android--多线程之AsyncTask
  15. 在 CentOS7 上安装 Zookeeper服务
  16. 小程序返回顶部top滚动
  17. 使用GatewayWorker 开发个即时聊天demo
  18. Android中<uses-sdk>属性和target属性分析
  19. jsp servlet基础复习 Part2--GET,Post请求
  20. Oracle分区表分批迁移

热门文章

  1. Python3-ORM-Sqlalchemy
  2. 关于Windows Server 服务器 安装tomcat部署Java Web 项目的问题
  3. 毕业论文系列之基于WiFi的智能农业大棚管控系统设计代码
  4. 《快乐编程大本营》java语言训练班-第4课:java流程控制
  5. 数百个 HT 工业互联网 2D 3D 可视化应用案例分享 - 2019 篇
  6. 解决Eclipse无法安装STS
  7. qt客户端程序使用svg图片资源的几种方法
  8. CERC2017 H Hidden Hierarchy(树+模拟)
  9. SpringBoot使用ELK日志收集ELASTIC (ELK) STACK
  10. 【WPF学习】第四十四章 图画