资讯动态

pigz:gzip的并行实现,让多核CPU不再闲置

发布时间:2026/10/16 1:29:31 来源:尧图企业网站定制
你是不是也有过这种经历打包一个几 GB 的日志目录gzip老老实实跑了两三分钟CPU 占用率却只有孤零零的一个核。看着任务管理器里剩下七八个闲着的核心一边等一边心疼机器。后来我换了个思路把压缩这件事从“单线程硬扛”改成“多线程并行”时间直接砍半这个改变我生命里真正起作用的工具就是今天要聊的pigz。pigz是gzip的并行实现全称是 Parallel Implementation of GZip由某知名开发者维护。它底层仍然调用 zlib 库压缩格式和标准 gzip 完全兼容。也就是说你用pigz压缩出来的.gz文件拿给任何一台没有装 pigz 的机器、用普通的gzip/gunzip都能正常解压。反过来你用gzip压的文件pigz也能直接解。这种“格式全兼容、速度翻几倍”的特性让它成了我在服务器上处理大文件时的默认选择。这篇文章我会从“为什么它能这么快”的原理讲起接着给出完整的安装方式、核心参数用法再用实测数据对比pigz和gzip的差距最后聊几个实战场景和我踩过的坑。不管你是运维、后端开发还是经常和数据集打交道的分析岗位看完这篇文章应该都能直接上手用起来。1. 为什么单线程压缩是性能瓶颈先理解 gzip 的工作方式在介绍 pigz 之前我们先得搞清楚传统的 gzip 到底慢在哪。gzip默认是单线程程序。它读取输入数据后通过 LZ77 算法加上 Huffman 编码进行压缩。LZ77 算法的核心是滑动窗口匹配在当前已经处理过的数据里寻找重复字符串然后用更短的引用替代它们。这个过程理论上是可并行的但传统的 gzip 实现为了保持输出格式的确定性选择了最简单的路线——从头到尾一个线程顺序处理。换句话说你的服务器就算有 32 个核心gzip 忙活的始终只是其中一个。其他核心都在围观CPU 总占用率可能只有 3% ~ 5%。在数据量小的时候这没什么但当你压缩 10 GB 的数据库备份或者一整年的访问日志时单线程的耗时就会变得非常扎眼。pigz做的事情其实很直接它把输入数据切分成多个独立的块block每个块交给一个线程去压缩最后再按照正确的顺序把结果拼接成一个合法的 gzip 流。因为每个块的压缩过程互相不干扰多核加速比可以做到接近线性。你用-p 8启动 8 个线程在 CPU 核心数足够的情况下理论上就能比单线程快到差不多 8 倍。这里有一个关键细节gzip 格式本身是按“成员”member组织的每个成员都是一个独立的压缩数据块。pigz 利用了这个格式特点每个线程负责一个成员最后把多个成员串在一起。解压端包括标准 gunzip会逐个成员解码因此完全不需要知道这份数据是被几线程压缩的。了解了这个底层逻辑你就能理解为什么 pigz 压缩的文件在任何环境下都能解不会有兼容性问题。可能有人会问既然能切成块并行压缩那解压是不是也能并行答案是“部分可以”。pigz解压时同样会尝试多线程但由于 gzip 格式中每个成员的长度信息只在压缩时写入解压端必须先从文件中定位各个成员的起始位置。对于多成员文件pigz 会并行解码不同的成员可同一个超大成员内部的解压依然是单线程的。这个差异在“压缩时用了较大块大小”的场景下尤其明显后面实战部分我再细说。2. 安装 pigz 的几种方式包管理器、源码编译与容器化pigz的安装非常友好几乎所有主流发行版都收录了它。我按平台把常见方式列在下面你可以直接对照操作。Ubuntu / Debian 系apt-get update apt-get install -y pigzCentOS / RHEL / Fedora 系yum install -y pigz # 或者较新版本的 Fedora dnf install -y pigzmacOS通过 Homebrewbrew install pigz如果你在 Windows 的 WSL 环境里用上面对应发行版的命令即可。也可以在 Git for Windows 自带的工具集里找到 pigz。如果你的生产环境不能访问外网源或者想自己编译最新版源码安装也非常简单。pigz 的源码依赖 zlib 的开发头文件确保系统里已经装了zlib1g-devDebian或zlib-develRHEL之后执行git clone https://github.com/madler/pigz.git cd pigz make make install编译产物就是一个独立的pigz二进制没有额外运行时依赖拷贝到其他同架构机器上甚至能直接跑。我曾在没有编译环境的内网服务器上直接拷贝编译好的二进制过去用非常省事。如果你是容器化部署的应用也可以基于 Alpine 或 Debian 镜像预先安装FROM debian:bookworm-slim RUN apt-get update apt-get install -y pigz安装完成后验证一下版本确保正常pigz --version我建议安装完后顺手做一个小实验随便找一个较大的文件分别用gzip和pigz -p $(nproc)压缩对比时间。实测数据会让你对这本书后面的内容有更直白的感知。3. 核心参数速查我日常最常用的就是这几个pigz的命令行参数和 gzip 高度相似熟悉 gzip 的人基本不需要重新学。下面这张表是我日常高频使用的参数按使用频率排序参数作用说明-p N指定并行线程数核心参数一般设置为 CPU 物理核心数-1~-9压缩级别和 gzip 一样数字越大压缩率越高、速度越慢-k压缩后保留原文件默认压缩后会删除源文件加-k保留-d解压模式相当于gunzip-v显示详细信息会打印压缩率、耗时等统计-r递归处理目录当传入参数是目录时遍历压缩其中的文件-b N设置块大小单位为 KB影响并行分块粒度默认 128-z标准 gzip 兼容模式保证输出完全兼容不支持多成员的 gzip-f强制覆盖目标文件已存在时强制写入基础用法示例# 使用 8 个线程压缩 access.log压缩级别默认是 6 pigz -p 8 -k access.log # 输出文件为 access.log.gz原文件保留 # 解压 pigz -d -p 8 access.log.gz # 压缩时使用最高压缩率对应 -9 pigz -p 8 -9 -k access.log # 递归压缩某个目录下所有 .log 文件 pigz -p 8 -r -k /var/log/nginx/有几个细节值得单独强调。第一-p并不是越大越好。线程数超过物理核心数之后收益趋近于零反而可能因为线程切换增加开销。我一般用-p $(nproc)直接取当前机器的核心数简单粗暴。第二压缩级别-9并不一定比-6慢到离谱pigz 的并行设计会摊掉一部分额外开销。但压缩率的提升通常也只有 1% ~ 3%如果对磁盘空间不敏感我倾向直接用默认的-6换速度。第三-b块大小默认 128 KB除非处理超大文件并且想进一步优化线程间负载均衡否则很少需要改动。调大块大小每个线程处理的数据更多能略微提升压缩率但内存占用随之上升调小块大小吞吐量更高压缩率略有下降。再看一个组合场景。备份数据库导出的 SQL 文件时我通常这么写mysqldump -u root --single-transaction mydb | pigz -p 8 -9 mydb_$(date %F).sql.gz核心逻辑是先让mysqldump输出明文 SQL直接喂给 pigz 的标准输入pigz 用 8 线程压缩后写入带日期的文件。全程不用落盘中间文件既节省了临时空间又充分利用了多核。这条命令是我最常用的“一行流”模式尤其适合大库备份。4. 实测对比gzip vs pigz到底快多少为了让你对“快多少”有一个具体的感知我用一台配置为 8 核 CPU、16 GB 内存的 Linux 服务器做了几组对照实验。测试数据是一份大约 2.1 GB 的日志文件格式是文本里面主要是 JSON 行。第一组分别用gzip -6和pigz -p 8 -6压缩同一份文件结果如下压缩方式耗时压缩后大小CPU 总占用gzip -668 秒389 MB单核 100%pigz -p 8 -69.5 秒392 MB约 620% ~ 750%时间相差接近 7 倍压缩后体积只多了不到 1%。这是因为并行压缩不会改变压缩算法的核心逻辑每个块内部的压缩率和单线程基本一致唯一可能增加的是块与块之间的冗余没有被跨块消除但这种损失在文本类数据上非常小。第二组测试最高压缩率。用gzip -9和pigz -p 8 -9对比压缩方式耗时压缩后大小gzip -998 秒371 MBpigz -p 8 -914.2 秒375 MB结论和第一组类似速度提升明显体积差距可以忽略。第三组测试解压速度。把同一份pigz压缩出的.gz文件分别用gunzip和pigz -d -p 8解压解压方式耗时gunzip10.7 秒pigz -d -p 83.8 秒解压加速比没有压缩那么夸张但依然有近 3 倍提升。如果你处理的文件是单线程压缩出的超长 gzip 流pigz 在解压时能拆分的程度会受限提速会更少。这个差异背后就是我第一部分提到的“成员”结构问题。再补充一组小文件场景。比如一个目录里有 5000 个平均大小 1 KB 的小文件用pigz -p 8 -r递归压缩实际加速效果反而可能不如 gzip。原因很容易理解每个小文件的压缩时间极短线程分配、任务调度、文件 IO 的开销淹没了并行收益。所以我后来对小文件目录的处理策略都是先打包成 tar 再整体压缩后面实战部分会细讲。5. 实战场景从备份到日志归档pigz 的正确打开方式pigz 最适合的场景有三个大文件压缩、管道流处理、定时备份任务。下面我逐个讲我的实际配置和理由。第一个场景是数据库备份。MySQL 的逻辑备份导出后是一段连续文本压缩收益极高但也极度耗时。之前我用 gzip 备份一个 30 GB 的数据库整个流程接近 20 分钟后来换成 pigz时间降到 3 分多钟。推荐配置如下mysqldump -u root --single-transaction --quick mydb \ | pigz -p $(nproc) -9 \ /backup/mydb_$(date %F).sql.gz注意这里我加了--quick参数让 mysqldump 逐行读取而不是一次性载入内存因为管道下游的处理速度远快于单线程 gzip避免 mysqldump 端变成新的瓶颈。如果你遇到“pigz 没有跑满 CPU”的情况多半是上游命令输出太慢这与 pigz 本身无关。第二个场景是日志归档。线上服务的 Nginx 日志和业务日志每天会产生数 GB 文件保留原始文件占空间不保留又怕临时要回溯。我的方案是每天凌晨用 cron 执行一个 Shell 脚本压缩昨天的日志#!/bin/bash LOG_DIR/var/log/myapp DATE_YESTERDAY$(date -d yesterday %Y%m%d) find ${LOG_DIR} -name *${DATE_YESTERDAY}*.log -type f | while read f; do pigz -p $(nproc) -k -9 $f done-k保留原始文件压缩完成后我会再跑一个清理命令只保留 7 天内的原始日志。这里有个经验如果你确认压缩结果没问题尽早删掉源文件否则磁盘空间压力反而会先升后降。第三个场景是文件传输前的预处理。跨机房同步大文件时网络带宽通常是瓶颈。先把文件压缩到最小再传输能显著降低带宽占用和传输时间。例如tar -cf - /data/to_send | pigz -p 8 -9 /tmp/to_send.tar.gz # 传输完成后解压 pigz -d -p 8 /tmp/to_send.tar.gz tar -xf /tmp/to_send.tar.gz顺便提一句 tar 的组合方式。tar 本身能直接调-I参数指定压缩器tar -I pigz -p 8 -czf backup.tar.gz /data/to_send tar -I pigz -p 8 -xzf backup.tar.gz这种写法最简洁而且能同时保留 tar 的归档能力和 pigz 的并行压缩能力。如果你要压缩的是单个文件直接用 pigz 就够了如果是多个文件或目录建议先 tar 再 pigz处理小文件时的性能问题也会被一并解决——因为 tar 把所有小文件合并成了一个大流pigz 再对这个大流做切块并行效果和小文件分别压缩完全不同。6. 避坑指南我踩过的几个 pigz 相关的坑与对应解法pigz 用起来不难但要把它用得稳有几个坑值得提前知道。以下五条都来自我实际踩过的经历。第一个坑是进程数超过 CPU 核心数。有次我在一台 16 核机器上压缩 40 GB 数据图省事写了-p 32结果 CPU 被线程切换拖累比-p 16还慢了 20%。pigz 的线程模型是每个线程独立做压缩任务过多的线程不会带来额外收益只会增加调度开销。建议预先检查nproc lscpu | grep Core(s) per socket然后把-p设置成物理核心数而不是逻辑线程数。如果机器上还有其他服务在跑可以留一两个核给系统比如 16 核就写-p 14。第二个坑是 I/O 瓶颈被误判为“pigz 不够快”。有次我压缩一块慢速机械硬盘上的大文件-p 8跑起来 CPU 占用率始终只有 30%我一度以为参数没生效。后来用iostat一看硬盘读写已经 100% 饱和。这种情况下再增加线程数毫无意义应该先把文件拷贝到 SSD 或 tmpfs 再压缩。判断方法很简单在 pigz 运行的同时开一个终端跑top如果%Cpu(s)的sy系统态高或者wa等待 I/O高说明瓶颈基本不在压缩算法本身。第三个坑是压缩率与兼容性的权衡。pigz 默认输出的 gzip 格式是“多成员”格式。虽然标准 gzip 也能解但某些古老解压工具或者特殊固件可能只支持单成员。如果你的.gz文件要交给下游系统处理且不确定对方的兼容性建议用-z参数强制 pigz 输出更接近 gzip 单成员模式的结果pigz -z -p 8 -9 bigfile代价是-z模式下并行度受限速度会比默认模式慢一些。但相比完全退回 gzip仍然快不少。第四个坑是解压时输出文件名不一致。pigz 解压时会根据压缩文件内部存储的原始文件名来恢复如果你压缩时用了-k保留了源文件再解压时会看到类似file.gz和file同时存在。为了避免覆盖源文件pigz 会在目标文件已存在时默认询问是否覆盖在脚本里需要显式加-f或提前删除旧文件。我的建议是写脚本时如果确定目标不存在直接加-f强制写省得交互卡住。第五个坑是管道下游的缓冲问题。用mysqldump | pigz backup.gz时如果 mysqldump 输出是带缓冲的并且在异常退出时缓冲数据丢失那么压缩出的文件可能是损坏的。预防方式是给 mysqldump 加--quick参数强制逐行无缓冲输出同时在管道命令结束后用gzip -t或pigz -t测试压缩文件的完整性。完整性的验证命令是pigz -t backup.gz echo OK这条命令我会放进备份脚本的最后一步如果校验失败就触发告警。文本类数据损坏后通常还能解出一半内容最怕的是数据库备份里某个页坏了恢复时才发现。校验这一步不能省。7. 进阶技巧并行度自适应、进程优先级与流式压缩如果你已经把 pigz 基本用熟了下面这几个进阶技巧可以进一步释放它的潜力。第一个是并行度自适应的脚本写法。不同机器核心数不一样我通常在脚本开头定义一个变量CORES$(lscpu | awk /^CPU\(s\):/{print $2}) # 或者直接 CORES$(nproc) PIGZ_OPTS-p ${CORES} -9这样脚本拷贝到任何机器上都能自动适配。如果你的机器是云服务器vCPU 可能被超分此时还可以参考lscpu -p的物理核数量来调整避免在共享宿主机上抢满 CPU。第二个是进程优先级。备份任务通常在后半夜跑但如果生产环境 24 小时在线我不建议把 CPU 占满。用nice和ionice可以降低任务的优先级nice -n 10 ionice -c2 -n7 pigz -p $(nproc) -9 bigfilenice降低 CPU 优先级ionice降低磁盘 I/O 优先级。这样即使备份任务在高负载时段触发也不至于把在线服务的延迟拉高。第三个是流式压缩中的 yaml/JSON 日志场景。我处理过一种“多行一条记录”的日志格式每几条日志之间有空行分隔此时直接压缩文本整个流即可不需要额外处理。但如果你需要保留日志的实时可读性可以考虑先用 zstd 或 lz4 这类面向速度的设计这点我要单独说一句pigz 在很多场景下确实是“更快”但如果你追求极致的压缩速度zstd 的--ultra -22和-T0多线程模式也值得对比。pigz 的优势在于生态兼容和零成本替换zstd 的压缩率调整空间更大。我个人的选择是需要 gzip 兼容时用 pigz需要极致压缩率或解压速度时看 zstd。第四个是内存预算。pigz 的每个线程都会申请一个独立的窗口缓冲区-b 128时默认内存占用大约是 128 KB × 窗口数量。窗口大小由压缩级别决定-9时窗口较大线程数一多内存占用会明显上升。我曾在 2 GB 内存的小机器上开-p 8 -9结果触发了 OOM。后来我改成-p 4 -6问题才解决。如果你在高压缩级别下遇到内存不足优先降线程数再考虑降压缩级别。第五个是临时目录处理。pigz 解压时如果目标文件系统空间不够会直接失败。在大文件解压前先用df -h检查剩余空间是否大于压缩文件解压后的大小。这个习惯帮我在一次磁盘满的故障前及时踩了刹车。8. 从 gzip 平滑迁移到 pigz 的成本与收益评估如果你现在还在用 gzip迁移到 pigz 的成本几乎为零但这不意味着所有场景都应该立刻切换。我建议按下面的思路评估自己的情况。先看收益。最直接的收益是大文件压缩时间缩短。如果你压缩的数据量经常超过 1 GB且机器有多核pigz 带来的提速几乎是立竿见影的。比如日志归档、数据库备份、数据导出这几个场景换个工具脚本几乎不用改。gzip命令换成pigzgunzip换成pigz -d或者直接在.bashrc里加一个别名alias gzippigz alias gunzippigz -d这种方法最省事缺点是部分脚本可能依赖 gzip 的精确输出行为比如gzip -n去掉文件名时间戳而 pigz 对应参数也能做到。建议先在测试环境验证一两个核心脚本再大规模替换。再看成本。pigz 不是标准系统自带工具目标机器上需要提前安装。如果你管理的一批临时容器里没有预装 pigz脚本里最好加一个“检测不到 pigz 就退回 gzip”的逻辑if command -v pigz /dev/null 21; then COMPRESSORpigz -p $(nproc) else COMPRESSORgzip fi这样新旧环境都能跑属于典型的渐进式迁移。最后看压缩率的细微差异。并行压缩因为分块导致跨块冗余未被消除最终体积通常会比 gzip 大 0.5% ~ 2%。对绝大多数场景这个差异可忽略但如果你压的是“高度重复且跨文件长度很大”的数据比如单个 100 GB 的超大文本文件里的重复内容单线程 gzip 在去重上会更彻底。遇到这种极端情况我会先评估体积差是否真的影响存储成本再决定是否保留一条 gzip 通道。9. 写在最后一个小习惯让我少走了很多弯路在使用 pigz 的这段时间里我最大的体会是工具本身不难难的是分清什么时候该用它。多线程并行压缩不是万能的它解决的是“单线程 CPU 空转”的问题但如果你遇到的瓶颈是网络、磁盘 I/O、上游命令输出速度那么换再快的压缩器也没用。动手之前先跑两个命令——top看 CPUiostat看磁盘确认瓶颈在压缩算法本身再去切换工具效果立竿见影。最后分享一个我现在一直在用的习惯所有临时生成的压缩文件我都会顺手跑一遍完整性检查再删除源文件。命令很简单但每次备份后的安心感是真的。如果你也打算在生产环境引入 pigz我建议第一周先只在低风险场景用跑通之后再逐步扩大范围。多核机器上的压缩提速体验用一次就回不去了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