参考自黑马程序员
缓存穿透
缓存穿透: 数据在数据库和redis中都不存在,请求直接打在数据库中。 解决方案:
方案一: 方案二: 代码案例实现:
private Shop cacheBreakDown(Long id){
String jsonShop = (String) redisTemplate.opsForValue().get(RedisConstants.SHOP_CACHE + id);
if (StrUtil.isNotBlank(jsonShop)) {
return JSONUtil.toBean(jsonShop, Shop.class);
}
if (jsonShop != null) {
return null;
}
Shop shop = this.getById(id);
if (shop == null) {
redisTemplate.opsForValue().set(RedisConstants.SHOP_CACHE + id, "", RedisConstants.SHOP_CACHE_TTL, TimeUnit.SECONDS);
return null;
}
jsonShop=JSONUtil.toJsonStr(shop);
redisTemplate.opsForValue().set(RedisConstants.SHOP_CACHE + id, jsonShop, RedisConstants.SHOP_CACHE_TTL, TimeUnit.MINUTES);
return shop;
}
缓存击穿
互斥锁时序图 代码实现:
private boolean tryLock(String key){
Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, "1", Duration.ofMillis(10000));
return BooleanUtil.isTrue(ifAbsent);
}
private void unLock(String key){
redisTemplate.delete(key);
}
private Shop cachePenetrate(long id){
String jsonShop = (String) redisTemplate.opsForValue().get(RedisConstants.SHOP_CACHE + id);
if (StrUtil.isNotBlank(jsonShop)) {
return JSONUtil.toBean(jsonShop, Shop.class);
}
if (jsonShop != null) {
return null;
}
Shop shop = null;
try {
if (!tryLock(RedisConstants.LOCK_KEY+id)){
Thread.sleep(100);
return cachePenetrate(id);
}
shop = this.getById(id);
if (shop == null) {
redisTemplate.opsForValue().set(RedisConstants.SHOP_CACHE + id, "", RedisConstants.SHOP_CACHE_TTL, TimeUnit.SECONDS);
return null;
}
jsonShop=JSONUtil.toJsonStr(shop);
redisTemplate.opsForValue().set(RedisConstants.SHOP_CACHE + id, jsonShop, RedisConstants.SHOP_CACHE_TTL, TimeUnit.MINUTES);
} catch (InterruptedException e) {
throw new RuntimeException("被打断");
} finally {
unLock(RedisConstants.LOCK_KEY+id);
}
return shop;
}
逻辑过期时序图 代码实现:
@Data
public class RedisData {
private LocalDateTime expireTime;
private Object data;
}
private ThreadPoolExecutor cacheThreadPool=new ThreadPoolExecutor(
5,10,1,TimeUnit.MINUTES,new ArrayBlockingQueue<>(10));
private Shop cacheLogicExpire(long id){
String jsonShop = (String) redisTemplate.opsForValue().get(RedisConstants.SHOP_CACHE + id);
if (StrUtil.isBlank(jsonShop)) {
return null;
}
RedisData redisData= JSONUtil.toBean(jsonShop, RedisData.class);
JSONObject jsonObject= (JSONObject) redisData.getData();
Shop shop = JSONUtil.toBean(jsonObject, Shop.class);
if (redisData.getExpireTime().isAfter(LocalDateTime.now())) {
return shop;
}
if (tryLock(RedisConstants.LOCK_KEY+id)) {
cacheThreadPool.execute(()->{
try {
saveData2Redis(id,1800);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
unLock(RedisConstants.LOCK_KEY+id);
}
});
}
return shop;
}
public void saveData2Redis(long id,long time){
Shop shop = getById(id);
RedisData redisData = new RedisData();
redisData.setData(shop);
redisData.setExpireTime(LocalDateTime.now().plusSeconds(time));
redisTemplate.opsForValue().set(RedisConstants.SHOP_CACHE,JSONUtil.toJsonStr(redisData));
}
缓存雪崩
封装工具类
方法1:
public void set(String key, Object value, long expireTime, TimeUnit unit){
redisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(value),expireTime,unit);
}
方法2:
public void setWithLogicalTime(String key, Object value, long expireTime, TimeUnit unit){
RedisData redisData = new RedisData();
long seconds = unit.toSeconds(expireTime);
redisData.setExpireTime(LocalDateTime.now().plusSeconds(seconds));
redisData.setData(value);
redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
}
方法3:
public <R,ID> R getCache(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbOpe,long expireTime,TimeUnit unit){
String key=keyPrefix+id;
String value = (String) redisTemplate.opsForValue().get(key);
if (StrUtil.isNotBlank(value)){
return JSONUtil.toBean(value,type);
}
if (value!=null){
return null;
}
R result = dbOpe.apply(id);
if (result == null) {
redisTemplate.opsForValue().set(key,"",expireTime,unit);
return null;
}
redisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(result),expireTime,unit);
return result;
}
方法4:
public <R,ID> R getCacheWithLogicExpire(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbOpe, long expireTime, TimeUnit unit){
String key=keyPrefix+id;
String value = (String) redisTemplate.opsForValue().get(key);
if (StrUtil.isBlank(value)){
return null;
}
RedisData redisData = JSONUtil.toBean(value, RedisData.class);
R result = JSONUtil.toBean((JSONObject) redisData.getData(), type);
if (LocalDateTime.now().isAfter(redisData.getExpireTime())){
return result;
}
if (tryLock(key)){
cacheThreadPool.execute(()->{
try {
R r = dbOpe.apply(id);
RedisData redis_data = new RedisData();
redis_data.setData(r);
redis_data.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(expireTime)));
redisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(redis_data));
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
unLock(RedisConstants.LOCK_KEY+id);
}
});
}
return result;
}
|