好问题。
“10亿条订单数据去重,只能用1GB内存”,这是个典型的大数据内存受限问题。
我们来一步步推导可行方案,不同场景下策略会不同。
🎯 一、明确问题
假设:
- 订单量:10亿条(10^9)
- 每条订单 ID 假设为字符串(比如 UUID 或订单号)
- 内存限制:1GB
- 目标:去重(找出唯一的订单号)
🚫 为什么不能直接在内存中去重
假设一个订单号平均长度是 20 字节(含对象开销、hash 等大约 ×5):
10亿 × 100B ≈ 100GB 内存
——远远超过 1GB。
所以我们得用外部存储 + 分阶段处理。
✅ 二、可行方案汇总
| 方案 | 思路 | 内存占用 | 精确性 | 实现难度 |
|---|---|---|---|---|
| 外排序 + 归并去重 | 分块排序 + 外部归并 | ⭐1GB | ✅ 精确 | 中 |
| 分桶哈希(Hash Partition) | 分桶写磁盘,再单桶去重 | ⭐1GB | ✅ 精确 | 中 |
| Bloom Filter + 二次过滤 | 先用布隆过滤器过滤,再精确去重 | ⭐几十MB | ⚠️ 近似 | 简 |
| HyperLogLog | 仅计算去重数量(不是列表) | ⭐极少 | ❌ 不输出明细 | 简 |
🧩 三、推荐方案 1:外排序去重
步骤:
-
分块读取
- 例如每次读 1000 万条(根据 1GB 内存调节)
- 用内存中的哈希集合或排序结构去重
-
写临时文件
- 每块内去重后,排序写入磁盘(如
temp1.txt,temp2.txt, ...)
- 每块内去重后,排序写入磁盘(如
-
归并阶段
- 使用多路归并(类似
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 分布均匀,我们可以分成若干个桶文件。
步骤:
-
遍历所有订单,写入桶文件:
int bucketCount = 1000; // 取决于内存 int bucketId = orderId.hashCode() % bucketCount; writeTo(bucketId, orderId); -
每个桶文件内数据量大幅减少:
- 原始 10亿 / 1000 ≈ 每桶 100万
-
每次加载一个桶文件,用内存去重(HashSet 或排序)
-
写回唯一订单。
⚠️ 桶数太少会导致单桶爆内存,可调节。
优点:
- 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)
⚙️ 六、如果是离线任务(推荐组合)
最实用的工业级方案是:
布隆过滤器 + 分桶去重
流程:
- 先用布隆过滤器过滤掉大部分重复;
- 剩余数据再通过分桶或外排序精确去重;
- 内存占用 < 1GB,性能可接受。
🧾 七、总结表
| 目标 | 推荐方案 | 精度 | 内存 | 磁盘 | 优点 |
|---|---|---|---|---|---|
| 获取唯一列表 | 分桶去重 | 100% | 1GB | ✅ | 稳定可靠 |
| 快速判断是否重复 | Bloom Filter | 99.9% | <200MB | ❌ | 极快 |
| 仅统计去重数量 | HyperLogLog | 近似 | <10MB | ❌ | 极简 |
| 离线批处理 | Bloom + 外排序 | 99.999% | 1GB | ✅ | 综合最优 |