【商城】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);
            }
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

广漂一枚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值