1億個のオブジェクトをフィルタリングするには、1G未満のメモリフットプリントが必要です

ここに画像の説明を挿入

昔ながらのビッグセットフィルタリングの質問は、多くの大規模な工場のインタビューで尋ねられますが、実際には、実装は非常に単純で、難しさは最適化です

最適化ポイント:

  • 効率を上げる方法
  • メモリ使用量を減らす方法

この場合、最初に実装してから、徐々に最適化していきましょう。


1億個のオブジェクト、リストを直接配置して、オブジェクトが存在するかどうかを判断しますか?

しかし、1億個のオブジェクトをリストに入れるのは少し難しいです。

最初に100wを試してみませんか?

試して死ぬ

まず、オブジェクトを生成するためのいくつかのツールを準備しましょう

1.新しいオブジェクトを作成し、それを人と呼びます

public class Person {
    
    
    private Long id;
    private String name;//姓名
    private Long phone;//电话
    private BigDecimal salary;//薪水
    private String company;//公司
    private Integer ifSingle;//是否单身
    private Integer sex;//性别
    private String address;//住址
    private LocalDateTime createTime;
    private String createUser;
}

2.実際のデータをシミュレートするには、いくつかの列挙値とランダムアルゴリズムを使用する必要があります

    private static Random random = new Random();
    private static String[] names = {
    
    "黄某人", "负债程序猿", "谭sir", "郭德纲", "李狗蛋", "铁蛋", "赵铁柱", "蔡徐鸡", "蔡徐母鸡"};
    private static String[] addrs = {
    
    "二仙桥", "成华大道", "春熙路", "锦里", "宽窄巷子", "双子塔", "天府大道", "软件园", "熊猫大道", "交子大道"};
    private static String[] companys = {
    
    "京东", "腾讯", "百度", "小米", "米哈游", "网易", "字节跳动", "美团", "蚂蚁", "完美世界"};
    private static LocalDateTime now = LocalDateTime.now();

	//获取指定数量person
    private static ArrayList<Person> getPersonList(int count) {
    
    
        ArrayList<Person> persons = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
    
    
            persons.add(getPerson());
        }
        return persons;
    }

    //随机生成person
    private static Person getPerson() {
    
    
        Person person = Person.builder()
                .name(names[random.nextInt(names.length)])
                .phone(18800000000L + random.nextInt(88888888))
                .salary(new BigDecimal(random.nextInt(99999)))
                .company(companys[random.nextInt(companys.length)])
                .ifSingle(random.nextInt(2))
                .sex(random.nextInt(2))
                .address("四川省成都市" + addrs[random.nextInt(addrs.length)])
                .createTime(LocalDateTime.now())
                .createUser(names[random.nextInt(names.length)]).build();
        return person;
    }

このアルゴリズムによって生成されたオブジェクトの繰り返し率は非常に低くなります(ほとんど繰り返しはありません)

効果を見てみましょう
ここに画像の説明を挿入
が、すべてがランダムではありません。オブジェクトが後で存在するかどうかを判断する必要があるため、明確なオブジェクトが必要です。したがって、明確なオブジェクトを生成するメソッドが必要です。

    //获得一个确定的person,需传入一个date,什么作用这里先别管,后面一看就懂
    private static Person getFixedPerson(LocalDateTime date) {
    
    
        Person person = Person.builder()
                .name("薇娅")
                .phone(18966666666L)
                .salary(new BigDecimal(99999))
                .company("天猫")
                .ifSingle(0)
                .sex(0)
                .address("上海市徐家汇某栋写字楼")
                .createTime(date)
                .createUser("老马").build();
        return person;
    }

テストを開始します(最後に、良いゼリー)

ここに画像の説明を挿入

一度に100wをインストールするのは少し多すぎます。個別にインストールして一度に50wをインストールします。

    public static void main(String[] args) {
    
    
        ArrayList<Person> arrayList = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
    
    
            arrayList.addAll(getPersonList(500000));
        }
        //add一个确定的person
        arrayList.add(getFixedPerson(now));
        long start = System.currentTimeMillis();
        System.out.println(arrayList.contains(getFixedPerson(now)));
        System.out.println("arrayList内对象数量" + arrayList.size());
        long end = System.currentTimeMillis() - start;
        System.out.println("耗时(ms):" + end + ",消耗内存(m):" + (ObjectSizeCalculator.getObjectSize(arrayList) / (1024 * 1024)));
    }

ここに画像の説明を挿入

有害で、100wの効率は有害であり、メモリも耐えることができます

スーパーダブル、1000w試してみてください

//其它代码跟上面一样
for (int i = 0; i < 20; i++) {
    
    
    arrayList.addAll(getPersonList(500000));
}

ここに画像の説明を挿入

効率も悪いですが、私のいいやつであるメモリは、少し伸びた2.5Gを使用していました。たとえば、1億は25Gのメモリを持っているので、少し耐えられないのではないかと思います
。 0.0を測定

リストスキームは失敗に終わった

実際、轟音、リストを使用することは不可能ではありません。1億をインストールできない場合は、それをバッチに分割できます。江孜はメモリの問題を解決しますが、時間内に解決することはできません。それは次のように計算されます。上記の1000w0.3sまで、1億の場合は少なくとも3秒かかります。、それでも少しストレッチです


1億個のオブジェクトを保持でき、それでもメモリの使用量を減らすことができるコンテナはありますか?

