最近有个新需求:判断一个用户是否是新用户。于是想到了之前看到过的布隆过滤器。

为了方便记录,已经将所有的常量都替换过来了。

首先定义了插入量和错误率:

    /**
     * 预计插入量 默认100000000
     */
    private long expectedInsertions = 100000000L;
    /**
     * 可接受的错误率
     */
    private double fpp = 0.001F;

bit数组的长度:

    private long optimalNumOfBits(long n, double p) {
        if (p == 0) {
            p = Double.MIN_VALUE;
        }
        return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
    }

hash函数的个数:

private int numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits);

    private int optimalNumOfHashFunctions(long n, long m) {
        return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
    }

接下来就是将数据加入过滤器中了,这里选用的是redis,并且pipleline来降低redis的并发:

    public void put(String bloomName, String key) {
        long[] indexs = getIndexs(key);
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            Arrays.stream(indexs)
                    .forEach(n -> connection.setBit(bloomName.getBytes(), n, true));
            return null;
        });
    }

判断用户是否在过滤器中。(有一点不同的是,处理业务put的时候,仅仅是put老用户进过滤器中,这样只要不存在过滤器中的用户,就认为他是新用户,这个错误率会随着时间越来越准确):

    public boolean isExist(String bloomName, String key) {
        long[] indexs = getIndexs(key);
        List list = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (long index : indexs) {
                connection.getBit(bloomName.getBytes(), index);
            }
            return null;
        });
        return !list.contains(false);
    }

接下来就是获取下标的方法了:

    private long[] getIndexs(String key) {
        long hash1 = hash(key);
        long hash2 = hash1 >>> 16;
        long[] result = new long[numHashFunctions];
        for (int i = 0; i < numHashFunctions; i++) {
            long combinedHash = hash1 + i * hash2;
            if (combinedHash < 0) {
                combinedHash = ~combinedHash;
            }
            result[i] = combinedHash % numBits;
        }
        return result;
    }

总体来说还是比较简单的,了解布隆过滤器之前也是查阅了很多文档,只要静下心去了解,一切问题都会迎刃而解。

01-16 08:37
查看更多