面试必备:在海量URL中快速判断某个URL是否存在?


布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。布隆过滤器有宁可错杀一百,也不能放过一个的性质。讲人话就是属于黑名单的 url 一定能够正确判断它在黑名单中,但不属于黑名单中的 url 也可能会被认为在黑名单中,存在一定的失误率。
至于失误率要保持在多少,数组长度,哈希函数的个数分别要设置多少就需要根据实际情况来选择了
在100亿URL中快速判断某个URL是否存在,可以采用**布隆过滤器(Bloom Filter)**这一高效的概率型数据结构。以下是详细解决方案:


1. 布隆过滤器的核心原理

  • 位数组 + 多个哈希函数:使用一个长度为m的二进制位数组和k个独立的哈希函数。
  • 插入操作:将URL通过k个哈希函数映射到位数组的k个位置,并将这些位置置为1。
  • 查询操作:检查URL对应的k个位是否全为1。若全为1,则可能存在(有一定误判率);否则一定不存在。

2. 空间与误判率计算

  • 参数选择公式
    • 位数组大小 m 和哈希函数数量 k 根据元素数量 n 和可接受的误判率 p 确定:
      [
m = -\frac{n \ln p}{(\ln 2)^2}, \quad k = \frac{m}{n} \ln 2
]

    • 示例:当 n=100亿(10^10)、p=1% 时:

      • m ≈ 9.6 × 10^10 bits ≈ 11.4 GB
      • k ≈ 7 个哈希函数

3. 分布式布隆过滤器(应对超大数据)

  • 分片策略:将位数组划分为多个分片,存储在不同机器上。
  • 一致性哈希:通过哈希函数将URL分配到特定分片,查询时只需访问对应分片,降低单机压力。

4. 优化与改进

  • 选择高效哈希函数:如 MurmurHash、Fnv 等,确保计算速度快且分布均匀。
  • 压缩位数组:使用Roaring Bitmap等压缩技术减少内存占用。
  • 结合持久化存储:布隆过滤器返回“可能存在”时,可进一步查询数据库或磁盘,降低误判影响。

5. 其他备选方案

  • Cuckoo Filter:支持删除操作,空间效率更高,但实现复杂度较高。
  • 分布式键值存储:如Redis Cluster,但存储100亿URL需要TB级内存,成本较高。

6. 实现步骤

  1. 初始化参数:根据np计算mk
  2. 部署分布式位数组:使用Redis或自定义分片服务。
  3. 插入数据:批量处理URL,通过哈希函数设置位数组。
  4. 查询流程:哈希URL后检查所有分片对应位是否为1。
    下面给你举一个小规模、易于理解的示例,从而把布隆过滤器的插入查询流程演示一遍。为了便于说明,示例中的参数会刻意设置得很小(在实际生产环境里数字会大得多、哈希函数也更复杂),但原理是一样的。

示例背景设置

  • 假设我们有一个位数组(Bit Array)长度为 m = 10 (只有10个bit)。
  • 我们准备使用 k = 3 个哈希函数(实际场景中可能更多)。

为了演示,我们先随便给这 3 个哈希函数示例化(在真实场景里,往往用如 MurmurHash、FNV 等专业哈希函数;这里仅用简单的假设函数演示):

  1. hash1(url):将 url 的 ASCII 码之和,对 10 取模。
  2. hash2(url):将 url 的长度乘以 7,再对 10 取模。
  3. hash3(url):将 url 的第一个字符和最后一个字符的 ASCII 码之和,对 10 取模。

注意:这些哈希函数只是为了举例,实际中不会这么简单!


1. 插入流程示例

