【redisson】分布式鎖與數據庫事務


場景:
  用戶消耗積分兌換商品。

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層,保證在事務提交成功后才釋放鎖!

(畫圖苦手,時序圖有緣再見)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM