gzip.writer复用必须调用reset重定向底层io.writer,不可并发写入;close()必调以写入crc32和isize尾部,否则解压失败;默认缓冲小需配bufio.writer优化i/o。

gzip.Writer 复用必须 Reset,不能直接 Write
直接把同一个 *gzip.Writer 丢给多个 goroutine 写,会 panic:fatal error: concurrent write to gzip writer。它不是并发安全的,底层 flate.Writer 状态机不可重入。
正确复用方式是:每次取用前调用 w.Reset(io.Writer),把底层输出目标重定向到新的 io.Writer(比如新文件、新 buffer)。Reset 不会清空内部压缩字典状态,对连续短文本有隐式加速效果。
-
sync.Pool管理*gzip.Writer实例时,New函数里不能传具体io.Writer,得传nil - 从 pool 取出后必须立刻
w.Reset(dst),否则写入会 panic 或静默失败 - 别在 Reset 前调用 Close —— Close 会把 writer 置为不可用状态,Reset 会 panic
压缩级别选错会让 CPU 和内存双爆炸
级别设成 gzip.BestCompression(=9)处理小数据(gzip.BestSpeed(=1)更慢,且内存分配翻倍。这不是线性增长,而是 deflate 算法在高压缩级别下启用更大滑动窗口和哈希表,导致 GC 压力陡增。
真实影响取决于数据特征:
- HTTP 响应体(几百字节):用
gzip.BestSpeed,延迟低、内存稳定 - 日志批量上传(几 MB):
gzip.DefaultCompression(=6)是平衡点 - 归档文件(>100MB):可试
gzip.BestCompression,但务必配合bufio.WriterSize(..., 64*1024)避免小块刷盘
bufio 缓冲大小没调好,I/O 性能掉一半
默认 gzip.NewWriter 底层用的是无缓冲的 io.Writer,每写一次小数据就触发一次系统调用。加 bufio.Writer 能聚合写入,但缓冲区太小(如 4KB)或太大(如 1MB)都吃亏。
64KB 是多数磁盘和网络场景下的实测平衡点:
- 小于 64KB:系统调用频次高,CPU 花在上下文切换上
- 大于 64KB:内存占用上升,且大缓冲在流式响应中造成首字节延迟
- 注意:要同时给
gzip.NewReader配bufio.NewReaderSize(r, 64*1024),否则解压端读取仍卡顿
Close() 不调,gzip 文件永远解不开
gzip.Writer.Close() 不只是 flush 缓冲区——它写入 gzip 尾部(CRC32 + ISIZE),缺了这个,任何标准解压工具(包括 gzip -d、Python 的 gzip.open)都会报 invalid checksum 或 unexpected EOF。
常见误操作:
- 用
Flush()代替Close():只发压缩块,不写尾部,无法被识别为合法 gzip 流 - defer
Close()但没检查返回值:压缩过程出错(如磁盘满)时,Close()才真正报错 - 多级嵌套 Writer(如
gzip.NewWriter(bufio.NewWriter(file))):必须按顺序gz.Close()→bufioWriter.Flush()→file.Close()
最易被忽略的是网络流场景:HTTP handler 中写完就 return,忘了 gz.Close(),客户端收到的 gzip 数据永远不完整。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











