阅读更多
原文链接: http://click.aliyun.com/m/26743/
摘要: 一,前言 GZIP是最常见的压缩文件格式,目前DataX是支持对该压缩文件直接的读取,但每个GZIP仅仅只能启动一个线程来读取,当GZIP比较大,或者说针对GZIP中的数据有着较复杂的操作的情况下,执行效率往往比较低下。
一,前言
GZIP是最常见的压缩文件格式,目前DataX是支持对该压缩文件直接的读取,但每个GZIP仅仅只能启动一个线程来读取,当GZIP比较大,或者说针对GZIP中的数据有着较复杂的操作的情况下,执行效率往往比较低下。下面就讨论下如何对针对GZIP文件类型的并行读取,大幅度提高执行效率。
二,简单测试
首先对一个GZIP文件进行解压缩的测试:
$ll -h event_custom_json_201705161100.0.log.gz
-rw-r--r-- 1 weiguang.sunwg users 18M May 26 21:44 event_custom_json_201705161100.0.log.gz
该压缩文件大小为18MB,测试解压缩的时间:
$time gzip -d event_custom_json_201705161100.0.log.gz
real 0m1.879s
user 0m1.550s
sys 0m0.314s
仅仅不到2秒就完成了对该文件的解压操作,看下解压后的文件情况:
$ll -h event_custom_json_201705161100.0.log
-rw-r--r-- 1 weiguang.sunwg users 301M May 27 13:34 event_custom_json_201705161100.0.log
解压后文件大小为301MB,这个压缩率还是相当可观的。一般来说,对于结构化的数据,压缩率都比较高。这么看来,解压的效率还是很高的,不会成为性能的瓶颈,这也是我们接下来通过对GZIP文件并行读取提高整体数据同步效率的前提。试想下,如果解压缩的操作非常耗时,那么并行读取意义就不大了。
$wc -l event_custom_json_201705161100.0.log
628410 event_custom_json_201705161100.0.log
该文件中共有将近63万条记录。
三,并行读取
1,拆分规则
并行读取的前提是读取任务可以拆分,对于类似上面这样结构化的文件通过记录数来拆分是最自然的想法。例如,上面的文件存在628410行记录,启动10个并行的话,那么每个线程读取62841行记录即可。但实际操作上却非常困难,针对一个大文件,想要准确定位到某一行绝对是件效率不高的操作。
记录数不行,那就通过数据量(偏移量)吧,上面的文件解压缩后有301MB,如果启动10个并行的话,每个线程读取30MB左右的数据(不能通过压缩后的18MB来做拆分,这并不是实际数据的大小)。通过JAVA可以很容易的实现对压缩文件的流式读取,边读边解压。这时候就要求我们在读之间最好就可以知道该文件压缩前的大小。看下GZIP的压缩格式,在文件的结尾部分是保存了该信息的:
ISIZE(4 byte):这是原始数据的长度以2的32次方为模的值。GZIP中字节排列顺序是LSB方式,即Little-Endian,与ZLIB中的相反。
比较悲催的一点是,ISIZE存储的是以2的32次方为模的值,也就是说当原始文件大于4GB的时候,该值就不能准确的反应原始文件的大小了。GZIP的规范应该很久很久以前制定的,当时估计还不支持这么大的文件吧。这点先忽略吧,目前的项目中文件都是相对较小的。
我们来验证下,看看如何通过读取GZIP的文件尾,来计算原始文件大小。
[
[email protected] /home/weiguang.sunwg/sunwg/test]
$echo a > 1.txt
[
[email protected] /home/weiguang.sunwg/sunwg/test]
$cat 1.txt
a
[
[email protected] /home/weiguang.sunwg/sunwg/test]
$ll 1.txt
-rw-r--r-- 1 weiguang.sunwg users 2 May 27 13:57 1.txt
对1.txt进行压缩,并查看压缩后的文件信息:
···
[
[email protected] /home/weiguang.sunwg/sunwg/test]
$gzip 1.txt
[
[email protected] /home/weiguang.sunwg/sunwg/test]
$xxd 1.txt.gz
0000000: 1f8b 0808 5b15 2959 0003 312e 7478 7400 ....[.)Y..1.txt.
0000010: 4be4 0200 07a1 eadd 0200 0000 K...........
···
最后4个字节为0200 0000,高低位转换后为0000 0002,意思该压缩文件对应的原始文件为2个字节。
在看下前面那个压缩后为18MB的文件,文件尾如下:
1145930: 4a21 c510 92a8 5177 0c9e b3da 7858 2867 J!....Qw....xX(g
1145940: 56f3 370f 28ff 3fa8 692e bc92 7dc0 12 V.7.(.?.i...}..
最后4个字节为927d c012,高低位转换后为12c0 7d92,转换为10进制为314604946,即为解压后文件大小。
$ll event_custom_json_201705161100.0.log
-rw-r--r-- 1 weiguang.sunwg users 314604946 May 27 13:34 event_custom_json_201705161100.0.log
通过解析GZIP文件尾,可以很方便的得到该原始文件大小,根据原始文件大小可以比较方便的进行并行的拆分,并且基本保证每个并行处理的数据量差不多,避免长尾。
2,记录对齐
按数据量拆分,不能保证每个拆分点都是一行记录的结尾,所以每个并行需要进行记录对齐,保证读取的是完整的一行。对齐的规则也相当简单,开头少读半行,结尾多读半行。示意图如下:
A012.jpg
该并行读取数据头和尾分别为A0和B0,根据上面的对齐规则调整为A1和B1,保证记录对齐。其实针对其他结构化的文件都可以如此操作,只要有明确的行分隔符。
四,结束
实现了对GZIP文件的并行读取,就可以很容易的通过设置更高的并行度来提高同步的效率,更好的满足用户的需求。
原文链接: http://click.aliyun.com/m/26743/