go海量数据过滤需分层选方案:百万级用map[string]struct{},千万级起用布隆过滤器,百亿级用哈希分治;选错易致oom。

Go 里处理海量数据过滤,不能靠嵌套循环或无节制开 goroutine,得按数据规模分层选方案:百万级用 map[string]struct{},千万级起必须上布隆过滤器(BF.ADD/BF.EXISTS),百亿级得哈希分治。选错方案,轻则内存爆掉,重则服务 OOM。
map[string]struct{} 去重的硬性约束
它只适合日增百万级以下的单机内存过滤,但写法稍错就翻车:
-
seen := make(map[string]struct{})必须显式声明类型,用map[string]bool会多占几 MB 内存(每个bool占 1 字节) - 判断存在性必须用
_, ok := seen[s],不是seen[s] == true—— 未存在的 key 访问返回零值,直接比较会误判 - 结构体不能直接当 key:含
[]int、map[string]int或func()的结构体编译报错invalid map key type;要按字段构造 key,比如strconv.Itoa(u.ID) + "|" + u.Status,别用fmt.Sprintf("%v", u) - 高并发写入必须加锁:
sync.RWMutex包裹普通 map,或用sync.Map(注意它不支持遍历)
Redis 布隆过滤器 BF.ADD/BF.EXISTS 调用失败的根因
所有 ERR unknown command `bf.add` 错误都跟 Go 代码无关,只说明 Redis 没加载 redisbloom 模块:
- 验证方式:进 Redis CLI 执行
MODULE LIST,看输出里有没有name:bf;Go 中用rdb.Do(ctx, "MODULE", "LIST").Val()拿结果遍历找"bf" - Docker 最省事:直接跑
docker run -p 6379:6379 redislabs/redismod(预装 RedisBloom v2.6+) -
BF.ADD和BF.EXISTS只能通过Do()调用,参数必须是[]interface{},例如rdb.Do(ctx, "BF.ADD", key, item) -
BF.EXISTS在 key 不存在时返回redis.Nil,直接调.Bool()会 panic,必须先.Result()再断言
为什么 BF.RESERVE 不能跳过且必须防并发
BF.ADD 自动建 key 是陷阱:默认容量(100)和误判率(0.01)完全不可控,超出后误判率飙升,生产环境必须显式控制精度:
-
BF.RESERVE参数顺序是key、error_rate、capacity,例如rdb.Do(ctx, "BF.RESERVE", key, 0.001, 1000000) - key 已存在时命令报错:
ERR not enough memory or key already exists - 多个服务实例冷启动可能同时执行
BF.RESERVE,需用SETNX+ TTL 做幂等保护,或加分布式锁
百亿级数据过滤只能靠哈希分治
100GB 文件塞不进 4GB 内存,排序+双指针也不现实,唯一可行路径是哈希取模分桶:
- 遍历原始数据,对每条记录算
hash(line) % N(N=100),写入file_0到file_99 - 相同内容必然进同一小文件(哈希确定性),不同内容可能碰撞,但不影响最终去重正确性
- 逐个读小文件进内存,用
map[string]struct{}去重;若某小文件仍超内存(如file_0达 15GB),说明数据倾斜严重,对该文件换另一个哈希函数递归拆分
布隆过滤器不支持删除,哈希分治无法避免磁盘 IO —— 这些不是缺陷,是海量数据过滤的物理边界,绕不开。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











