缓存穿透
1、缓存穿透是指查询一个缓存中和数据库中都不存在的数据,导致每次查询这条数据都会透过缓存,直接查库,最后返回空。
解决缓存穿透的方法一般有两种,第一种是缓存空对象,第二种是使用布隆过滤器。
缓存击穿
2、缓存击穿是指当缓存中某个热点数据过期了,在该热点数据重新载入缓存之前,有大量的查询请求穿过缓存,直接查询数据库。
解决缓存击穿的方法也有两种,第一种是设置key永不过期;第二种是使用分布式锁。
试试双重检查 来避免 缓存击穿
import cn.hutool.json.JSONUtil;
import com.jin.pagehelper.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.math.BigInteger;
import java.util.Date;
@SuppressWarnings("all")
@Service
public class RedisService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@PostConstruct
public void init() {
if (redisTemplate.opsForHash().hasKey("employee", "1")) {
redisTemplate.opsForHash().delete("employee", "1");
}
}
public void setValueTest() {
redisTemplate.opsForHash().put("employee", "1",
JSONUtil.parse(
new Employee()
.setEmployeeId(BigInteger.valueOf(1))
.setFirstName("jin")
.setLastName("jin")
.setHireDate(new Date())
)
);
System.out.println("ok");
}
public void getOps() throws InterruptedException {
Object object = redisTemplate.opsForHash().get("employee", "1");
if (object == null) {
synchronized (this) {
object = redisTemplate.opsForHash().get("employee", "1");
if (object == null) {
System.out.println("redis没有,只能从数据库找");
setValueTest();
} else {
System.out.println("并发情况下,从redis拿到了");
}
}
} else {
System.out.println("正常情况下,从redis拿到了");
}
}
}
自己模拟一个并发情况,做试验。
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
executorService.submit(() -> {
try {
redisService.getOps();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}