github.com/yourbasic/bloom 是默认选择,因其自动处理位数组长度对齐、最优哈希个数计算和原子位写入,避免手写常见错误;并发 add 需轻量锁,test 为 true 后必须二次校验,误判率与内存呈指数关系。

别搞插件化架构,先用对库、加好锁、兜住二次校验——这才是高性能的起点。
为什么 github.com/yourbasic/bloom 是默认选择
它把最易出错的三件事全包圆了:位数组长度自动拉齐到 2 的幂次(避免慢的%运算)、哈希函数个数按最优公式 k = round(ln(2) * m / n)自动算、底层用sync/atomic对[]byte做原子位写入(但仅限单字节内多 bit 并发安全,跨字节仍需锁)。手写或换其他库,90% 的性能退化来自哈希不一致、位越界、或Add竞态漏置。
不用自己算m和k:传bloom.New(10_000_000, 0.001)就够,库内部反解出约 1.44千万 bit(≈1.75MB)和k=3。
并发 Add 必须加锁,但锁粒度可以很轻
bloom.Filter本质是[]byte,Add会并发修改同一字节的不同 bit。Go 的 byte 读写是原子的,但bits[i] |= mask是“读-改-写”三步,中间可被抢占。压测时误判率飘到 5% 就是这个原因。
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
- 用
sync.RWMutex:写Add用Lock(),读Test用RUnlock(),别用sync.Mutex阻塞所有读 - 高频写场景可考虑
bbloom.WithPool():复用哈希中间状态,降低 GC 压力,但得接受额外依赖 - 真要无锁写入?换布谷鸟过滤器(
cuckoo filter),但 Go 生态没成熟实现,生产环境不推荐
Test 返回 true 后必须查 Redis 或 DB
这是线上事故最高发环节:filter.Test(key)返回true只表示“很可能存在”,不是“一定存在”。跳过二次校验等于主动接受误判,把新数据当重复项丢掉。
- 正确链路:
if !filter.Test(key) { goto load_from_db }→ 查 Redis → miss 则查 DB → 写入 Redis +filter.Add(key) - 风控拦截场景尤其危险:不能
Test为true就直接拒掉请求,得确认真实存储里真有这条记录 - 别在
Test为true时顺手再Add一次:布隆不负责去重,纯属浪费 CPU
误判率不是越小越好,内存会指数级涨
bloom.New(n, p)中的p每降一档(比如从0.01→0.001),位数组长度m几乎翻倍。1 亿条目下,p=1e-6内存可能从 12MB 涨到 200MB+。
初始化后别动态扩缩容:布隆过滤器没扩容机制,n超了就误判率失控,得重建 + 批量重放。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










