10亿的订单量,如何去重,使用1G内存 ?

97 阅读3分钟

好问题。
10亿条订单数据去重,只能用1GB内存”,这是个典型的大数据内存受限问题。
我们来一步步推导可行方案,不同场景下策略会不同。


🎯 一、明确问题

假设:

  • 订单量:10亿条(10^9)
  • 每条订单 ID 假设为字符串(比如 UUID 或订单号)
  • 内存限制:1GB
  • 目标:去重(找出唯一的订单号)

🚫 为什么不能直接在内存中去重

假设一个订单号平均长度是 20 字节(含对象开销、hash 等大约 ×5):

10亿 × 100B ≈ 100GB 内存

——远远超过 1GB。

所以我们得用外部存储 + 分阶段处理


✅ 二、可行方案汇总

方案思路内存占用精确性实现难度
外排序 + 归并去重分块排序 + 外部归并⭐1GB✅ 精确
分桶哈希(Hash Partition)分桶写磁盘,再单桶去重⭐1GB✅ 精确
Bloom Filter + 二次过滤先用布隆过滤器过滤,再精确去重⭐几十MB⚠️ 近似
HyperLogLog仅计算去重数量(不是列表)⭐极少❌ 不输出明细

🧩 三、推荐方案 1:外排序去重

步骤:

  1. 分块读取

    • 例如每次读 1000 万条(根据 1GB 内存调节)
    • 用内存中的哈希集合或排序结构去重
  2. 写临时文件

    • 每块内去重后,排序写入磁盘(如 temp1.txt, temp2.txt, ...)
  3. 归并阶段

    • 使用多路归并(类似 sort -u 原理)
    • 边归并边去重(只保留相邻重复的一个)

可用工具:

  • Linux 命令行:

    sort -u --buffer-size=1G --temporary-directory=/data temp*.txt -o result.txt
    
  • Java 实现:ExternalSort 库、Hadoop MapReduce、Spark sortByKey 去重

内存控制关键:

排序和归并时,设置 buffer-size 控制内存;每次操作流式处理。


🧠 四、推荐方案 2:哈希分桶 + 单桶去重

思路:

如果订单号 hash 分布均匀,我们可以分成若干个桶文件。

步骤:

  1. 遍历所有订单,写入桶文件:

    int bucketCount = 1000;  // 取决于内存
    int bucketId = orderId.hashCode() % bucketCount;
    writeTo(bucketId, orderId);
    
  2. 每个桶文件内数据量大幅减少:

    • 原始 10亿 / 1000 ≈ 每桶 100万
  3. 每次加载一个桶文件,用内存去重(HashSet 或排序)

  4. 写回唯一订单。

⚠️ 桶数太少会导致单桶爆内存,可调节。

优点:

  • IO 顺序写入,速度快
  • 精确去重
  • 内存固定(取决于单桶大小)

🌸 五、推荐方案 3:布隆过滤器(近似)

如果只是要判断某个订单是否重复,而不要求100%准确

思路:

  • 使用一个布隆过滤器(Bloom Filter)
  • 位数组大小 N = 10亿 × 10 ~ 100 bits ≈ 125MB
  • 多个哈希函数(k=7左右)
from pybloom_live import BloomFilter
bf = BloomFilter(capacity=1e9, error_rate=0.001)
for order_id in orders:
    if order_id not in bf:
        bf.add(order_id)
        # unique

优缺点:

  • 占用极少内存
  • 快速判断重复
  • 存在极少误判(false positive)

⚙️ 六、如果是离线任务(推荐组合)

最实用的工业级方案是:

布隆过滤器 + 分桶去重

流程:

  1. 先用布隆过滤器过滤掉大部分重复;
  2. 剩余数据再通过分桶或外排序精确去重;
  3. 内存占用 < 1GB,性能可接受。

🧾 七、总结表

目标推荐方案精度内存磁盘优点
获取唯一列表分桶去重100%1GB稳定可靠
快速判断是否重复Bloom Filter99.9%<200MB极快
仅统计去重数量HyperLogLog近似<10MB极简
离线批处理Bloom + 外排序99.999%1GB综合最优