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();
        }
    }

 

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