布隆过滤器必须前置强同步于缓存与数据库查询之前,即先执行bloomFilter.Test(key),返回false立即拦截,true才继续cache.Get()→db.query();key格式须严格对齐,实例须全局复用并启动时全量预热。

布隆过滤器必须在缓存和 DB 查询之前执行
布隆过滤器不是“锦上添花”的可选组件,而是穿透防护的第一道也是最关键的闸门。如果把它放在 cache.Get() 或 singleflight.Do() 之后,等于让恶意 key 先触发缓存 miss、再排队等 DB 查询失败——这反而把单次无效查询放大成 goroutine 阻塞风暴。
正确顺序只有一条:先 bloomFilter.Test([]byte(key)),返回 false 就立刻 return nil, errors.New("not found"),不走任何后续逻辑。
- key 必须和写入时完全一致:DB 存的是
"user:123",Test 时也得传[]byte("user:123"),不能是"123"或strconv.Itoa(123) - HTTP 参数含结构(如 JSON 字符串)时,先哈希再测:
sha256.Sum256([]byte(rawKey)).Sum(nil),避免格式差异导致漏判 - 别用
filter := bloom.New(...)写在 handler 里——每次请求新建一个空过滤器,Test永远返回false,形同虚设
空值缓存不能写 nil,必须用显式占位类型
Go 生态中不同缓存库对 nil 的序列化行为不一致:go-cache 可能存成字符串 "<nil>"</nil>,redis-go 可能存成 NULL,但读出来后 val == nil 在多数情况下为 false,导致空值缓存失效。
解决方案是定义统一空值类型并显式写入:
立即学习“go语言免费学习笔记(深入)”;
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
type Null struct{}
- 写缓存:
cache.Set(key, Null{}, 2*time.Second) - 读缓存后判断:
if _, ok := val.(Null); ok { return nil, errors.New("not found") } - TTL 要短(建议 1–5 秒),防止合法数据上线后被长期锁死
- 别依赖 JSON 的
null:用json.Marshal(UserResp{NotFound: true})替代nil,反序列化时明确区分空结果与错误
singleflight.Do 的 key 必须和缓存 key 分离
singleflight.Group.Do(cacheKey, loadFn) 是常见错误。恶意 key(如 "user:-1")会直接进入等待队列,大量并发请求卡在同一个坏 key 上,CPU 和 goroutine 全耗在无意义阻塞里。
真正该合并的,是「确认存在后」的回源加载行为。所以:
- Do 的 key 应该是业务语义 key 的哈希或脱敏形式,比如
sha256.Sum256([]byte("user:" + id)).Sum(nil),避免原始恶意 key 直接参与合并 - loadFn 内部必须包含完整副作用链:查 DB → 判空 → 写空值缓存 → 返回结果;缺一不可
- 写空值缓存时必须检查 err:
if err := cache.Set(key, Null{}, ttl); err != nil { log.Printf("failed to set null cache: %v", err) }
布隆过滤器实例必须全局复用且预热完成
布隆过滤器没有删除能力,也不支持运行时扩容。它一旦初始化,就必须靠容量预估、分片或重建来兜底。
生产环境必须做到:
- 声明为包级变量:
var bloomFilter *bloom.Bloom,在init()或服务启动时一次性bloom.New(...) - 预热必须在程序启动时完成,从 DB 或 Redis 全量加载合法 key,不能靠运行时
Add——否则冷启动期间大量穿透请求直接打穿 - 若业务有分片(如
user_id % 16),每个分片对应独立 filter 实例,但仍是全局常驻,不可按请求 new - 别存 Redis:序列化/反序列化开销大,网络延迟高,且更新困难;内存 filter 响应在纳秒级,Redis 至少多一次 round-trip
误判率不是越低越好。0.001 比 0.01 多占一倍内存,但实际业务中 1% 误判率已足够拦截绝大多数穿透流量;真正要盯紧的是过滤器饱和度——当实际 key 数接近预估上限时,误判率会陡增,必须提前分片或重建。

















