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

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

user_point(用戶積分):redis

id point
1 2000

point_item(積分商品):sql

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,再提交事務app

  3. 事務是何時提交完成的?   事務提交也須要花費必定的時間code

因爲先釋放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請求的事務可能未提交完成形成!get

解決方案:
暫時是將lock改寫到controller層,保證在事務提交成功後才釋放鎖!

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

相關文章
相關標籤/搜索