分佈式緩存更新應用(redis擊穿問題)

緩存擊穿

什麼是緩存擊穿?數據庫裏面數據存在,緩存數據因某種原因不存在,導致大量請求到數據庫獲取數據現象。這種現象有可能會導致數據庫connections數耗盡,嚴重會導致數據庫服務停止。

分佈式鎖解決方案

防止請求穿透到數據庫,可以使用分佈式鎖方式實現,例如查詢商品數據;

 public Product getProductById(Long productId){
        log.debug("查詢商品信息id:{}", productId);
        //1、從本地緩存獲取
        Product product = ehcache.get(Constants.CACHE_PRODUCT_PREFIX + productId, Product.class);
        if (product != null){
            return product;
        }
        //2、從redis緩存獲取
        product = redis.get(Constants.CACHE_PRODUCT_PREFIX + productId, Product.class);
        if (product != null){
            return product;
        }
        //3、從db獲取,防止【緩存擊穿】
        String uuid = UUID.randomUUID().toString();
        boolean lockFlag = false;
        try {
            //獲取分佈式鎖
            lockFlag = redis.lock(Constants.LOCK_PRODUCT_PREFIX + productId, uuid);
            if (lockFlag) {
                //再次查詢緩存,確保下次進入線程從緩存中獲取到
                product = redis.get(Constants.CACHE_PRODUCT_PREFIX + productId, Product.class);
                if (product != null) {
                    return product;
                }
                product = productService.findByProductId(productId);
                if (product != null) {
                    redis.setex(Constants.CACHE_PRODUCT_PREFIX + productId, EXPIRE_TIME, product);
                }
            }
        }catch (Exception e){
            log.error("分佈式加鎖異常", e);
            return null;  //具體返回編碼時候確認
        }finally {
            if (lockFlag){
                redis.unlock(Constants.LOCK_PRODUCT_PREFIX + productId, uuid);
            }
        }
        return product;
    }

分佈式鎖方案優化

分佈式會存在一個問題,未能獲取到分佈式鎖的請求,會返回null空數據,可以對返回null數據進行封裝,也可能採用while休眠方式等待從緩存redis中獲取數據,下面以休眠方式等待爲例,只對try部分代碼進行重構。

 try {
            //獲取分佈式鎖
            lockFlag = redis.lock(Constants.LOCK_PRODUCT_PREFIX + productId, uuid);
            if (lockFlag) {
                //再次查詢緩存,確保下次進入線程從緩存中獲取到
                product = redis.get(Constants.CACHE_PRODUCT_PREFIX + productId, Product.class);
                if (product != null) {
                    return product;
                }
                product = productService.findByProductId(productId);
                if (product != null) {
                    redis.setex(Constants.CACHE_PRODUCT_PREFIX + productId, EXPIRE_TIME, product);
                }
            }else{
			 long endTime = 0L;
             long waitTime = 0L;
             while (true) {
                // 一般情況下,面向用戶的讀請求控制在200ms
                if (waitTime > 200) {
                    break;
                }
                // 嘗試再去redis中讀取商品
                product =  redis.get(Constants.CACHE_PRODUCT_PREFIX + productId, Product.class);
                if (product  != null) {
                    return product ;
                } else {
                    // 如果沒有讀取到結果,那麼等待一段時間
                    Thread.sleep(20);
                    endTime = System.currentTimeMillis();
                    waitTime = endTime - startTime;
                }
            }
        }catch (Exception e){
            log.error("分佈式加鎖異常", e);
            return null;  //具體返回編碼時候確認
        }finally {
            if (lockFlag){
                redis.unlock(Constants.LOCK_PRODUCT_PREFIX + productId, uuid);
            }
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章