原文:https://blog.csdn.net/ocean_fan/article/details/78350431
一、什么是缓存击穿
查询一个在缓存内必然不存在的数据,导致每次请求都要去存储层去查询,这样缓存就失去了意义。如果在大流量下数据库可能挂掉。缓存击穿是黑客攻击系统的常用手段。
二、怎么解决缓存击穿问题?
采用布隆过滤器来实现。
什么是布隆过滤器?
它是一种空间效率极高的概率型算法和数据结构,用于判断一个元素是否在集合中(类似Hashset)。它的核心是一个很长的二进制向量和一系列的hash函数。
java中如何使用布隆过滤器?
使用谷歌的guava实现布隆过滤器。
bloom Filter布隆过滤器优劣势?
优势:
1)全量存储但不存储元素本身,在某些保密要求非常严格的场合有优势
2)空间效率高
3)插入/查询时间都是常数,远远超过一般算法
劣势:
1)存在误算率,随着存入的元素数量增加,误算率也随着增加
2)一般情况下不能从布隆过滤器删除元素
3)数组长度以及hash函数个数确定过程复杂
布隆过滤器的使用场景?
1)垃圾邮件地址过滤(地址数量很庞大)
2)爬虫URL地址去重
-
解决缓存击穿问题
4)浏览器安全浏览网址提醒
5)google 分布式数据库Bigtable以及Hbase使用布隆过滤器来查找不存在的行或列,以减少磁盘查找的IO次数
6)文档存储检索系统也可以采用布隆过滤器来检测先前存储的数据
三、java中使用guava中的bloom Filter解决缓存击穿问题
工程目录结构图:
@PostConstruct和@PreDestroy
从Java EE 5规范开始,Servlet中增加了两个影响Servlet生命周期的注解(Annotion);@PostConstruct和@PreDestroy。这两个注解被用来修饰一个非静态的void()方法 。写法有如下两种方式:
@PostConstruct
Public void someMethod() {}
或者
public @PostConstruct void someMethod(){}
被@PostConstruct修饰的方法会在服务器加载Servle的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。PreDestroy()方法在destroy()方法执行执行之后执行
模拟100个线程同时查询缓存中必不存在的情况。
package com.ocean.cache;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import com.ocean.dao.UserDao;
import com.ocean.dto.UserDto;
/**
* 缓存击穿
* @author
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:config/spring/spring-dao.xml",
"classpath:config/spring/spring-bean.xml",
"classpath:config/spring/spring-redis.xml"})
public class CacheBreakDownTest {
private static final Logger logger = LoggerFactory.getLogger(CacheBreakDownTest.class);
private static final int THREAD_NUM = 100;//线程数量
@Resource
private UserDao UserDao;
@Resource
private RedisTemplate redisTemplate;
private int count = 0;
//初始化一个计数器
private CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
private BloomFilter bf;
List allUsers;
@PostConstruct
public void init(){
//将数据从数据库导入到本地
allUsers = UserDao.getAllUser();
if(allUsers == null || allUsers.size()==0){
return;
}
//创建布隆过滤器(默认3%误差)
bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), allUsers.size());
//将数据存入布隆过滤器
for(UserDto userDto : allUsers){
bf.put(userDto.getUserName());
}
}
@Test
public void cacheBreakDownTest(){
for(int i=0;i operation = (ValueOperations) redisTemplate.opsForValue();
synchronized (countDownLatch) {
Object cacheUser = operation.get(key);
if(cacheUser!=null){
System.out.println("return user from redis");
return;
}
//如果缓存不存在查询数据库
List user = UserDao.getUserByUserName(randomUser);
if(user == null || user.size() == 0){
return;
}
//将mysql数据库查询到的数据写入到redis中
System.out.println("write to redis");
operation.set("Key:"+user.get(0).getUserName(), user.get(0).getUserName());
}
}
}
}
maven配置文件pom.xml
4.0.0
manager
manager
war
0.0.1-SNAPSHOT
manager Maven Webapp
http://maven.apache.org
21.0
junit
junit
4.9
org.mockito
mockito-all
1.10.19
test
org.springframework
spring-webmvc
4.2.0.RELEASE
jstl
jstl
1.2
log4j
log4j
1.2.17
org.apache.logging.log4j
log4j-core
2.0
org.apache.logging.log4j
log4j-api
2.0
com.fasterxml.jackson.core
jackson-core
2.6.1
com.fasterxml.jackson.core
jackson-databind
2.6.1
com.fasterxml.jackson.core
jackson-annotations
2.6.1
org.mybatis
mybatis
3.2.8
org.mybatis
mybatis-spring
1.2.2
commons-dbcp
commons-dbcp
1.4
org.springframework
spring-jdbc
4.2.0.RELEASE
org.springframework
spring-tx
4.2.0.RELEASE
mysql
mysql-connector-java
5.1.39
commons-io
commons-io
2.2
dom4j
dom4j
1.6.1
jaxen
jaxen
1.1.6
com.google.guava
guava
${guava.version}
org.slf4j
slf4j-api
1.7.21
org.springframework
spring-test
4.2.9.RELEASE
test
redis.clients
jedis
2.6.2
org.springframework.data
spring-data-redis
1.2.0.RELEASE
manager
spring-bean.xml文件
spring-dao.xml文件
spring-redis.xml文件
mybatisconfig.xml文件
user.xml文件