第四节:多种判重方案落地(基于DB、基于缓存、基于布隆过滤器)

一. 说明

1. 常见场景

 A 注册的时候手机号判重 【本节以这个为例】

 B 注册的时候昵称判重

 C 商品详情页请求的时候,利用对商品id判重 (商品的数量是海量的)

 

2 常见方案

 A 可以直接去数据中查

 B 本地缓存 或 redis中 同步一份  (海量数据的时候会占用内存空间)

 C 数据量非常大,且重复率不是很高,使用布隆过滤器 

 

 

二. 布隆过滤器复习

1 用bloomfilter过滤器防重复?一定能防止吗?

 不一定,通常使用bloom快速判重,然后需要结合DB来操作。

 A 只能证明一定不存在,只要一个值为0则证明一定不存在。

 B 3个值都是1,可能存在,因为这个1可能是其他key生成的,这个时候再去DB中查一下。

2 bloomFilter底层是什么结构?判重的原理是什么?

 底层结构:是 二进制位(bit)组成的数组【bit array】,初始全为0   和  多个独立的hash函数。

  判重的原理:先初始化一个值全为0的大二进制位数组。添加key时,通过3次hash计算并对数组的长度取模得到数据下标,将对应位置的值设为1。查找时,过程是一样的,若3个位置都为1,证明key可能存在;只要有一个位置不为1,key则一定不存在。

3 补充:

  换算关系:位/比特(bit)<字节(byte)<千字节(kb)<兆字节(mb)<gb

  1byte=8bit   1k=1024byte

4 相关指令
  ### 创建布隆过滤器(容量 1000,错误率 0.1%)
    BF.RESERVE mybloom 0.001 1000
  ### 添加单个元素(成功返回1)
    BF.ADD mybloom apple
  ### 批量操作(成功返回1)
    BF.MADD mybloom cherry date
  ### 检查单个存在性
    BF.EXISTS mybloom apple  # 返回 1(存在)
    BF.EXISTS mybloom banana # 返回 0(不存在)
  ### 批量检查存在性
    BF.MEXISTS mybloom apple cherry grape  # 返回 1,1,0
  ### 删除布隆过滤器
    DEL mybloom
解析:上述1000容量的含义,详见截图01

 

三. 实操

1 基本用法

  创建、添加、批量添加、判断是否存在、删除

    public IActionResult BasicUse()
    {
        //这里只能使用CSRedisClient类,RedisHelper类中没有集成

        //1 创建布隆过滤器(容量 1000,错误率 0.1%)
        string key = "mybloom";
        decimal errorRate = 0.001m;
        long capacity = 1000;
        bool res1 = redisClient.BfReserve(key, errorRate, capacity);

        //2 添加单个元素(成功返回1)
        bool res2 = redisClient.BfAdd(key, "apple");

        //3 批量添加
        bool[] res3 = redisClient.BfMAdd(key, ["cherry", "date"]);

        //4 检查元素是否存在
        bool res4 = redisClient.BfExists(key, "apple");
        bool res5 = redisClient.BfExists(key, "banana");

        //5. 批量检查存在性
        bool[] res6 = redisClient.BfMExists(key, ["apple", "cherry", "grape"]);


        //6. 删除布隆过滤器
        //long res7 = redisClient.Del(key);

        

2 注册场景判重手机号

 核心逻辑
   (1) bloomfilter中判断手机号是否存在,如果不存在,则一定不存在,继续后续业务
   (2) 判断存在,只能说明是可能存在的,需要DB层次精确判断一下
   (3) 全部执行完毕,则将手机号插入bloomfilter

program代码

//初始化布隆过滤器--注册判重手机号(如果不存在)
csredis.BfReserve("userPhoneList", 0.001m, 1000000);

action代码

/// <summary>
/// 02-使用手机号注册用户--手机号判重
/// </summary>
/// <param name="phoneNumber">手机号</param>
/// <param name="verificationCode">验证码</param>
/// <param name="password">密码</param>
/// <returns></returns>
[HttpPost]
public IActionResult RegisterByPhone(string phoneNumber, string verificationCode, string password)
{
    try
    {
        //1 验证码判断--假设正确


        //2 手机号判重
        string bloomPhoneKey = "userPhoneList";
        //2.1 布隆过滤器中判重
        bool phoneMayExist = redisClient.BfExists(bloomPhoneKey, phoneNumber);
        if (phoneMayExist)
        {
            //2.2 表示bloom中判断可能存在,需要DB进行精确判重
            //bool phoneExists = await _userRepository.CheckPhoneExistsAsync(phoneNumber);  //模拟
            bool phoneExists = true;
            if (phoneExists)
            {
                return Json(new { status = "error", msg = "该手机号已被注册", data = "" });
            }
        }
        //3 其它DB层面的业务--假设成功


        //4 将手机号存入布隆过滤器
        redisClient.BfAdd(bloomPhoneKey, phoneNumber);


        return Json(new { status = "error", msg = "注册成功", data = "" });
    }
    catch (Exception)
    {
        return Json(new { status = "error", msg = "注册失败", data = "" });
    }
} 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
posted @ 2025-05-08 09:37  Yaopengfei  阅读(33)  评论(1)    收藏  举报