tarfile流式写入可避免oom,应使用"w|gz"模式配合with语句、预缓存stat、手动构造tarinfo、裁剪相对arcname,并用gzip.gzipfile指定compresslevel=1提升吞吐。

用 tarfile 流式写入,避免内存堆积
直接把所有小文件读进内存再打包会 OOM,尤其当文件数超百万或总大小远超可用 RAM 时。tarfile 支持流式写入(mode="w|" 或 "w|gz"),边读边写,内存占用基本恒定在几 MB 级别。
关键点:
-
mode="w|"对应未压缩 tar 流,"w|gz"对应 gzip 压缩流 —— 后者 CPU 开销略高但磁盘节省明显 - 不要先调用
add()再close(),而应配合with语句确保底层管道及时 flush - 避免用
add(file_path)批量添加:它会递归扫描目录、生成大量TarInfo对象,反而吃内存
import tarfile
with tarfile.open("output.tar.gz", "w|gz") as tar:
for file_path in file_list: # file_list 是已知路径列表,非 glob 结果
tar.add(file_path, arcname=file_path.split("/")[-1])
跳过 os.stat() 重复调用,预缓存元数据
默认 tar.add() 每次都调用 os.stat() 获取 mtime/size/mode,对海量小文件是严重 I/O 瓶颈。实际打包时若不关心精确时间戳或权限位,可手动构造轻量 TarInfo。
常见做法:
- 用
pathlib.Path(file_path).stat()提前批量获取 size/mtime,缓存在列表里 - 构造
TarInfo时只设必要字段:name、size、mtime、type(tarfile.REGTYPE) - 显式设
tarinfo.uname = tarinfo.gname = "root"可避免 uid/gid 查找开销
from tarfile import TarInfo, REGTYPE
tarinfo = TarInfo(name="data.txt")
tarinfo.size = 1024
tarinfo.mtime = 1717020000
tarinfo.type = REGTYPE
tarinfo.uname = tarinfo.gname = "root"
with open("data.txt", "rb") as f:
tar.addfile(tarinfo, f)
注意 arcname 路径处理,防止归档内出现绝对路径或冗余父目录
如果 file_path 是 /home/user/logs/app_001.log,直接 tar.add(file_path) 会导致归档里也带完整绝对路径,解压时可能覆盖系统文件;用 arcname=os.path.basename(file_path) 又会丢失目录结构。
更安全的控制方式:
- 统一用相对路径作为
arcname,例如基于某个根目录裁剪:os.path.relpath(file_path, root_dir) - 确保
root_dir是所有待打包文件的公共父目录,且不以/开头 - 绝对禁止传入以
/开头的arcname,否则tarfile会静默转为绝对路径(不受filter参数影响)
gzip 压缩率与速度权衡:用 zlib 替代默认级别
Python 默认 "w|gz" 使用 zlib.Z_DEFAULT_COMPRESSION(等价于 -1),对小文件压缩率差、速度慢。实测将压缩级别设为 1 或 2 可提升 3–5 倍吞吐,体积仅多出 5%–10%。
实现方法:
- 不用
tarfile.open(..., "w|gz"),改用gzip.GzipFile手动包装 - 传入
compresslevel=1,并指定mtime=0避免每文件写时间戳(节省 CPU)
import gzip
import tarfile
with gzip.GzipFile("output.tar.gz", "wb", compresslevel=1) as gz:
with tarfile.TarFile(fileobj=gz, mode="w") as tar:
for fp in file_list:
tar.add(fp, arcname=...)
注意:gzip.GzipFile 不支持 mtime=0 的快捷参数,需额外用 gzip._GzipWrite 或接受默认时间戳。
真正卡点不在算法选型,而在路径裁剪逻辑是否健壮、TarInfo 构造是否绕过 stat、以及 gzip 层是否被 Python 默认行为拖慢 —— 这三处不调优,哪怕换 pyarrow 或 rust-tar 绑定也难提效。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











