針對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,記錄對齊
按數據量拆分,不能保證每個拆分點都是一行記錄的結尾,所以每個並行需要進行記錄對齊,保證讀取的是完整的一行。對齊的規則也相當簡單,開頭少讀半行,結尾多讀半行。示意圖如下:
該並行讀取數據頭和尾分別為A0和B0,根據上麵的對齊規則調整為A1和B1,保證記錄對齊。其實針對其他結構化的文件都可以如此操作,隻要有明確的行分隔符。
四,結束
實現了對GZIP文件的並行讀取,就可以很容易的通過設置更高的並行度來提高同步的效率,更好的滿足用戶的需求。
最後更新:2017-07-25 11:02:52