刘毅同学

About Python, MySQL & Life

Python流式压缩和解压缩调研总结

| Comments

Changelog

Time Description
2016-01-05 小幅修改
2015-12-26 initial version

最近两周在研究和实现了xtrabackup流式压缩上传&下方案,这里做一下总结分享。

实现的效果是备份不在本地暂存,压缩内置到上传逻辑中,通过配置文件可以配置压缩细节,对上传使用者来讲是『无感知』的。利用压缩提高了上传效率和备份空间的使用率。

压缩率和压缩性能的比较

  • lz4
  • gzip/pigz
  • lzop
  • qpress

结论:在兼顾压缩率和压缩占用CPU资源以及压缩效率几个方面,最终选择了lzop。

lz4据说是压缩最快的算法,最终没有选择,有几个方面的原因:

  • Python支持不完善,因为我的需求是流式的上传和下载,压缩源是一个tar stream,和压缩后的文件不会落在本地,而是直接上传到远端。
  • 可运维性不高:lzo文件没有找到和gzip, lzop这样的命令行工具可以一个命令来对一个完整的lz4文件管理。当调用file a.lz4 显示的文件是data,这样就无法确认这个文件到底是否正确了。

gzip给力的地方:

  • gzip是目前应用最为广泛的格式了,看过很多压缩率评测的文章,gzip压缩率十分给力,在compresslevel是7到9时,压缩比很给力。
  • Python也是在standard library中支持gzip的压缩和解压缩。

gzip不够给力的地方:

  • 压缩速度慢,在高压缩率上很慢。但是compress level在2~3时,gzip无论是压缩比还是压缩效率上都很有竞争优势
  • Python标准库gzip压缩对stream支持不够,流式压缩期望的方法是:传入一个fileobj,返回一个fileobj。而Gzip模块直接用的话是不支持这种方式的。

然而gzip不足点是可以弥补的。 首先是压缩速度上,默认gzip是不支持并行化的压缩,最多只能有单核的性能,这是压缩性能的瓶颈。但是gzip是有其他工具支持并行压缩的 —— pigz就是并行压缩版本的gzip,测试结果显示pigz可以充分利用多核并行化的性能,让压缩时间有明显的减少,当然代价就是load也会成倍的增长(24核机器上,我只测试了2~8个线程数)

其次是流式压缩可以通过Python轻松实现数据分块并行压缩。这里很重要的一点是:可以将每个数据分块看做一个独立文件压缩最终可以合成一个符合Gzip格式的压缩文件 简单的看了下的pigz的代码注释,发现pigz的原理也是这样子的。Python中使用生产者和消费者模式(生产者:一个线程专门来从数据源拉数据,数据存入Queue中,多个压缩进程负责压缩,把压缩后数据再放入到PriorityQueue按顺序组合成一个文件即可,当然也可以直接分块上传到远端服务器)。这也是我实现的第一个版本的流式压缩方案。这个方案主要的弱点在于内存资源占用比较大:无论是从数据源读取的原始数据块还是压缩后的压缩数据块,在最终写入/上传前都需要缓存在内存中。 在流式压缩上传的场景下,压缩上传的输出端是REST上传接口,因此并行压缩的内存占用上一定程度上受上传接口的性能影响。极端的情况,当上传速度很慢时,为了不影响压缩效率则需要在内存中开辟更大的内存buffer来放入等待压缩的数据块,这时就会占用到比较大的内存资源。不过也可以通过限制上传队列大小,在队列满的情况,数据源的write会阻塞等待。

我的第二个方案的是利用pigz外部工具在读取数据源前利用管道先接入到pigz,在从pigz直接读取到压缩后的数据流。这个方案算是最终方案的替代方案,之所以没有最终使用,原因在于和lzop相比,在压缩率相近的情况下,pigz消耗了更多的CPU资源。

对比方案:pigz -p 2 -2 vs lzop -c ,即通过2线程压缩等级2和默认的lzop对比,前者压缩比可以高出10%,但是Load比后者高出了2倍不止,同时速度上也慢了25%。

lzop给力的地方: 实际测试中,lzop兼顾性能和压缩比同时压缩占用的CPU上是最平衡的。压缩速度稍慢于lz4,压缩比上可以达到gzip等级2~3的水平,同时cpu占用率和内存上比gzip低。

lzop不给力的地方:

  • Python bindings 接口较少,不能直接用来流式压缩上传。
    • 通过 外挂 方式也可以完成。(其实即使支持的好,也需要多进程来并行工作,和外挂差别不大)

总结

  • lzop和pigz这两个压缩方案上在实际应用上都很有竞争力,只是我涉及的项目需求是生产环境上尽量不占用过多CPU,MEM资源,可以在压缩比上做妥协。因此选择了lzop。如果你在意CPU资源,更在乎压缩数据大小,则pigz是不错的选择。
  • 外挂 方式的数据压缩方案虽然集成上让Python程序有更多的外部依赖,但是考虑到Python本身并没有真正的线程的并行方案(你想要并行也要生成多个进程),其实资源占用区别不大。同时方案选型上也更加灵活。
压缩工具 描述 参数 优势 劣势
gzip 最常用的压缩工具 -2 压缩比在压缩参数大于2时很高,平台上通用很高,所有Linux发行版都会有预装,同时tar也集成了gzip压缩 单线程,较慢
pigz 多线程版gzip,主页, github 多线程并行+gzip压缩算法,无论从性能上还是压缩输出上都很不错,是个很不错的选择 压缩效率和系统资源占用成正比
lzop

Comments