缓存击穿
问题1:当数据没有缓存的时候,正好有很多个请求一起去访问数据。
解决方法1:
利用setnx增加分布式锁
同时只有一个线程能拿到锁,其他线程自旋等待
问题2:如果某个线程拿到锁很长时间没处理完,其他线程会一直等待。
问题3:如果设置锁的超时时间,在释放锁del的时候,可能删除的是别人所持到的锁。
解决方法,得到锁的线程只会释放自己的锁 每个线程生成一个一个随机token,释放锁的时候加以验证
缓存穿透
查询一个不存在于缓存和数据库的数据,永远不会加载缓存,会反复查询数据库,增加io压力
@Override
public SkuInfo getSkuById(String skuId) {
//redis缓存和缓存锁应该为不同的节点,这里简化为在同一个节点上
Jedis jedis = redisUtil.getJedis();
SkuInfo skuInfo = null;
//查询redis缓存
String key = "sku:" + skuId + ":info";
String val = jedis.get(key);
//此数据在数据库中也没有,直接返回空值
//避免缓存穿透,即数据库和缓存中都没有值,却不断的去查询,导致宕机
if ("empty".equals(val)) {
return skuInfo;
}
if (StringUtils.isBlank(val)) {//没有命中缓存,查询数据库
//申请缓存锁
String OK = jedis.set("sku:" + skuId + ":lock", "1", "nx", "px", 3000);
if ("OK".equals(OK)) {//拿到缓存锁
//查询数db
skuInfo = getSkuByIdFormDb(skuId);
if (skuInfo != null) {
//同步缓存
jedis.set(key, JSON.toJSONString(skuInfo));
} else {
//当其他线程再执行时,通知同伴,数据库中没有
jedis.setex("sku:" + skuId + ":info", 10, "empty");
}
//归还锁
jedis.del("sku:" + skuId + ":lock");
} else {//没有拿到缓存锁
//自旋
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
getSkuById(skuId);
}
} else {//查询的数据在缓存中
skuInfo = JSON.parseObject(val, SkuInfo.class);
}
return skuInfo;
}

京公网安备 11010502036488号