布隆过滤器的demo

Posted on   FLGB  阅读(635)  评论(0编辑  收藏  举报
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
 * 缓存击穿
 * @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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@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。

编辑推荐:
· .NET 9 new features-C#13新的锁类型和语义
· Linux系统下SQL Server数据库镜像配置全流程详解
· 现代计算机视觉入门之:什么是视频
· 你所不知道的 C/C++ 宏知识
· 聊一聊 操作系统蓝屏 c0000102 的故障分析
阅读排行:
· 不到万不得已,千万不要去外包
· C# WebAPI 插件热插拔(持续更新中)
· .NET 9 new features-C#13新的锁类型和语义
· 会议真的有必要吗?我们产品开发9年了,但从来没开过会
· 《SpringBoot》EasyExcel实现百万数据的导入导出

随笔 - 205, 文章 - 1, 评论 - 7, 阅读 - 14万

Copyright © 2025 FLGB
Powered by .NET 9.0 on Kubernetes

点击右上角即可分享
微信分享提示