这几天突然有个组内的小伙伴为我,原有系统里边添加布隆过滤器的操作怎么搞?为什么百度了半天,相关的文章和代码很少?
结合他的问题,我看了下原有的代码结构,整理了一下.net core系统中如何简单高效的接入Redis的布隆过滤器。
1.首先,需要给Redis安装布隆过滤器的组件,这个百度一下,结果比较多,我自己试了几个基本靠谱。
2.原有代码中已经接入了StackExchange.Redis,也封装了一些基本操作。这样的话,我们只需要添加一个对布隆过滤器相关操作的基础方法拓展就行。
using StackExchange.Redis;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BloomFilterpProject
{

    public static class RedisBloomExtensions
    {
        public static async Task BloomReserveAsync(this IDatabaseAsync db, RedisKey key, double errorRate, int initialCapacity)
            => await db.ExecuteAsync("BF.RESERVE", key, errorRate, initialCapacity);

        public static async Task<bool> BloomAddAsync(this IDatabaseAsync db, RedisKey key, RedisValue value)
            => (bool)await db.ExecuteAsync("BF.ADD", key, value);

        public static async Task<bool[]> BloomAddAsync(this IDatabaseAsync db, RedisKey key, IEnumerable<RedisValue> values)
            => (bool[])await db.ExecuteAsync("BF.MADD", values.Cast<object>().Prepend(key).ToArray());

        public static async Task<bool> BloomExistsAsync(this IDatabaseAsync db, RedisKey key, RedisValue value)
            => (bool)await db.ExecuteAsync("BF.EXISTS", key, value);

        public static async Task<bool[]> BloomExistsAsync(this IDatabaseAsync db, RedisKey key, IEnumerable<RedisValue> values)
                => (bool[])await db.ExecuteAsync("BF.MEXISTS", values.Cast<object>().Prepend(key).ToArray());
        

        }

}
3.在原有封装的Redis基础操作的类中,添加对布隆过滤器操作的方法就行。
using BloomFilterpProject;
using StackExchange.Redis;
using System.Threading.Tasks;

namespace BloomFilterpProjects
{
    public class RedisHelper
    {
        private int defaultDB;
        private string defaultFolder;
        private static string defaultConn;
        private static ConnectionMultiplexer connectionMultiplexer;
        private static ConnectionMultiplexer ConnectionMultiplexer
        {
            get
            {
                if (connectionMultiplexer == null || !connectionMultiplexer.IsConnected)
                {
                    var config = ConfigurationOptions.Parse(defaultConn);
                    config.AbortOnConnectFail = false;
                    config.AllowAdmin = true;
                    config.AsyncTimeout = 5000;
                    config.ConnectTimeout = 15000;
                    config.KeepAlive = 180; 
                    connectionMultiplexer = ConnectionMultiplexer.Connect(config);
                    //Log.Information("创建Redis连接...");
                }
                return connectionMultiplexer;
            }
        }
        public RedisHelper(string conn, string folder, int dbID = 0)
        {
            defaultDB = dbID;
            defaultFolder = folder;
            defaultConn = conn;
        }
        private IDatabase GetDatabase()
        {
            connectionMultiplexer = ConnectionMultiplexer;
            return connectionMultiplexer.GetDatabase(defaultDB);
        }

        #region Bloom
        /// <summary>
        /// 布隆过滤器添加值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public async Task<bool> BloomAdd(string key,string value)
        {
           return await RedisBloomExtensions.BloomAddAsync(GetDatabase(), key, value);
        }
        /// <summary>
        /// 布隆过滤器验证值是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public async Task<bool> BloomExist(string key, string value)
        {
           return await RedisBloomExtensions.BloomExistsAsync(GetDatabase(), key, value);
        }

        /// <summary>
        /// 设置布隆过滤器key的错误率和初始容量
        /// </summary>
        /// <param name="key"></param>
        /// <param name="errorRate"></param>
        /// <param name="initialCapacity"></param>
        /// <returns></returns>
        public async Task BloomReserve(RedisKey key, double errorRate, int initialCapacity)
        {
            await RedisBloomExtensions.BloomReserveAsync(GetDatabase(),key,errorRate,initialCapacity);
        }
        #endregion

    }
}
4.这样就可以优雅的使用Redis的布隆过滤器了,无需引用任何新的插件和依赖。
 private readonly RedisHelper redis;
        public Service(RedisHelper redisHelper)
        { 
        this.redis=redisHelper;
        }
        public void Run()
        {
            redis.BloomAdd("lander","9111");

            var result = redis.BloomExist("lander", "9112");
        }

 

这里我再夹带一些私货,赘述一些自己对布隆过滤器的理解:

1.布隆过滤器创建的时候,就已经确定了能够存储的数据量和该key对应的布隆过滤器的占用的空间大小。

2.为什么会造成可能不存在但是验证的时候却返回存在?如果是单个标记为,哈希碰撞的概率比较大;如果是多个,那么每个标记为被其他的值标记的时候哈希的碰撞率也是不低的。所以我理解的,如果存储的数据越多,其实验证的时候,误报率理论上是要更高的,那么就是要在创建该过滤器之前考虑好数组位数。这里redis或者其他布隆过滤器的实现,已经考虑了这种情况,所以创建时,按照参数去设置自己想要的容错率和需要存储的数量大小就可以了

 

posted on 2022-07-07 16:18  CRUDEngineer  阅读(656)  评论(1编辑  收藏  举报