#maven依賴
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.12</version>
</dependency>
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.hmdp.entity.Shop;
import com.sun.org.apache.bcel.internal.generic.NEW;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@Slf4j
@Component
/**
* 用于解決緩存擊穿和緩存穿透的工具類
*/
public class CacheClient {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 添加數(shù)據(jù)到redis
*
* @param key 名稱
* @param value 內(nèi)容
* @param time 有效時(shí)間
* @param unit 時(shí)間單位
*/
public void set(String key, Object value, Long time, TimeUnit unit) {
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, unit);
}
/**
* 設(shè)置邏輯過(guò)期添加數(shù)據(jù)到redis
*
* @param key 名稱
* @param value 內(nèi)容
* @param time 超時(shí)時(shí)間
* @param unit 超時(shí)時(shí)間單位
*/
public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) {
//設(shè)置邏輯過(guò)期時(shí)間
RedisData redisData = new RedisData();
redisData.setData(value);
redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
//寫(xiě)入redis
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
}
/**
* 緩存穿透
* @param keyPrefix key的前綴
* @param id id
* @param type 返回類型
* @param dbFallback 數(shù)據(jù)庫(kù)查詢
* @param time 時(shí)間
* @param unit 時(shí)間單位
* @param <R> 返回值
* @param <ID> ID
* @return
*/
public <R, ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit) {
String key = keyPrefix + id;
//1.從redis查詢商品緩存
String json = stringRedisTemplate.opsForValue().get(key);
//2.判斷是否存在
if (StrUtil.isNotBlank(json)) {
//3。存在直接返回
R content = JSONUtil.toBean(json, type);
return content;
}
//判斷是否是空值(上面已經(jīng)判斷了數(shù)據(jù)存在的情況,所以這里直接使用!=null就能查出空值)
if (json != null) {
return null;
}
//4.不存在,查詢數(shù)據(jù)庫(kù)
R r = dbFallback.apply(id);
//5.數(shù)據(jù)庫(kù)不存在返回錯(cuò)誤
if (r == null) {
//為了防止緩存穿透,將空值寫(xiě)入redis
stringRedisTemplate.opsForValue().set(key, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
//返回
return null;
}
//6。存在寫(xiě)入redis
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(r), time, unit);
//7.返回
return r;
}
/**
* 緩存擊穿
*/
private static final ExecutorService CACHE_THREAD_POOL = Executors.newFixedThreadPool(10);
public <R,ID> R queryWithLogicExpiredTime(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit) {
String key = keyPrefix + id;
//1.從redis查詢商品緩存
String json = stringRedisTemplate.opsForValue().get(key);
//2.判斷是否命中(未命中返回)
if (StrUtil.isBlank(json)) {
return null;
}
//2.1命中-判斷是否過(guò)期
RedisData redisData = JSONUtil.toBean(json, RedisData.class);
R r = JSONUtil.toBean((JSONObject) redisData.getData(), type);
LocalDateTime expireTime = redisData.getExpireTime();
if (expireTime.isAfter(LocalDateTime.now())) {
//未過(guò)期
return r;
}
String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
try {
//2.2過(guò)期-獲取互斥鎖
boolean isLock = tryLock(lockKey);
//3.判斷鎖是否拿到
if (isLock) {
//4.是-開(kāi)啟獨(dú)立線程
CACHE_THREAD_POOL.submit(() -> {
//重建緩存2步走,先查數(shù)據(jù)庫(kù),再寫(xiě)redis
//查詢數(shù)據(jù)庫(kù)
R r1 = dbFallback.apply(id);
//寫(xiě)入redis
this.setWithLogicalExpire(key,r1,time,unit);
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//4.2釋放鎖
delLock(lockKey);
}
//5.返回?cái)?shù)據(jù)
return r;
}
//利用redis中的setnx來(lái)設(shè)置鎖
private boolean tryLock(String key) {
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10L, TimeUnit.SECONDS);
//自動(dòng)拆箱有可能返回空值,所以用工具類
return BooleanUtil.isTrue(flag);
}
//刪除鎖
private void delLock(String key) {
stringRedisTemplate.delete(key);
}
}
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-615027.html
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-615027.html
到了這里,關(guān)于redis工具類(緩存的設(shè)置,緩存擊穿,穿透)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!