假设我们要往布隆过滤器里插入一个 URL,比如 url = "google.com",来看看会发生什么。

  1. 初始化位数组

    • 初始时,位数组的 10 个 bit 全都是 0
    • 用一个示意来表示:
      bit array: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                 ↑  ↑  ↑  ↑  ↑  ↑  ↑  ↑  ↑  ↑ 
      索引      0  1  2  3  4  5  6  7  8  9
      
  2. 计算哈希值

    • 使用前面定义的 3 个哈希函数,分别计算 google.com 的哈希值:

      • hash1(“google.com”)

        • 假设把字符串 "google.com" 转成 ASCII 码后求和,为了演示,姑且算作 1089(这里不做细算);
        • 再对 10 取模: 1089 % 10 = 9
        • 所以 hash1("google.com") = 9
      • hash2(“google.com”)

        • 字符串长度为 10(含 .),再乘以 7 得到 70
        • 70 % 10 = 0
        • 所以 hash2("google.com") = 0
      • hash3(“google.com”)

        • 第一个字符是 'g',ASCII 码大约是 103
        • 最后一个字符是 'm',ASCII 码大约是 109
        • 两者之和 103 + 109 = 212
        • 212 % 10 = 2
        • 所以 hash3("google.com") = 2
  3. 设置位数组

    • 根据这 3 个哈希值,去设置位数组对应位置为 1
      • bit_array[9] = 1
      • bit_array[0] = 1
      • bit_array[2] = 1
    • 此时,位数组变为:
      bit array: [1, 0, 1, 0, 0, 0, 0, 0, 0, 1]
                 ↑           ↑                 ↑ 
      索引      0  1  2  3  4  5  6  7  8  9
      

完成插入:接下来我们如果还有别的 URL,也会重复相同的流程,把相应的 bit 置 1。


2. 查询流程示例

接下来,假设我们要查询某个 URL 是否“可能已经在集合里”。
比如说我们先来查询 url = "google.com",看看结果:

  1. 计算哈希

    • 跟插入时做同样的 3 次哈希:
      • hash1("google.com") = 9
      • hash2("google.com") = 0
      • hash3("google.com") = 2
  2. 检查位数组

    • 查看位数组的 bit_array[9]bit_array[0]bit_array[2] 是否1
    • 现在这三个位置确实都是 1
    • 于是我们可以说:“google.com 在这个布隆过滤器里 可能存在。”

查询结论:由于布隆过滤器会存在误判(可能有别的字符串碰巧把相同的位置都置为了 1),所以这里只能说“可能存在”。但如果任意一个对应位置是 0,则一定不存在


再查询一个不存在的示例

假设我们要查询 url = "baidu.com"(我们事先没有插入过它),来看会发生什么:

  1. 计算哈希

    • 同样用那 3 个示例哈希函数:
      • hash1("baidu.com") → 假设结果是 5
      • hash2("baidu.com") → 假设结果是 1
      • hash3("baidu.com") → 假设结果是 7
  2. 检查位数组

    • bit_array[5]bit_array[1]bit_array[7] 是否都为 1
    • 回头看我们的位数组:[1, 0, 1, 0, 0, 0, 0, 0, 0, 1],可以发现:
      • bit_array[5] = 0
      • bit_array[1] = 0
      • bit_array[7] = 0
    • 至少 bit_array[5] 就是 0,所以根本没必要看后面了,说明必定不存在

3. 误判是怎么产生的?

在大规模场景中,位数组长度 m 虽然足够大,但是仍然可能存在不同的 URL 分别通过 k 个哈希函数映射后,落到一模一样的几个 bit 上的可能性。结果就是:

  • 当你去查询某个并没插入过的“陌生”URL 时,可能它的 k 个哈希值正好撞上了先前插入的若干 URL 设置过的那几个 bit,于是就会误判为“可能存在”。
  • 不过,只要有任意一个 bit 是 0,那么就能马上确定它一定没被插入过。

通常,布隆过滤器是通过调整 m(位数组大小)和 k(哈希函数数量)来把这种“误判率”尽量压到一个可接受的水平。


  1. 插入:把要插入的元素(URL)通过 k 个哈希函数映射到位数组的若干位置上,并把那些位置标记为 1
  2. 查询:同样通过这 k 个哈希函数查对应位置;若全为 1,则“可能存在”;若有任意 0,则“一定不存在”。
  3. 误判原理:因为不同元素可能会把同样的位置标记为 1,导致有些本没插入过的元素,也查到的都是 1

总结

布隆过滤器以约11.4GB内存实现100亿URL的快速检索(1%误判率),是空间与时间效率最优解。若需精确判断,可结合数据库二次验证。此方案适用于爬虫去重、缓存穿透防护等场景。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值