zoukankan      html  css  js  c++  java
  • 布隆过滤器的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。

  • 相关阅读:
    Java 正则表达式的总结和一些小例子
    jquery用div模拟一个下拉列表框
    jquery点击div以外的区域触发事件
    Jackson 框架,轻易转换JSON
    Java Json API:Gson使用简单入门
    linux必学
    Python2 long() 函数
    Python int() 函数
    PyCharm快捷键大全
    PyCharm常用设置
  • 原文地址:https://www.cnblogs.com/flgb/p/10857141.html
Copyright © 2011-2022 走看看