10 亿数据判重方案对比:布隆过滤器vs 分块哈希表

方案1:布隆过滤器(Bloom Filter)------ 允许小概率"误判"的黑名单

原理详解(技术+生活类比)

布隆过滤器是一种用位数组多个哈希函数实现的"概率型判重器"。想象你是图书馆管理员,要快速判断一本书是否在馆内,但无法存下所有10亿本书的书名。于是:

  1. 创建"记忆表格"(位数组):准备一张有10亿个格子的表格,每个格子只能填0或1。
  2. 生成"位置指纹"(哈希函数):每本书通过3个不同的"随机数生成器"(哈希函数)算出3个位置,把表格对应格子标记为1。
  3. 快速查询 :有人借书时,用同样的3个随机数生成器计算位置:
    • 若3个格子全为1 → 书可能在馆内(但可能误判,因为这3个格子可能被其他书标记过)。
    • 若有1个格子为0 → 书肯定不在馆内

技术关键点

  • 内存占用:仅需12.5GB(10亿位≈1.25亿字节≈12.5GB),比存完整数据省99%空间。
  • 误判率:可通过调整位数组大小和哈希函数数量控制,通常设为0.1%~1%。
  • 适用场景 :快速筛掉"肯定不存在"的情况,如:
    • 浏览器拦截恶意网站(先查布隆过滤器,再查真正的黑名单)。
    • 爬虫避免重复爬取URL(先问布隆过滤器"这个URL爬过吗?")。

代码实现(Java简化版)

java 复制代码
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

public class BloomFilterDemo {
    // 创建布隆过滤器:预计存10亿数据,误判率0.1%
    BloomFilter<String> filter = BloomFilter.create(
        Funnels.stringFunnel(java.nio.charset.StandardCharsets.UTF_8),
        1_000_000_000,
        0.001 // 误判率0.1%
    );

    // 添加数据
    public void add(String data) {
        filter.put(data);
    }

    // 查询数据(可能存在/肯定不存在)
    public boolean mightContain(String data) {
        return filter.mightContain(data);
    }
}

方案2:分块哈希表------ 把大数据拆成小抽屉

原理详解(技术+生活类比)

分块哈希表是一种"化整为零"的精确判重方法。继续以图书馆为例:

  1. 分区管理:把图书馆分成100个小房间(分片),每个房间最多放1000万本书。
  2. 建立索引:每个房间有一本目录(哈希表),记录该房间所有书的书名。
  3. 快速定位:借书时,通过书名拼音首字母确定去哪个房间(如"A-C"去1号房,"D-F"去2号房),再查对应目录。

技术关键点

  • 内存估算:每个房间目录约500MB(1000万条数据),100个房间共需50GB内存。
  • 分片策略 :用哈希函数(如hash % 100)确定数据归属的分片。
  • 优化技巧
    • 懒加载:只在查询某个分片时才加载其目录到内存。
    • 动态扩容:若某个分片数据过多,将其拆分为多个更小的分片。

代码实现(Java简化版)

java 复制代码
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class ShardedHashMap {
    private final int shardCount = 100; // 分100个片区
    private final List<ConcurrentHashMap<String, Boolean>> shards;

    public ShardedHashMap() {
        // 初始化100个片区的哈希表
        shards = new ArrayList<>(shardCount);
        for (int i = 0; i < shardCount; i++) {
            shards.add(new ConcurrentHashMap<>());
        }
    }

    // 计算数据该放入哪个片区
    private int getShardIndex(String key) {
        return Math.abs(key.hashCode()) % shardCount;
    }

    // 添加数据
    public void add(String key) {
        int index = getShardIndex(key);
        shards.get(index).put(key, true);
    }

    // 查询数据(精确存在/不存在)
    public boolean contains(String key) {
        int index = getShardIndex(key);
        return shards.get(index).containsKey(key);
    }
}

方案对比与选择建议

场景 选布隆过滤器 选分块哈希表
允许小概率误判 ✅ 如拦截垃圾邮件 ❌ 必须100%精确
内存限制严格(<16GB) ✅ 5GB就能存10亿数据 ❌ 至少需要32GB+内存
查询速度要求极高 ✅ 微秒级(一步到位) ❌ 需先定位分片,再查询
数据类型复杂 ✅ 支持任意类型(如URL、ID) ✅ 同样支持

一句话总结

  • 布隆过滤器:用极小内存快速判断"可能存在"或"肯定不存在",适合容忍小误差的超大规模数据判重。
  • 分块哈希表:把大数据拆成小份,每份单独管理,适合内存充足且必须100%准确的场景。

(注:实际使用时,布隆过滤器可配合精确存储(如数据库)进一步验证,分块哈希表可通过懒加载优化内存。)

相关推荐
章豪Mrrey nical7 小时前
前后端分离工作详解Detailed Explanation of Frontend-Backend Separation Work
后端·前端框架·状态模式
写写闲篇儿8 小时前
微软面试之白板做题
面试·职场和发展
派大鑫wink8 小时前
【JAVA学习日志】SpringBoot 参数配置:从基础到实战,解锁灵活配置新姿势
java·spring boot·后端
程序员爱钓鱼8 小时前
Node.js 编程实战:文件读写操作
前端·后端·node.js
xUxIAOrUIII8 小时前
【Spring Boot】控制器Controller方法
java·spring boot·后端
Dolphin_Home9 小时前
从理论到实战:图结构在仓库关联业务中的落地(小白→中级,附完整代码)
java·spring boot·后端·spring cloud·database·广度优先·图搜索算法
zfj3219 小时前
go为什么设计成源码依赖,而不是二进制依赖
开发语言·后端·golang
weixin_462446239 小时前
使用 Go 实现 SSE 流式推送 + 打字机效果(模拟 Coze Chat)
开发语言·后端·golang
JIngJaneIL9 小时前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
敲敲了个代码9 小时前
隐式类型转换:哈基米 == 猫 ? true :false
开发语言·前端·javascript·学习·面试·web