原文链接:http://www.conductor.com/nightlight/how-to-build-a-speedy-custom-compression-codec-for-hadoop/
译者:林炀
小象科技原创作品,欢迎大家疯狂转发;
机构、自媒体平台转载务必至后台留言,申请版权。
开发人员喜爱的
数据压缩格式
数据压缩提供了一种可以减少数据存储和处理过程间的数据传输的方法,以此提高I/O性能。任何使用过因特网的人都知道下载一个庞大的文件会比下载一个小文件快的多,而压缩就可以用来改善这个问题。
Gzip是一种被广泛使用的压缩格式,它可以让文件在压缩前后的空间占用上有一个相当大的差异,也正是因为这种很高的压缩比率,Gzip才得以被为众人所知。
但是请注意,Gzip会占用大量的CPU资源。虽然使用Gzip对数据进行压缩转移时可以节省I/O时间,但是在压缩和解压缩的时候都需要消耗CPU的性能来进行处理。它最优化了I/O密集型应用的性能,但是未来仍然需要在压缩比率(I/O负载)和压缩速度(CPU负载)之间能达成一个平衡上来进行改进。 由于Gzip已经普及,并且在数据分析和对数据进行ETL (Extract, Transform, Load)时我们所使用的CPU性能已经非常的强劲,因此目前Gzip成为了我们压缩时的第一选择。
在数据使用量上,Conductor Searchlight的报表架构允许用户交互的去操作报表数据,但是这要取决于我们的UI服务器是否能访问由我们的ETL管道产生的压缩数据。
之前我们曾经尝试使用Gzip在服务器上生成报告,但是却发现我们的UI服务器占用了大约三分之一的平均应用程序事务时间用在解压报告并呈现结果给用户上,这通常会导致加载时间变得过长。
我们确实可以通过给UI服务器更换更快的CPU或多核的CPU在硬件上直接的扩展机器的性能,并在这些机器上面来生成报告。但是通常来说相比于后台服务器,UI服务器的性能并没有那么重要,而且我们一般也不会去拓展UI服务器的性能。
另外还有一种压缩格式是Snappy,它是由Google开发的。Snappy的解压缩速度相比于Gzip快了大约三倍,但是相对于在CPU占用时间的改进上,它却牺牲了压缩比率。
我们开始使用Snappy作为我们UI服务器上ETL系统产生的报告数据的压缩格式后,我们明显的看到我们所用的程序响应速度有了一个质的飞跃。当程序运行了一段时间之后,我们注意到在生成报告时服务器的内存使用率突然开始上升,这让我们非常吃惊,因为我们运行的程序在设计时是能将报告数据按“流”的方式传输到用户目录下来的,这并不会需要在服务器内存上保存整个报告。而在我们研究了以后才发现,Snappy在解压时要缓存非常多的数据。
幸运的是,Google在Snappy之上还发布了一个特别的压缩格式,这个格式被成为Snappy framing格式。这种格式修改了Snappy的压缩算法,它可以逐步递增的压缩一个文件,这使得压缩的结果是由压缩块组成并且是独立的,或者这也可以被称为“帧”。在数据压缩和解压的时候都是一帧一帧来进行的,这样也就不用再把整个文件都放到内存中来操作了。
由于在生成报告时,所需要用到的几个文件可能很大,这相对于要处理所有线程请求的内存来说太大了(可能是好几十个未被压缩的百万字节文件),而且每个用户的事务可能会取决于多个文件,因此我们自然就倾向于选择Snappy framing格式以便更好的管理服务器上的内存使用率。
我们曾经使用Hadoop家族的各种工具来构建ETL的整体流程模块。方便的一点是,Hadoop MapReduce过程中可以很轻松的对mappers和reducers产生的结果进行配置使其被压缩。Hadoop Java API中已经包含了可以使用的Gzip压缩和Snappy压缩的编解码器,你所需要做的就是将它们配置在你的Hadoop程序中:
<property>
<name>io.compression.codecs</name>
<value>org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.DefaultCodec,org.apache.hadoop.io.compress.SnappyCodec,conductor.hadoop.core.io.compression.SnappyFramedCodec</value>
<final>false</final>
</property>
<property>
<name>mapred.map.output.compression.codec</name>
<value>org.apache.hadoop.io.compress.SnappyCodec</value>
</property>
<property>
<name>mapred.output.compression.codec</name>
<value>org.apache.hadoop.io.compress.SnappyCodec</value>
</property>
不幸的是,因为Snappy framing格式只是一个可选的选项(事实上,Google甚至都没有提供一个参考实现),因此你刚装好的Hadoop可能并不支持对Snappy framing的开箱即用,你有两个选择:
A.选择并使用一个Hadoop为你准备好的压缩编解码器
B.让自己变身成为一个开发者,并且编写你自己的Snappy framing编解码器,这并不难!
在Hadoop(Java)中
使用自定义数据压缩编解码器
幸运的是,在几个开源项目中,Snappy framing格式已经在Java上实现了。而不幸的是,当我们开始寻找已经实现的编解码器时,却发现并非所有的编解码器都可以用。比如说,一些项目是在Snappy的发布日期与Snappy framing规划阶段期间写的,因此,在这些项目中的Snappy framing格式并没有按照官方最后的规划设计。无论如何,我们找到了snappy-java,它正好符合我们的需求,并且我想它可能也会适合你的项目,其中的SnappyFramedOutputStream和SnappyFramedInputStream类分别包含有你需要的压缩和解压功能。
CompressionCodec是为了编写新压缩编解码器时Hadoop安装配置唯一必须要实现的接口,它可以给你的Hadoop添加一个压缩格式。CompressionCodec的主要职责是创建CompressionOutputStream和CompressionInputStream对象,它们可以对数据分别进行压缩和解压。你所需要做的就是给前述的SnappyFramed 流和相应的Compression流之间提供一个适配器(看看我们的例子 OStreamDelegatingCompressorStream 和IStreamDelegatingDecompressorStream)。没有必要去支持Compressor或者Decompressor,因为MapReduce并不使用它们,可以看一些使用别的方法来解决的例子。
目前你已经编写好了你自己的新数据压缩编解码器,并且已经准备好在MapReduce上运行它了。可能在你的应用程序完美运行起来之前还有一些其它的工作需要完成,这取决于你集群里的其它Hadoop生态系统程序。比如说,很多Conductor的ETL系统使用Oozie来进行工作流管理并且使用Hive作为可以拓展的数据转换工具。我会给你展示这两种技术是如何利用你的新自定义压缩编解码器的。
首先,确保你的Hive能够使用你的新编解码器。将含有你编解码器的JAR文件拷贝到Hive的主机上,并且将它的地址配置到HIVE_AUX_JARS_PATH变量中。现在,剩下需要做的就是告诉Hive什么时候要使用这些新格式了。还记的我们之前教的属性配置吗?你要做的就是更新在io.compression.codecs中的编解码器,让它包含新编解码器的完整路径。
<property>
<name>io.compression.codecs</name>
<value>org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.DefaultCodec,org.apache.hadoop.io.compress.SnappyCodec,conductor.hadoop.core.io.compression.SnappyFramedCodec</value>
<final>false</final>
</property>
你还需要指定MapReduce的结果(中间或最终结果)来使用新的压缩编解码器,这个可以在单个查询脚本或者全局配置文件里来指定,这取决于你所需的压缩需求。
SET mapred.child.java.opts=-Xmx2048m -XX:MaxPermSize=256M -noverify;
SET mapred.output.compression.codec=conductor.hadoop.core.io.compression.SnappyFramedCodec;
SET hive.intermediate.compression.codec=org.apache.hadoop.io.compress.SnappyCodec;
仅仅在Hive上来运行你的自定义编解码器是不够的,必须让Oozie也能支持新编解码器。
需要做的就是添加上述JAR到你的Oozie安装目录下的“libext”目录,如果目录不存在的话就手动创建一个,添加完毕以后运行: [oozie installation directory]/bin/oozie-setup.sh prepare-war 运行之后会创建一个WAR文件,里面包含了能在Oozie中运行的编解码器,当然你的自定义编解码器也在其中,之后重启Oozie就一切就绪了。
现在,Google的Snappy Frame格式应该可以支持Hive和Oozie了,必须庆幸一下这些步骤并没有那么复杂。
如果你曾经希望手动检查你的Snappy压缩文件完整性或者类似的其它操作,那么有一个方便的命令行工具,你可以用它来压缩和解压缩文件中的Snappy-frame格式。
我们上述的Hadoop Snappy Frame压缩编解码器可以在我们不断壮大的开源库Kangaroo中找到。
联系客服