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