缓存穿透需组合防御:先参数校验拦截非法ID,再空值缓存兜底,最后用布隆过滤器事前拦截;布隆必须与DB写操作强一致,误判率超0.02需扩容,否则防护失效。

缓存穿透:Gin接口里怎么拦住无效ID请求
直接写空值缓存最省事,但必须配合参数校验和布隆过滤器才真正防得住。只靠redis.Set写"null",遇到恶意刷/user/999999999这种随机ID,照样打穿数据库。
- 先做基础校验:
id字段必须是正整数,id 或非数字格式直接<code>c.AbortWithStatusJSON(400, ...)返回,不进缓存逻辑 - 查缓存前过布隆过滤器:
bloomFilter.Test([]byte("user:" + strconv.FormatInt(id, 10)))返回false就直接c.JSON(200, nil),不查Redis也不查DB - 空值缓存必须设短TTL:
redis.Set(ctx, key, "null", 60*time.Second),不能用30*time.Minute这种长周期,否则新增数据后会短暂不一致 - 布隆过滤器要和写库联动——用户注册成功后,必须同步调用
bloomFilter.Add([]byte("user:" + strconv.FormatInt(newID, 10))),否则漏判率飙升
缓存雪崩:Redis批量key失效时Gin怎么稳住
别信“加个随机数就万事大吉”。如果所有key都按3600 + rand.Intn(600)设TTL,上线一小时后还是可能扎堆过期——因为启动时间相同、写入时间相近的key,随机偏移量根本拉不开差距。
- 过期时间必须带业务语义:比如商品详情页缓存,按上架时间+72小时,再叠加±1800秒;用户资料按最后更新时间+24小时+±900秒
- 关键接口强制双缓存:Gin handler里先查
cache.Get("user:"+id)(Caffeine本地缓存),命中则返回;未命中再查Redis,Redis也未命中才查DB并异步回填Redis - 预热不能只靠启动脚本:在Gin的
router.GET("/healthz", ...)里加个轻量探测,发现缓存命中率就触发后台goroutine批量加载TOP 100热点key - 禁用统一TTL配置:像
redis.Set(ctx, key, val, time.Hour*2)这种硬编码必须替换成动态计算,否则运维改配置时容易漏掉某类key
Gin中间件里怎么安全实现互斥锁防击穿
缓存击穿虽不是雪崩,但在Gin高并发场景下,单个热点key过期瞬间仍可能把DB打满。用SETNX加锁比synchronized更合适,但要注意Redis命令超时和锁释放失败的风险。
- 锁key必须带过期时间:
redis.SetNX(ctx, "lock:user:"+id, "1", 3*time.Second),避免死锁;不能只用SETNX不设TTL - 查DB后必须用
redis.Set覆盖原key,而不是redis.GetSet——后者在Redis集群模式下可能路由到不同节点,导致缓存脏读 - 锁获取失败时别忙等:
time.Sleep(50 * time.Millisecond)后重试最多3次,第4次直接降级返回缓存旧值或空对象,防止线程堆积 - 务必用
defer redis.Del(ctx, "lock:user:"+id)确保锁释放,但得放在if err != nil分支之后——DB查询失败时锁必须保留,否则其他请求又会重复打DB
线上监控里哪些指标能提前预警穿透和雪崩
等告警短信来了再处理就晚了。Gin日志埋点和Redis监控必须盯紧三个真实可测的数字,而不是“缓存命中率”这种虚指标。
- 穿透预警看
redis.keyspace_hits / (keyspace_hits + keyspace_misses)是否且持续1分钟,同时<code>db.query_count中WHERE id = ?类查询占比突增到> 70% - 雪崩预警看Redis的
expired_keys每秒增长率是否超过历史均值3倍,且connected_clients在10秒内上涨> 500 - Gin中间件里记录每个接口的
cache_miss_duration_msP99,如果某接口该值从2ms跳到450ms,基本就是局部雪崩前兆 - 布隆过滤器误判率必须实时上报:
bloomFilter.EstimatedFalsePositiveRate()超过0.02就得扩容或重建,别等用户投诉才查
实际落地时,最常被忽略的是布隆过滤器与写库的强一致性——删用户后没同步删过滤器里的bit,或者新增用户时网络抖动导致Add()失败,都会让防护形同虚设。这事没法靠测试覆盖,得在DAO层写死钩子,任何UserDAO.Create或Delete操作后,必须走一遍bloomFilter.Add/Delete并记录error log。


