ブルームフィルター:あなたは私のIDカードを読むことができます

はい、今日のトピックはBloomFilter(BloomFilter)です

主人公がやってきた

フロントはブルームフィルターを引き出すためのものです。比較すると、これがどれほど素晴らしいかがわかります。

以下はブルームフィルターの目立たないバージョンです、原理は最終的に言われています

public class MyBloomFilter {
    
    
    //后面hash函数会用到,用来生成不同的hash值,可以随便给,但别给奇数
    private final int[] ints = {
    
    6, 8, 16, 38, 58, 68};
    //统计当前对象数量
    private Integer currentBeanCount = 0;
    //你的布隆过滤器容量
    private int DEFAULT_SIZE = Integer.MAX_VALUE;
    //bit数组,用来存放结果
    private final BitSet bitSet = new BitSet(DEFAULT_SIZE);

    public MyBloomFilter() {
    
    
    }

    public MyBloomFilter(int size) {
    
    
        if (size > Integer.MAX_VALUE) throw new RuntimeException("size is too large");
        if (size <= (2 << 8)) throw new RuntimeException("size is too small");
        DEFAULT_SIZE = size;
    }
	
	//获取当前过滤器的对象数量
    public Integer getCurrentBeanCount() {
    
    
        return currentBeanCount;
    }

    //计算出key的hash值,并将对应下标置为1
    public void push(Object key) {
    
    
        Arrays.stream(ints).forEach(i -> bitSet.set(hash(key, i)));
        currentBeanCount++;
    }

    //判断key是否存在,true不一定说明key存在,但是false一定说明不存在
    public boolean contain(Object key) {
    
    
        boolean result = true;
        for (int i : ints) {
    
    
            result = result && bitSet.get(hash(key, i));
        }
        return result;
    }

    //hash算法,借鉴了hashmap的算法,利用i对同个key生成一组不同的hash值
    private int hash(Object key, int i) {
    
    
        int h;
        int index = key == null ? 0 : (DEFAULT_SIZE - 1 - i) & ((h = key.hashCode()) ^ (h >>> 16));
        return index > 0 ? index : -index;
    }
}

効果を最初に見てください

1000w

    public static void main(String[] args) {
    
    
        //实例化
        MyBloomFilter filter = new MyBloomFilter();
        for (int i = 0; i < 20; i++) {
    
    
            //push到BloomFilter
            getPersonList(500000).forEach(person -> filter.push(person));
        }
        //push一个确定的对象
        filter.push(getFixedPerson(now));
        //判断这个对象是否存在
        long start = System.currentTimeMillis();
        System.out.println(filter.contain(getFixedPerson(now)));
        long end = System.currentTimeMillis() - start;
        System.out.println("bloomFilter内对象数量:" + filter.getCurrentBeanCount());
        System.out.println("耗时(ms):" + end + ",消耗内存(m):" + (ObjectSizeCalculator.getObjectSize(filter) / (1024 * 1024)));
    }

ここに画像の説明を挿入
あっという間ですよね。メモリが256mしか使用せず、咆哮する限り、このメモリはオブジェクトの増加に伴って増加することはありません。信じないでください。試してみましょう。

1億

//其他代码全一样
for (int i = 0; i < 200; i++) {
    
    
      //push到BloomFilter
      getPersonList(500000).forEach(person -> filter.push(person));
}

ここに画像の説明を挿入
1億個でもパフォーマンスが速くガタガタすることがあり、メモリは256mのままで、良くありません

もちろん、古くから良いことはジレンマにあり、BloomFilterは強力なパフォーマンスを発揮しますが、欠点もあります。

オブジェクトが存在するかどうかと偽陽性率があり、オブジェクトの数が増えると偽陽性率が高くなりますが、
オブジェクトが存在、偽陽性率はありません。

この機能により、キャッシュの侵入を処理するためによく使用されます(キーが有効かどうかを判断するため)


見るのにうんざりして、こんにちはシルクに報酬を与える
ここに画像の説明を挿入


原理を簡単に説明する

これは、ハッシュアルゴリズムを比較して計算された添え字ですが、これは1回ではなく比較のグループであり、ハッシュ結果は1つの添え字にのみ対応することに注意してください。

同じキーに対して複数のハッシュ操作を実行し、ハッシュされた添え字を配列に入れます。配列はデフォルトですべて0であり、要素が配置された後は添え字は1です。後で、あるかどうかを判断するときに同じ数のハッシュが実行されます。要素。操作、結果に対応するすべての添え字がすべて1であるかどうかを確認します。すべてが1の場合、キーが存在する可能性があることを意味します。1がない場合は、キーが存在してはならないことを意味します。

デフォルトのビット配列:[0、0、0、0、0、0]
たとえば、キーによって計算されるハッシュ添え字のセットは
、ビット配列に対応する0、2、5です:[1、0、1、0、 0、1]
未知のキーが存在するかどうかを判断するとき、計算した添え字
が0、2、および4の対応するビット配列であると想定します。[ 1、0、1、0、1、0]
この時点で、ビット配列はに対応します添え字値は0であり、既知のキービット配列の5は添え字ビット1に対応し、2つのキーが異なっている必要があることを示します

逆に、キーで計算される添え字が[1、0、1、0、0、1]の場合、これらの位置は他のキーで計算される可能性があるため、キーが存在する可能性があるとしか言えません。


了解しました

おすすめ

転載: blog.csdn.net/qq_33709582/article/details/122046773