本文共 3180 字,大约阅读时间需要 10 分钟。
什么是缓存击穿?数据库里面数据存在,缓存数据因某种原因不存在,导致大量请求到数据库获取数据现象。这种现象有可能会导致数据库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); } }
转载地址:http://nccpi.baihongyu.com/