针对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的文件尾,来计算原始文件大小。

[weiguang.sunwg@buffer010101169107.et2sqa /home/weiguang.sunwg/sunwg/test]
$echo a > 1.txt

[weiguang.sunwg@buffer010101169107.et2sqa /home/weiguang.sunwg/sunwg/test]
$cat 1.txt
a

[weiguang.sunwg@buffer010101169107.et2sqa /home/weiguang.sunwg/sunwg/test]
$ll 1.txt
-rw-r--r-- 1 weiguang.sunwg users 2 May 27 13:57 1.txt

对1.txt进行压缩,并查看压缩后的文件信息:
···
[weiguang.sunwg@buffer010101169107.et2sqa /home/weiguang.sunwg/sunwg/test]
$gzip 1.txt

[weiguang.sunwg@buffer010101169107.et2sqa /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文件的并行读取,就可以很容易的通过设置更高的并行度来提高同步的效率,更好的满足用户的需求。

目录
相关文章
|
14天前
|
存储 算法 Linux
Gzip的压缩级别
【4月更文挑战第29天】压缩级别
44 1
|
14天前
|
存储 算法 Linux
Gzip的压缩级别有哪些选择?
【4月更文挑战第29天】Gzip的压缩级别有哪些选择?
35 1
|
14天前
|
算法 Linux
Gzip是一种广泛使用的文件压缩程序
【4月更文挑战第28天】Gzip是一种广泛使用的文件压缩程序
32 2
|
14天前
|
存储 C++ iOS开发
C++文件操作(文本文件的读写+二进制文件的读写)
C++文件操作(文本文件的读写+二进制文件的读写)
|
9月前
|
存储 算法 Java
解压缩流和压缩流
解压缩流和压缩流
54 0
|
iOS开发 C++
C++文件读写操作分析文本文件与二进制文件
文本文件 写文件 写文件步骤如下: 1. 包含头文件 #include <fstream> 2. 创建流对象 ofstream ofs; 3. 打开文件 ofs.open("文件路径",打开方式); 4. 写数据 ofs << "写入的数据"; 5. 关闭文件 ofs.close(); 文件打开方式: 打开方式 解释 ios::in 为读文件而打开文件 ios::out 为写文件而打开文件 ios::ate 初始位置:文件尾 ios::app 追加方式写文件 ios::trunc 如果文件存在先删除,再创建 ios::binary 二进制方式
354 0
C++文件读写操作分析文本文件与二进制文件
|
机器学习/深度学习 C#
C#使用Gzip解压缩完整读取网页内容
using System; using System.Threading; using System.Text; using System.Text.RegularExpressions; using System.
1545 0
|
C#
C#选择多个文件并读取多个文件数据
原文:C#选择多个文件并读取多个文件数据 版权声明:本文为博主原创文章,转载请附上链接地址。 https://blog.csdn.net/ld15102891672/article/details/80586097 ...
1476 0
2-SII--应用本包下文件写入和读取
零、先说一下我的IO小工具方法: 1.IO读写: IO.png 2.读取InputStream /** * 读取InputStream * * @param is 输入流 * @return ...
939 0

热门文章

最新文章