作者热门文章
- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
一个在缓存和数据库都不存在的数据,而用户不断发起请求,借此攻击数据库,造成数据库压力过大。比如请求 id < 0 的数据
解决方案:
@GetMapping("/penetrate")
public String cachePenetrate(Integer id) {
String cacheKey = "penetrate:" + id;
long cacheTime = 30L;
//缓存查询
String cacheValue = (String) redisTemplate.opsForValue().get(cacheKey);
if (cacheValue == null) {
//缓存没有,查询数据库
Product product = productService.getById(id);
if (product == null) {
//数据库没有,设置空值或默认值
cacheValue = "";
} else {
cacheValue = product.getName();
}
redisTemplate.opsForValue().set(cacheKey, cacheValue, cacheTime, TimeUnit.SECONDS);
}
return cacheValue;
}
一个key数据库存在,原来缓存有但现在过期了,发送请求发现过期了直接查询数据库并回设缓存,此时若发生高并发可能会瞬间把数据库压垮
解决方案:
@GetMapping("/puncture")
public String cachePuncture(Integer id) {
String cacheKey = "puncture:" + id;
long cacheTime = 30L;
//缓存查询
String cacheValue = (String) redisTemplate.opsForValue().get(cacheKey);
if (cacheValue == null) {
//缓存没有,使用互斥锁查询数据库更新缓存,其余阻塞排队
synchronized (cacheKey) {
//此时可能有缓存数据了
cacheValue = (String) redisTemplate.opsForValue().get(cacheKey);
if (cacheValue == null) {
//缓存还是没有,查询数据库
Product product = productService.getById(id);
cacheValue = product.getName();
//回设缓存
redisTemplate.opsForValue().set(cacheKey, cacheValue, cacheTime * 10, TimeUnit.SECONDS);
}
}
}
return cacheValue;
}
public String cachePuncture2(Integer id) throws InterruptedException {
String cacheKey = "puncture:" + id;
String blockKey = "block:" + id;
long cacheTime = 30L;
//缓存查询
String cacheValue = (String) redisTemplate.opsForValue().get(cacheKey);
if (cacheValue == null) {
//setIfAbsent == SETNX :只有key不存在的时候才能设置成功,利用它可以实现锁的效果
if (redisTemplate.opsForValue().setIfAbsent(blockKey, "1", cacheTime, TimeUnit.SECONDS)) {
//查询数据库
Product product = productService.getById(id);
cacheValue = product.getName();
//回设缓存
redisTemplate.opsForValue().set(cacheKey, cacheValue, cacheTime * 10, TimeUnit.SECONDS);
redisTemplate.delete(blockKey);
} else {
//阻塞一会,再重试获取数据
Thread.sleep(50);
return cachePuncture2(id);
}
}
return cacheValue;
}
当缓存服务器重启或者大量缓存集中在某一时间段失效,这样在失效的时候,也会给数据库带来很大压力。跟缓存击穿不一样,雪崩是大量key集体过期
解决方案:
@GetMapping("/avalanche")
public String cacheAvalanche(Integer id) {
String cacheKey = "avalanche:" + id;
long cacheTime = 30L;
//缓存查询
String cacheValue = (String) redisTemplate.opsForValue().get(cacheKey);
if (cacheValue == null) {
//缓存没有,使用互斥锁查询数据库更新缓存,其余阻塞排队
synchronized (cacheKey) {
//此时可能有缓存数据了
cacheValue = (String) redisTemplate.opsForValue().get(cacheKey);
if (cacheValue == null) {
//缓存还是没有,查询数据库
Product product = productService.getById(id);
cacheValue = product.getName();
//回设缓存
redisTemplate.opsForValue().set(cacheKey, cacheValue, cacheTime * 10, TimeUnit.SECONDS);
}
}
}
return cacheValue;
}
@GetMapping("/avalanche2")
public String cacheAvalanche2(Integer id) {
String cacheKey = "avalanche:" + id;
String signKey = "avalanche:sign" + id;
long cacheTime = 60L;
//缓存查询
String cacheValue = (String) redisTemplate.opsForValue().get(cacheKey);
//缓存标记
String signValue = (String) redisTemplate.opsForValue().get(signKey);
if (signValue == null) {
//缓存标记过期
//设置成功的去查询数据库并更新缓存,其余的返回旧的缓存值(缓存值的时间是缓存标记的2倍)
if (redisTemplate.opsForValue().setIfAbsent(signKey, "1", cacheTime, TimeUnit.SECONDS)) {
//查询数据库
Product product = productService.getById(id);
cacheValue = product.getName();
redisTemplate.opsForValue().set(cacheKey, cacheValue, cacheTime * 2, TimeUnit.SECONDS);
}
}
return cacheValue;
}
这里第一种方法,采用加锁排队有可能还要解决分布式锁的问题,线程还会被阻塞,用户体验很差
第二种方法:
缓存标记:记录缓存数据是否过期,如果过期就去更新实际key的缓存;
缓存数据:它的过期时间比缓存标记的时间延长1倍。这样,当缓存标记过期后,实际缓存还能把旧数据返回给调用端,直到新的key值更新完成后,才会返回新缓存
1、前言 Redis作为一款高性能的缓存数据库,为许多应用提供了快速的数据访问和存储能力。然而,在使用Redis时,我们不可避免地会面对一些常见的问题,如缓存雪崩、缓存穿透和缓存击穿。本文将深入
本文分享自华为云社区《【高并发】什么是缓存穿透?击穿?雪崩?如何解决?》,作者:冰 河。 说到Redis,往往更多的场景是被用作系统的缓存,说到缓存,尤其是分布式缓存系统,在实际高并发场景下,稍有不慎
1. 性能测试 - 【redis-benchmark】 //进入含有redis-benchmark执行文件的目录 // 解释:访问localhost:6379 模拟10个用户,每个用户请求10
我是一名优秀的程序员,十分优秀!