sync.pool 仅在高频、短生命周期、可安全重置的场景下有效降低 gc 压力;new 必须返回全新对象,get 后须手动 reset,put 前需确保对象已脱离所有 goroutine 作用域,否则引发竞态或脏数据。

用 sync.Pool 复用高频对象,减少 GC 压力
高频创建小对象(如 *bytes.Buffer、json.Encoder、自定义请求结构体)会触发频繁 GC,导致 P99 延迟毛刺。Go 的 sync.Pool 不是万能缓存,而是“按需复用 + 无强引用”的轻量机制,适合生命周期短、构造开销大的对象。
- 不要把大对象或带状态的对象丢进
sync.Pool(比如未清空的map或已写入数据的bytes.Buffer),否则可能引发数据污染 -
New函数只在池为空时调用,不保证每次 Get 都执行;务必在Put前重置对象状态(如buf.Reset()、slice = slice[:0]) - 避免在热路径中做类型断言失败的
Get:确保Put和Get类型严格一致,否则 runtime 会 panic
逃逸分析指导栈分配,避免隐式堆分配
编译器根据变量生命周期决定分配位置。一旦变量“逃逸”到函数外(如返回指针、传入闭包、存入全局 map),就会被分配到堆,增加 GC 负担。这不是 bug,而是 Go 安全模型的一部分,但可主动干预。
- 运行
go build -gcflags="-m -l"查看关键函数的逃逸报告;-l禁用内联,让分析更准确 - 常见逃逸点:返回局部变量地址(
return &x)、切片扩容(append到 cap 不足的 slice)、闭包捕获大变量 - 对小结构体(≤ 128 字节),优先用值传递而非指针;若必须传指针,确保它不被存储到长期存活的结构中
用 ETag + If-None-Match 减少 95% 的响应体传输
轮询类接口(如设备状态、配置下发)常返回相同内容。与其每次都序列化、压缩、传输完整 JSON,不如让客户端自己判断是否需要更新——这是 HTTP 协议原生支持的优化,零额外依赖。
- ETag 必须基于业务数据生成(如
md5.Sum([]byte(fmt.Sprintf("%d-%v", obj.ID, obj.UpdatedAt)))),不能用固定字符串或随机值 - 务必在
c.Header("ETag", eTag)后再调用c.JSON(),否则中间件可能覆盖 Header - 如果服务端使用 Redis 缓存响应,ETag 应和缓存 key 绑定,避免缓存击穿时误返回旧 ETag
批量处理 + 连接池,砍掉网络往返和握手开销
单次请求发 1 条 SQL / 调 1 次下游 API 是性能杀手。批量不是“把 for 循环改成一次调用”,而是合并语义、复用连接、控制节奏。
- 数据库批量插入用
INSERT INTO ... VALUES (...), (...),别用事务包 N 个单条 INSERT;PostgreSQL 还支持UNNEST数组参数 - HTTP 客户端必须配
http.Transport:设MaxIdleConns≥ 并发峰值,IdleConnTimeout≥ 30s,否则连接频繁重建 - gRPC 客户端用
option.WithGRPCConnectionPool(n),n 建议设为预期并发数的 1.5 倍;小于 4 会排队,大于 32 可能浪费 fd











