序列化是指將結(jié)構(gòu)化對(duì)象轉(zhuǎn)化為字節(jié)流以便在網(wǎng)絡(luò)上傳輸或?qū)懙酱疟P進(jìn)行永久存儲(chǔ)的過(guò)程。反序列化是指將字節(jié)流轉(zhuǎn)回結(jié)構(gòu)化對(duì)象的逆過(guò)程。
序列化用于分布式數(shù)據(jù)處理的兩大領(lǐng)域:進(jìn)程間通信和永久存儲(chǔ)
在Hadoop中,系統(tǒng)中多個(gè)節(jié)點(diǎn)進(jìn)程間的通信是通過(guò)“遠(yuǎn)程過(guò)程調(diào)用”(RPC)實(shí)現(xiàn)的。RPC協(xié)議將消息序列化成二進(jìn)制流后發(fā)送到遠(yuǎn)程節(jié)點(diǎn),遠(yuǎn)程節(jié)點(diǎn)接著將二進(jìn)制流反序列化為原始消息。通常情況下,RPC序列化格式如下:
1.緊湊
緊湊格式能充分利用網(wǎng)絡(luò)帶寬(數(shù)據(jù)中心最稀缺的資源)
2.快速
進(jìn)程間通信形成了分布式系統(tǒng)的骨架,所以需要盡量減少序列化和反序列化的性能開(kāi)銷,這是基本的。
3.可擴(kuò)展
為了滿足新的需求,協(xié)議不斷變化。所以在控制客戶端和服務(wù)期的過(guò)程中,需要直接引進(jìn)相應(yīng)的協(xié)議。例如,需要能夠在方法調(diào)用的過(guò)程中增加新的參數(shù),并且新的服務(wù)器需要能夠接受來(lái)自老客戶端的老格式的消息(無(wú)新增的參數(shù))。
4.支持互操作
對(duì)于系統(tǒng)來(lái)說(shuō),希望能夠支持以不同語(yǔ)言寫的客戶端與服務(wù)器交互,所以需要設(shè)計(jì)一種特定的格式來(lái)滿足這一需求。
Writable 接口
Writable 接口定義了兩個(gè)方法:一個(gè)將其狀態(tài)寫入 DataOutput 二進(jìn)制流,另一個(gè)從 DataInput二進(jìn)制流讀取狀態(tài)。
BytesWritable
BytesWritable 是對(duì)二進(jìn)制數(shù)據(jù)數(shù)組的封裝。它的序列化格式為一個(gè)指定所含數(shù)據(jù)字節(jié)數(shù)的整數(shù)域(4字節(jié)),后跟數(shù)據(jù)內(nèi)容的本身。例如,長(zhǎng)度為2的字節(jié)數(shù)組包含數(shù)值3和5,序列化形式為一個(gè)4字節(jié)的整數(shù)(00000002)和該數(shù)組中的兩個(gè)字節(jié)(03和05)
NullWritable
NullWritable 是 writable 的特殊類型,它的序列化長(zhǎng)度為0。它并不從數(shù)據(jù)流中讀取數(shù)據(jù),也不寫入數(shù)據(jù)。它充當(dāng)占位符。
ObjectWritable和GenericWritable
ObjectWritable 是對(duì) java 基本類型(String,enum,Writable,null或這些類型組成的數(shù)組)的一個(gè)通用封裝。它在 Hadoop RPC 中用于對(duì)方法的參數(shù)和返回類型進(jìn)行封裝和解封裝。
Wriable集合類
io 軟件包共有6個(gè) Writable 集合類,分別是 ArrayWritable,ArrayPrimitiveWritable,TwoDArrayWritable,MapWritable,SortedMapWritable以及EnumMapWritable
ArrayWritable 和 TwoDArrayWritable 是對(duì) Writeble 的數(shù)組和兩維數(shù)組(數(shù)組的數(shù)組)的實(shí)現(xiàn)。ArrayWritable 或 TwoDArrayWritable 中所有元素必須是同一類的實(shí)例。ArrayWritable 和 TwoDArrayWritable 都有g(shù)et() ,set() 和 toArray()方法,toArray() 方法用于新建該數(shù)組的一個(gè)淺拷貝。
ArrayPrimitiveWritable 是對(duì) Java 基本數(shù)組類型的一個(gè)封裝。調(diào)用 set() 方法時(shí),可以識(shí)別相應(yīng)組件類型,因而無(wú)需通過(guò)繼承該類來(lái)設(shè)置類型。
序列化框架
盡管大多數(shù) Mapreduce 程序使用的都是 Writable 類型的鍵和值,但這并不是 MapReduce API 強(qiáng)制要求使用的。事實(shí)上,可以使用任何類型,只要能有一個(gè)機(jī)制對(duì)每個(gè)類型進(jìn)行類型與二進(jìn)制表示的來(lái)回轉(zhuǎn)換就可以。
為了支持這個(gè)機(jī)制,Hadoop 有一個(gè)針對(duì)可替換序列化框架的 API 。序列化框架用一個(gè) Serialization 實(shí)現(xiàn)來(lái)表示。Serialization 對(duì)象定義了從類型到 Serializer 實(shí)例(將對(duì)象轉(zhuǎn)換為字節(jié)流)和 Deserializer 實(shí)例(將字節(jié)流轉(zhuǎn)換為對(duì)象)的映射方式。
序列化IDL
還有許多其他序列化框架從不同的角度來(lái)解決問(wèn)題:不通過(guò)代碼來(lái)定義類型,而是使用接口定義語(yǔ)言以不依賴與具體語(yǔ)言的方式進(jìn)行聲明。由此,系統(tǒng)能夠?yàn)槠渌Z(yǔ)言生成模型,這種形式能有效提高互操作能力。它們一般還會(huì)定義版本控制方案。
兩個(gè)比較流行的序列化框架 Apache Thrift 和Google的Protocol Buffers,常常用作二進(jìn)制數(shù)據(jù)的永久存儲(chǔ)格式。Mapreduce 格式對(duì)該類的支持有限,但在 Hadoop 內(nèi)部,部分組件仍使用上述兩個(gè)序列化框架來(lái)實(shí)現(xiàn) RPC 和數(shù)據(jù)交換。
基于文件的數(shù)據(jù)結(jié)構(gòu)
對(duì)于某些應(yīng)用,我們需要一種特殊的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)自己的數(shù)據(jù)。對(duì)于基于 Mapreduce 的數(shù)據(jù)處理,將每個(gè)二進(jìn)制數(shù)據(jù)大對(duì)象單獨(dú)放在各自的文件中不能實(shí)現(xiàn)可擴(kuò)展性,所以 Hadoop 為此開(kāi)發(fā)了很多更高層次的容器。
關(guān)于 SequenceFile 。
考慮日志文件,其中每一行文本代表一條日志記錄。純文本不適合記錄二進(jìn)制類型的數(shù)據(jù)。在這種情況下,Hadoop 的 SequenceFile 類非常合適,為二進(jìn)制鍵值對(duì)提供了一個(gè)持久數(shù)據(jù)結(jié)構(gòu)。將它作為日志文件的存儲(chǔ)格式時(shí),你可以自己選擇鍵,以及值可以是 Writable 類型。
SequenceFile 也可以作為小文件的容器。HDFS和Mapreduce 是針對(duì)大文件優(yōu)化的,所以通過(guò) SequenceFile 類型將小文件包裝起來(lái),可以獲得更高效率的存儲(chǔ)和處理。
SequnceFile的寫操作
通過(guò) createWriter()靜態(tài)方法可以創(chuàng)建 SequenceFile 對(duì)象,并返回 SequnceFile.Writer 實(shí)例。該靜態(tài)方法有多個(gè)重載版本,但都需要制定待寫入的數(shù)據(jù)流,Configuration 對(duì)象,以及鍵和值的類型。存儲(chǔ)在 SequenceFIle 中的鍵和值并不一定是 Writable 類型。只要能夠被 Sertialization 序列化和反序列化,任何類型都可以。
SequenceFile的讀操作
從頭到尾讀取順序文件不外乎創(chuàng)建 SequenceFile.reader 實(shí)例后反復(fù)調(diào)用 next() 方法迭代讀取記錄。讀取的是哪條記錄與使用的序列化框架有關(guān)。如果使用的是 Writable 類型,那么通過(guò)鍵和值作為參數(shù)的 next() 方法可以將數(shù)據(jù)流的下一條鍵值對(duì)讀入變量中。
1.通過(guò)命令行接口顯示 SequenceFile。
hadoop fs 命令有一個(gè) -text 選項(xiàng)可以以文本形式顯示順序文件。該選項(xiàng)可以查看文件的代碼,由此檢測(cè)出文件的類型并將其轉(zhuǎn)換為相應(yīng)的文本。該選項(xiàng)可以識(shí)別 gzip 壓縮文件,順序文件和 Avro 數(shù)據(jù)文件;否則,假設(shè)輸入為純文本文件。
2. SequenceFile 的排序和合并。
Mapreduce 是對(duì)多個(gè)順序文件進(jìn)行排序(或合并)最有效的方法。Mapreduce 本身是并行的,并且可由你制定使用多少個(gè) reducer 。例如,通過(guò)制定一個(gè) reducer ,可以得到一個(gè)輸出文件。
3.SequenceFile 的格式。
順序文件由文件頭和隨后的一條或多條記錄組成。順序文件的前三個(gè)字節(jié)為 SEQ,緊隨其后的一個(gè)字節(jié)表示順序文件的版本號(hào)。文件頭還包括其他字段,例如鍵和值的名稱,數(shù)據(jù)壓縮細(xì)節(jié),用戶定義的元數(shù)據(jù)以及同步標(biāo)識(shí)。同步標(biāo)識(shí)用于在讀取文件時(shí)能夠從任意位置開(kāi)始識(shí)別記錄邊界。每個(gè)文件都有一個(gè)隨機(jī)生成的同步標(biāo)識(shí),其值存儲(chǔ)在文件頭中,位于順序文件中的記錄與記錄之間。同步標(biāo)識(shí)的額外存儲(chǔ)開(kāi)銷要求小于1%,所以沒(méi)有必要在每條記錄末尾添加該標(biāo)識(shí)。
關(guān)于MapFile
MapFile 是已經(jīng)排過(guò)序的 SequenceFile ,它有索引,所以可以按鍵查找。索引本身就是一個(gè) SequenceFile ,包含 map 中的一小部分鍵。由于索引能夠加載進(jìn)內(nèi)存,因此可以提供對(duì)主數(shù)據(jù)文件的快速查找。主數(shù)據(jù)文件則是另一個(gè) SequenceFIle ,包含了所有的 map 條目,這些條目都按照鍵順序進(jìn)行了排序。
其他文件格式和面向列的格式
順序文件和 map 文件是 Hadoop 中最早的,但并不是僅有的二進(jìn)制文件格式,事實(shí)上,對(duì)于新項(xiàng)目而言,有更好的二進(jìn)制格式可供選擇。
Avro 數(shù)據(jù)文件在某些方面類似順序文件,是面向大規(guī)模數(shù)據(jù)處理而設(shè)計(jì)的。但是 Avro 數(shù)據(jù)文件又是可移植的,它們可以跨越不同的編程語(yǔ)言使用。順序文件,map 文件和 Avro 數(shù)據(jù)文件都是面向行的格式,意味著每一行的值在文件中是連續(xù)存儲(chǔ)的。在面向列的格式中,文件中的行被分割成行的分片,然后每個(gè)分片以面向列的形式存儲(chǔ):首先存儲(chǔ)每行第一列的值,然后是每行第2列的值,如此以往。
能夠減少磁盤的占用空間和網(wǎng)絡(luò)傳輸?shù)牧浚⒓铀贁?shù)據(jù)在網(wǎng)絡(luò)和磁盤上的傳輸。
Hadoop 應(yīng)用處理的數(shù)據(jù)集非常大,因此需要借助于壓縮。使用哪種壓縮格式與待處理的文件的大小,格式和所用的工具有關(guān)。比較各種壓縮算法的壓縮比和性能(從高到低):
1. 使用容器文件格式,例如順序文件, Avro 數(shù)據(jù)文件。 ORCF 了說(shuō) Parquet 文件
2. 使用支持切分的壓縮格式,例如 bzip2 或者通過(guò)索引實(shí)現(xiàn)切分的壓縮格式,例子如LZO。
3. 在應(yīng)用中將文件中切分成塊,并使用任意一種他所格式為每個(gè)數(shù)據(jù)塊建立壓縮文件(不論它是否支持切分)。在這種情況下,需要合理選擇數(shù)據(jù)大小,以確保壓縮后的數(shù)據(jù)塊的大小近似于HDFS塊的大小。
4. 存儲(chǔ)未經(jīng)壓縮的文件。
重點(diǎn):壓縮和拆分一般是沖突的(壓縮后的文件的 block 是不能很好地拆分獨(dú)立運(yùn)行,很多時(shí)候某個(gè)文件的拆分點(diǎn)是被拆分到兩個(gè)壓縮文件中,這時(shí) Map 任務(wù)就無(wú)法處理,所以對(duì)于這些壓縮,Hadoop 往往是直接使用一個(gè) Map 任務(wù)處理整個(gè)文件的分析)。對(duì)大文件不可使用不支持切分整個(gè)文件的壓縮格式,會(huì)失去數(shù)據(jù)的特性,從而造成 Mapreduce 應(yīng)用效率低下。
Map 的輸出結(jié)果也可以進(jìn)行壓縮,這樣可以減少 Map 結(jié)果到 Reduce 的傳輸?shù)臄?shù)據(jù)量,加快傳輸速率。
在 Mapreduce 中使用壓縮
FileOutputFormat.setCompressOutput(job,true);
FileOutputFormat.setOutputCompressorClass(job,GzipCodec.class);
如果輸出生成順序文件,可以設(shè)置 mapreduce.output.fileoutputformat.compress.type 屬性來(lái)控制限制使用壓縮格式。默認(rèn)值是RECORD,即針對(duì)每條記錄進(jìn)行壓縮。如果將其改為BLOCK,將針對(duì)一組記錄進(jìn)行壓縮,這是推薦的壓縮策略,因?yàn)樗膲嚎s效率更高。
更多建議: