最准的压缩比计算需两次os.stat:先读原始文件size()得originalsize,再gzip压缩并close()后查.gz文件大小得compressedsize,最后用float64(originalsize)/float64(compressedsize)计算,避免除零和未close导致的虚高。

压缩前先获取原始文件大小
直接用 os.Stat 读取 SQL 转储文件(如 dump.sql)的 Size() 方法,这是最准的原始字节数。别用 len([]byte) 读全文件再算长度——大文件会吃光内存,且对换行符处理可能有偏差(比如 Windows 的 \r\n 在某些场景下被误判)。
-
fi, err := os.Stat("dump.sql"),检查err是否为nil - 原始大小就是
fi.Size(),单位是字节 - 注意:如果 SQL 文件是通过
pg_dump或mysqldump生成的,它本身不含二进制控制字符,Size()就是真实文本体积
压缩后立即读取 .gz 文件长度
调用 gzip.NewWriter 完成压缩并 Close() 后,必须立刻用 os.Stat 再查一次输出文件(如 dump.sql.gz)的大小。不能跳过 Close() ——否则 gzip 尾部校验和、ISIZE 字段没写入,文件不完整,Size() 偏小,算出来的压缩比虚高。
-
gzipWriter.Close()是强制刷新缓冲区+写 footer 的关键步骤,漏掉会导致结果不可靠 - 不要在
io.Copy后就去查大小;必须等Close()返回成功 - 如果用
bytes.Buffer做目标(内存压缩),直接用buf.Len(),但仅限小文件;TB 级 SQL 转储必须走磁盘文件
压缩比 = 原始大小 ÷ 压缩后大小
公式就是 float64(originalSize) / float64(compressedSize)。结果通常在 3–8x 之间,取决于 SQL 内容:纯 INSERT 语句、重复表结构、大量空白或注释会拉高压缩比;而已经 base64 编码的 blob 字段或加密字段基本压不动。
- 别用整数除法,否则结果恒为
1 - 如果
compressedSize == 0(极罕见,但可能因写入失败或空文件导致),需提前判断避免除零 panic - SQL 转储里含大量重复关键字(
INSERT INTO、VALUES ()、字段名,gzip 的 LZ77 对这种高冗余文本非常友好——所以实测比随机二进制数据压缩率高得多
为什么不用 compress/gzip 自带的统计?
gzip.Writer 没有暴露已写入压缩字节数的字段,gzip.Reader 也没有提供原始数据长度的接口。标准库设计就是流式、无状态的,它不缓存原始数据也不记录元信息。想拿到压缩比,唯一可靠路径就是两次 os.Stat ——一次压前,一次压后。
- 第三方库如
github.com/klauspost/compress的 zstd 实现倒是有Encoder.EncodedSize(),但 gzip 不行 - 有人试图用
io.TeeReader+ 计数器拦截原始流,但会破坏流式处理优势,且无法反映真实 gzip 输出长度(因为压缩是块级的,输出长度不等于输入长度的线性函数) - 真正要注意的不是“怎么算”,而是“什么时候算”:必须在
Close()之后、文件未被其他进程截断或覆盖之前
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











