springboot项目避免脏读影响修改数据的几种方法

文章目录
1.通过sql层面进行行锁
2.通过cas原则(compareAndSwapInt)进行自旋
3.通过synchronized锁住查询跟修改语句
4.通过分布式锁redission


1.通过sql层面进行行锁


((1)Update时,where中的过滤条件列,如果用索引,锁行,无法用索引,锁表。按照索引规则,如果能使用索引,锁行,不能使用索引,锁表。
(2)Insert时,锁行。)
1.修改之前查询数据,通过select for update加上@transaction标签,查询的时候就锁住了这条数据,直到下面一句update语句执行结束
2.如果是增加固定的数值,比如点赞每次like加1,就可以这样写:update 表 set A=A+1 where XXX 这样就可以通过行锁解决
3.比如是账户余额扣款,不想让账户超额支付,可以再where条件后面加一个大于0,如果修改行数为0就说明扣款失败

 

2.通过cas原则(compareAndSwapInt)进行自旋
首先select出一个数值A,然后计算出改变后的数值B,UPDATE music_order SETstatus= B WHERE order_id='e2524882-23' ANDstatus= A

这样可以得到
当影响行数等于0的时候,就进行重试,直到成功

 

3.通过synchronized锁住查询跟修改语句
如果是单体springboot项目可以直接用synchronized锁住查询修改的语句

   

    public ResponseVO<Object> likeComment(Integer commentId) {
        //获取账户
        String account = (String) ThreadLocalUtils.getCache("account");
        //锁越小越好
        synchronized (this) {
			//查询数据A
			//计算出B
			//update数据
            return ResponseVO.buildSuccess();
        }
    }


4.通过分布式锁redission
如果是分布式项目集群就可以用redission

@Transactional(rollbackFor = Exception.class)
    @Override
    public void payOrder(OrderDTO orderDTO) {
        UserInformationDTO userBasic = GetUserInformationUtils.getUserBasic();
        //锁住用户支付的操作(维度是用户的账户)
        String key = "pay_order_lock_" + userBasic.getAccount();
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try {
            //获取用户的财产信息
            UserPropertyPO userPropertyPO = payMapper.queryUserPropertyById(userBasic.getUserId());
            ResponseVO<List<OrderPO>> listResponseVO = feignService.queryMyOrder(orderDTO);
            List<OrderPO> result = listResponseVO.getResult();
            OrderPO orderPO = result.get(0);
            BigDecimal orderMoney = orderPO.getOrderMoney();
            BigDecimal userMoney = userPropertyPO.getMoney();
            Integer compare = orderMoney.compareTo(userMoney);
            //余额不足抛出异常
            if (compare == 1) {
                throw new MyException(ErrorCode.BALANCE_ERROR);
            }
            BigDecimal newUserMoney = userMoney.subtract(orderMoney);
            //扣款
            payMapper.changeUserMoney(userBasic.getUserId(), newUserMoney);
            //修改订单状态
            ResponseVO responseVO = feignService.changeAlreadyPay(orderDTO.getOrderId());
            if (!"success".equals(responseVO.getMessage())) {
                throw new MyException(ErrorCode.FEIGN_ERROR);
            }
            //TODO:消息队列通知库存系统
        } catch (MyException e) {
            log.error("出错{}", e.getMessage());
            if (ErrorCode.BALANCE_ERROR.toString().equals(e.getMessage())) {
                throw new MyException(ErrorCode.BALANCE_ERROR);
            } else {
                throw new MyException(ErrorCode.FEIGN_ERROR);
            }

        } catch (Exception e) {
            log.error("出错{}", e.getMessage());
            throw new MyException(ErrorCode.ERROR);
        } finally {
            lock.unlock();
        }
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章