登录
首页 >  Golang >  Go教程

Go singleflight:热点缓存失效时只让一个请求回源

来源:Golang学习网专题原创

时间:2026-06-08 19:06:00 588浏览 收藏

所属专题:Go 并发治理实战

缓存击穿最危险的地方在于“同一瞬间”。一个热点 key 过期后,大量请求同时发现缓存未命中,并一起打到数据库。singleflight 的目标不是缓存数据,而是合并同 key 的回源动作。

singleflight 解决什么

它保证同一个 key 在同一时刻只有一个函数执行,其它请求等待结果。执行完成后,所有等待者共享同一份返回值。

不要把 key 设计得太粗

key 太粗会把不相关请求串行化,key 太细又无法合并热点。通常应使用业务对象 ID、租户 ID 和影响结果的参数共同组成 key。

错误和超时也要处理

如果回源失败,等待者会一起拿到错误。对于高价值热点,可以结合短 TTL 空值缓存、降级数据或后台刷新来减少反复击穿。

生产场景

适用于热点商品、热门文章、活动配置、权限列表等缓存失效后会集中回源的读路径。singleflight 重点解决瞬时并发合并,而不是替代缓存。

关键指标

  • singleflight shared 比例
  • 热点 key 回源次数与数据库 QPS
  • 回源失败后的等待者错误数量

常见误区

  • key 设计太粗导致不同请求互相阻塞
  • 回源函数无超时,所有等待者一起被拖住
  • 把错误结果长期缓存导致恢复后仍返回旧错误

落地建议

生产落地常和短 TTL 缓存、空值缓存、后台刷新一起使用。对高价值热点可以设置 stale-while-revalidate,让用户优先拿到可接受的旧值,同时后台异步刷新。

代码示例

var group singleflight.Group

func LoadProduct(ctx context.Context, id int64) (*Product, error) {
    key := fmt.Sprintf("product:%d", id)
    v, err, _ := group.Do(key, func() (any, error) {
        return repo.LoadProduct(ctx, id)
    })
    if err != nil {
        return nil, err
    }
    return v.(*Product), nil
}

上线检查

  • singleflight key 包含影响结果的参数。
  • 回源函数必须有 timeout。
  • 监控 shared 命中比例和回源错误。
声明:本文转载于:Golang学习网专题原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>