聊聊商城项目缓存的使用
做了一个商城的项目,虽然规模不大也使用到了缓存。
就结合代码聊下如何使用缓存,避免常见的问题。问题的解释都是copy其他大佬的。
说的很详细,很清楚。之前也有过一篇详细讲的(我只是搬运工。感谢大佬的付出)
- 缓存穿透(查询不存在的数据,疯狂loadDb)
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
- 缓存击穿(同时失效,高并发疯狂load一个key的db)
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。
缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后
- 缓存雪崩(所有key,同时失效,即时高并发疯狂盘数据库)
缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
获取商品信息
@Override
public String getGoodsRedisInfo(Long spuId) {
String spuKey = MallCacheKeys.getSpuKey(spuId);
return (String) getRedisCache(spuKey, new IMallDataCache<String>() {
@Override
public String get(String key) {
return jedisCluster.get(key);
}
@Override
public String push(String key) {
List<GoodsRedisInfo> spuList = goodsSpuMapper.selectRedisInfoByPrimaryKey(spuId,null);
if (spuList.isEmpty()) {
// 预防缓存穿透,放入一个默认值
return addDefaultSpuCache(key);
}
/*selectIntegral*/
List<GoodsSku> skuList = goodsSkuMapper.selectByspuId(spuId);
if(skuList!=null){
GoodsSku goodsSku = skuList.get(0);
if (goodsSku!=null){
spuList.get(0).setIntegral(goodsSku.getIntegral());
spuList.get(0).setIntegralPrice(goodsSku.getIntegralPrice());
}
}
return addSpuCache(spuList.get(0));
}
});
}
获取缓存信息
/**
* 获取Redis缓存数据
*
* @param key KEY
* @param mallDataCache 商店数据缓存接口
* @return 缓存信息
*/
private Object getRedisCache(String key, IMallDataCache<?> mallDataCache) {
Object cache = mallDataCache.get(key);
if (cache == null) { //代表缓存值过期
//设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
String mutexKey = key + MallCacheKeys.tmpSuffix;
//设置互斥锁,防止缓存击穿
if (jedisCluster.setnx(mutexKey, "1") == 1) { //代表设置成功
jedisCluster.expire(mutexKey, preventCacheThroughTime);
cache = mallDataCache.push(key);
jedisCluster.del(mutexKey);
} else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
int i = 0;
int retry = 5;
while (i++ < retry) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
LOGGER.error("读取缓存线程意外终止!", e);
throw new MallException("等待超时,请稍后重试!");
}
cache = mallDataCache.get(key);
if (cache != null) {
return cache;
}
}
throw new MallException("等待超时,请稍后重试!");
}
}
return cache;
}