一、介紹
當(dāng)一種請求,總是能越過緩存,調(diào)用數(shù)據(jù)庫,就是緩存穿透。
比如當(dāng)請求一個數(shù)據(jù)庫沒有的數(shù)據(jù),那么緩存也不會有,然后就一直請求,甚至高并發(fā)去請求,對數(shù)據(jù)庫壓力會增大。
二、方案介紹
- 如果
key
具有某種規(guī)則,那么可以對key增加校驗(yàn)機(jī)制,不符合直接返回。 -
Redisson
布隆過濾器 - 邏輯修改,當(dāng)數(shù)據(jù)庫沒有此數(shù)據(jù),以
null
為value
,也插入redis
緩存,但設(shè)置較短的過期時間。
三、Redis Docker部署
docker-compose示例如下,redis.conf從這里下載
redis:
container_name: redis
image: redis:7.2
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
ports:
- "6379:6379"
command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
四、SpringBoot3 Base代碼
1. 依賴配置
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis 連接線程池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<!-- redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.24.3</version>
</dependency>
spring:
data:
redis:
host: 192.168.101.65 # Redis服務(wù)器的主機(jī)名或IP地址
port: 6379 # Redis服務(wù)器的端口號
password: # 用于連接Redis服務(wù)器的密碼
database: 0 # 要連接的Redis數(shù)據(jù)庫的索引號
lettuce:
pool:
max-active: 20 # 連接池中最大的活躍連接數(shù)
max-idle: 10 # 連接池中最大的空閑連接數(shù)
min-idle: 0 # 連接池中最小的空閑連接數(shù)
timeout: 10000 # 連接超時時間(毫秒)
lock-watchdog-timeout: 100 # Redisson的分布式鎖的看門狗超時時間(毫秒)
2. 基本代碼
要演示的代碼很簡單,就是一個攜帶courseId
請求過來,調(diào)用下面的service
函數(shù),然后查詢數(shù)據(jù)庫。
@Override
public CoursePublish getCoursePublish(Long courseId) {
return coursePublishMapper.selectById(courseId);
}
當(dāng)我們使用redis改造時,基本代碼如下
@Override
public CoursePublish getCoursePublishCache(Long courseId) {
String key = "content:course:publish:" + courseId;
//先查詢redis
Object object = redisTemplate.opsForValue().get(key);
if (object != null){
String string = object.toString();
CoursePublish coursePublish = JSON.parseObject(string, CoursePublish.class);
return coursePublish;
}else {
//后查詢數(shù)據(jù)庫
CoursePublish coursePublish = getCoursePublish(courseId);
if (coursePublish != null){
redisTemplate.opsForValue().set(key, JSON.toJSONString(coursePublish));
}
return coursePublish;
}
}
五、緩存優(yōu)化代碼
1. 校驗(yàn)機(jī)制
我這里的id
沒規(guī)則,所以加不了,跳過。
2. 布隆過濾器
讀取yaml
配置
@Data
@Component
@ConfigurationProperties(prefix = "spring.data.redis")
public class RedisProperties {
private String host;
private int port;
private String password;
private int database;
private int lockWatchdogTimeout;
}
配置RedissonClient
@Slf4j
@Configuration
public class RedissionConfig {
@Autowired
private RedisProperties redisProperties;
@Bean
public RedissonClient redissonClient() {
RedissonClient redissonClient;
Config config = new Config();
//starter依賴進(jìn)來的redisson要以redis://開頭,其他不用
String url = "redis://"+ redisProperties.getHost() + ":" + redisProperties.getPort();
config.useSingleServer().setAddress(url)
//.setPassword(redisProperties.getPassword())
.setDatabase(redisProperties.getDatabase());
try {
redissonClient = Redisson.create(config);
return redissonClient;
} catch (Exception e) {
log.error("RedissonClient init redis url:[{}], Exception:", url, e);
return null;
}
}
}
把布隆過濾器加到service
,如下
private RBloomFilter<String> bloomFilter;
@PostConstruct
public void init(){
//初始化布隆過濾器
bloomFilter = redissonClient.getBloomFilter("bloom-filter");
bloomFilter.tryInit(100, 0.003);
List<CoursePublish> coursePublishList = coursePublishMapper.selectList(new LambdaQueryWrapper<CoursePublish>());
coursePublishList.forEach(coursePublish -> {
String key = "content:course:publish:" + coursePublish.getId();
bloomFilter.add(key);
});
}
@Override
public CoursePublish getCoursePublishCache(Long courseId) {
String key = "content:course:publish:" + courseId;
//布隆過濾器
boolean contains = bloomFilter.contains(key);
if (!contains){
return null;
}
//先查詢redis
Object object = redisTemplate.opsForValue().get(key);
if (object != null){
String string = object.toString();
CoursePublish coursePublish = JSON.parseObject(string, CoursePublish.class);
return coursePublish;
}else {
//后查詢數(shù)據(jù)庫
CoursePublish coursePublish = getCoursePublish(courseId);
if (coursePublish != null){
bloomFilter.add(key);
redisTemplate.opsForValue().set(key, JSON.toJSONString(coursePublish));
}
return coursePublish;
}
}
3. 邏輯優(yōu)化
當(dāng)數(shù)據(jù)庫沒有此數(shù)據(jù),以null
為value
,也插入redis
緩存,但設(shè)置較短的過期時間。文章來源:http://www.zghlxwxcb.cn/news/detail-834269.html
//后查詢數(shù)據(jù)庫
CoursePublish coursePublish = getCoursePublish(courseId);
if (coursePublish != null) {
bloomFilter.add(key);
redisTemplate.opsForValue().set(key, JSON.toJSONString(coursePublish));
}else {
redisTemplate.opsForValue().set(key, JSON.toJSONString(coursePublish), 10, TimeUnit.SECONDS);
}
return coursePublish;
文章來源地址http://www.zghlxwxcb.cn/news/detail-834269.html
到了這里,關(guān)于Redis之緩存穿透問題解決方案實(shí)踐SpringBoot3+Docker的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!