布隆过滤器的demo

 
/**
 * 缓存击穿
 * @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<String> bf;
	
	List<UserDto> 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<THREAD_NUM;i++){
			new Thread(new MyThread()).start();
			//计数器减一
			countDownLatch.countDown();
		}
		try {
			Thread.currentThread().join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	class MyThread implements Runnable{
 
		@Override
		public void run() {
			try {
				//所有子线程等待,当子线程全部创建完成再一起并发执行后面的代码
				countDownLatch.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//随机产生一个字符串
			String randomUser = UUID.randomUUID().toString();
//			String randomUser = allUsers.get(new Random().nextInt(allUsers.size())).getUserName();
			String key = "Key:"+randomUser;
			
			//如果布隆过滤器中不存在这个用户直接返回,将流量挡掉
			if(!bf.mightContain(randomUser)){
				System.out.println("bloom filter don't has this user");
				return;
			}
			//查询缓存,如果缓存中存在直接返回缓存数据
			ValueOperations<String,String> operation = (ValueOperations<String, String>) redisTemplate.opsForValue();
			synchronized (countDownLatch) {
				Object cacheUser = operation.get(key);
				if(cacheUser!=null){
					System.out.println("return user from redis");
					return;
				}
				//如果缓存不存在查询数据库
				List<UserDto> 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());
			}
		}
		
	}
}

  demo2

@RunWith(SpringRunner.class)
	@SpringBootTest
	public class BloomFilterTest {
	    private BloomFilter<Integer> bloomFilter;

	    private int size = 1000000;
	    @Before
	    public void init(){
	        //不设置第三个参数时,误判率默认为0.03
	        //bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size);
	        //进行误判率的设置,自动计算需要几个hash函数。bit数组的长度与size和fpp参数有关
	        //过滤器内部会对size进行处理,保证size为2的n次幂。
	        bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.01);
	        for(int i = 0; i < size; i++){
	            bloomFilter.put(i);
	        }
	    }
	    @Test
	    public void testBloomFilter(){
	        for(int i = 0; i < size; i++){
	            if(!bloomFilter.mightContain(i)){
	                //不会打印,因为不存在的情况不会出现误判
	                System.out.println("不存在的误判" + i);
	            }
	        }

	        List<Integer> list = new ArrayList<>(1000);
	        for (int i = size + 10000; i < size + 20000; i++) {
	            if (bloomFilter.mightContain(i)) {
	                list.add(i);
	            }
	        }
	        //根据设置的误判率
	        System.out.println("存在的误判数量:" + list.size());
	    }
	}
	

  布隆过滤器有以下应用场景:

  1、黑名单,比如邮件黑名单过滤器,判端邮件地址是否在黑名单中。

  2、网络爬虫,判端url是否已经被爬取过。

  3、首次访问,判端访问网站的IP是否是第一次访问。

  4、缓存击穿,防止非法攻击,频繁发送无法命中缓存的请求,导致缓存击穿,最总引起缓存雪崩。

  5、检查英文单词是否拼写正确。

  6、K-V系统快速判断某个key是否存在,典型的例子有Hbase,Hbase的每个Region中都包含一个BloomFilter,用于在查询时快速判断某个key在该region中是否存在,如果不存在,直接返回,节省掉后续的查询。

  扩展,如何让布隆过滤器支持删除。

  进行计数删除,但是计数删除需要存储一个数值,而不是原先的 bit 位,会增大占用的内存大小。这样的话,增加一个值就是将对应索引槽上存储的值加一,删除则是减一,判断是否存在则是看值是否大于0。

原文地址:https://www.cnblogs.com/flgb/p/10857141.html