缓存击穿

什么是缓存击穿?数据库里面数据存在,缓存数据因某种原因不存在,导致大量请求到数据库获取数据现象。这种现象有可能会导致数据库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);
            }
        }
作者:Jeebiz  创建时间:2020-05-29 09:35
 更新时间:2024-10-26 16:26