[Hadoop]轉載-avro資料序列化簡介

大搜車-自娛發表於2012-07-20
Avro(讀音類似於[ævrə])是Hadoop的一個子專案,由Hadoop的創始人Doug Cutting(也是Lucene,Nutch等專案的創始人,膜拜)牽頭開發,當前最新版本1.3.3。Avro是一個資料序列化系統,設計用於支援大批量資料交換的應用。它的主要特點有:支援二進位制序列化方式,可以便捷,快速地處理大量資料;動態語言友好,Avro提供的機制使動態語言可以方便地處理Avro資料。

當前市場上有很多類似的序列化系統,如Google的Protocol Buffers, Facebook的Thrift。這些系統反響良好,完全可以滿足普通應用的需求。針對重複開發的疑惑,Doug Cutting撰文解釋道:Hadoop現存的RPC系統遇到一些問題,如效能瓶頸(當前採用IPC系統,它使用Java自帶的DataOutputStream和DataInputStream);需要伺服器端和客戶端必須執行相同版本的Hadoop;只能使用Java開發等。但現存的這些序列化系統自身也有毛病,以Protocol Buffers為例,它需要使用者先定義資料結構,然後根據這個資料結構生成程式碼,再組裝資料。如果需要操作多個資料來源的資料集,那麼需要定義多套資料結構並重復執行多次上面的流程,這樣就不能對任意資料集做統一處理。其次,對於Hadoop中Hive和Pig這樣的指令碼系統來說,使用程式碼生成是不合理的。並且Protocol Buffers在序列化時考慮到資料定義與資料可能不完全匹配,在資料中新增註解,這會讓資料變得龐大並拖慢處理速度。其它序列化系統有如Protocol Buffers類似的問題。所以為了Hadoop的前途考慮,Doug Cutting主導開發一套全新的序列化系統,這就是Avro,於09年加入Hadoop專案族中。

上面通過與Protocol Buffers的對比,大致清楚了Avro的特長。下面著重關注Avro的細節部分。

Avro依賴模式(Schema)來實現資料結構定義。可以把模式理解為Java的類,它定義每個例項的結構,可以包含哪些屬性。可以根據類來產生任意多個例項物件。對例項序列化操作時必須需要知道它的基本結構,也就需要參考類的資訊。這裡,根據模式產生的Avro物件類似於類的例項物件。每次序列化/反序列化時都需要知道模式的具體結構。所以,在Avro可用的一些場景下,如檔案儲存或是網路通訊,都需要模式與資料同時存在。Avro資料以模式來讀和寫(檔案或是網路),並且寫入的資料都不需要加入其它標識,這樣序列化時速度快且結果內容少。由於程式可以直接根據模式來處理資料,所以Avro更適合於指令碼語言的發揮。

Avro的模式主要由JSON物件來表示,它可能會有一些特定的屬性,用來描述某種型別(Type)的不同形式。Avro支援八種基本型別(Primitive Type)和六種混合型別(Complex Type)。基本型別可以由JSON字串來表示。每種不同的混合型別有不同的屬性(Attribute)來定義,有些屬性是必須的,有些是可選的,如果需要的話,可以用JSON陣列來存放多個JSON物件定義。在這幾種Avro定義的型別的支援下,可以由使用者來創造出豐富的資料結構來,支援使用者紛繁複雜的資料。

Avro支援兩種序列化編碼方式:二進位制編碼和JSON編碼。使用二進位制編碼會高效序列化,並且序列化後得到的結果會比較小;而JSON一般用於除錯系統或是基於WEB的應用。對Avro資料序列化/反序列化時都需要對模式以深度優先(Depth-First),從左到右(Left-to-Right)的遍歷順序來執行。基本型別的序列化容易解決,混合型別的序列化會有很多不同規則。對於基本型別和混合型別的二進位制編碼在文件中規定,按照模式的解析順序依次排列位元組。對於JSON編碼,聯合型別(Union Type)就與其它混合型別表現不一致。

Avro為了便於MapReduce的處理定義了一種容器檔案格式(Container File Format)。這樣的檔案中只能有一種模式,所有需要存入這個檔案的物件都需要按照這種模式以二進位制編碼的形式寫入。物件在檔案中以塊(Block)來組織,並且這些物件都是可以被壓縮的。塊和塊之間會存在同步標記符(Synchronization Marker),以便MapReduce方便地切割檔案用於處理。下圖是根據文件描述畫出的檔案結構圖:

上圖已經對各塊做肢解操作,但還是有必要再詳細說明下。一個儲存檔案由兩部分組成:頭資訊(Header)和資料塊(Data Block)。而頭資訊又由三部分構成:四個位元組的字首(類似於Magic Number),檔案Meta-data資訊和隨機生成的16位元組同步標記符。這裡的Meta-data資訊讓人有些疑惑,它除了檔案的模式外,還能包含什麼。文件中指出當前Avro認定的就兩個Meta-data:schema和codec。這裡的codec表示對後面的檔案資料塊(File Data Block)採用何種壓縮方式。Avro的實現都需要支援下面兩種壓縮方式:null(不壓縮)和deflate(使用Deflate演算法壓縮資料塊)。除了文件中認定的兩種Meta-data,使用者還可以自定義適用於自己的Meta-data。這裡用long型來表示有多少個Meta-data資料對,也是讓使用者在實際應用中可以定義足夠的Meta-data資訊。對於每對Meta-data資訊,都有一個string型的key(需要以“avro.”為字首)和二進位制編碼後的value。對於檔案中頭資訊之後的每個資料塊,有這樣的結構:一個long值記錄當前塊有多少個物件,一個long值用於記錄當前塊經過壓縮後的位元組數,真正的序列化物件和16位元組長度的同步標記符。由於物件可以組織成不同的塊,使用時就可以不經過反序列化而對某個資料塊進行操作。還可以由資料塊數,物件數和同步標記符來定位損壞的塊以確保資料完整性。

上面是將Avro物件序列化到檔案的操作。與之相應的,Avro也被作為一種RPC框架來使用。客戶端希望同伺服器端互動時,就需要交換雙方通訊的協議,它類似於模式,需要雙方來定義,在Avro中被稱為訊息(Message)。通訊雙方都必須保持這種協議,以便於解析從對方傳送過來的資料,這也就是傳說中的握手階段。

訊息從客戶端傳送到伺服器端需要經過傳輸層(Transport Layer),它傳送訊息並接收伺服器端的響應。到達傳輸層的資料就是二進位制資料。通常以HTTP作為傳輸模型,資料以POST方式傳送到對方去。在Avro中,它的訊息被封裝成為一組緩衝區(Buffer),類似於下圖的模型:



如上圖,每個緩衝區以四個位元組開頭,中間是多個位元組的緩衝資料,最後以一個空緩衝區結尾。這種機制的好處在於,傳送端在傳送資料時可以很方便地組裝不同資料來源的資料,接收方也可以將資料存入不同的儲存區。還有,當往緩衝區中寫資料時,大物件可以獨佔一個緩衝區,而不是與其它小物件混合存放,便於接收方方便地讀取大物件。

下面聊下Avro的其它方面資訊。前文中引述Doug Cutting的話說,Protocol Buffer在傳輸資料時,往資料中加入註釋(annotation),以應對資料結構與資料不匹配的問題。但直接導致資料量變大,解析困難等缺點。那Avro是如何應對模式與資料的不同呢?為了保證Avro的高效,假定模式至少大部分是匹配的,然後定義一些驗證規則,如果在規則滿足的前提下,做資料驗證。如果模式不匹配就會報錯。相同模式,互動資料時,如果資料中缺少某個域(field),用規範中的預設值設定;如果資料中多了些與模式不匹配的資料。則忽視這些值。

Avro列出的優點中還有一項是:可排序的。就是說,一種語言支援的Avro程式在序列化資料後,可由其它語言的Avro程式對未反序列化的資料排序。我不知道這種機制是在什麼樣的場景下使用,但看起來還是挺不錯的。

當前關於Avro的資料挺少的,上面的文章也是我由官方文件和作者的文章來總結的。我相信其中肯定有很多錯誤,或許有些方面根本就理解錯了。現在放出這篇總結,便於不斷修訂和補充,也是對這兩天學習成果的分享,希望對想了解Avro的人有些許幫助,更希望大家指證我理解錯誤的地方,利於提高。


其它資料:
Avro規範:http://avro.apache.org/docs/current/spec.html
Doug Cutting文章:http://www.cloudera.com/blog/2009/11/avro-a-new-format-for-data-interchange/
各序列化系統效能比較:http://wiki.github.com/eishay/jvm-serializers/


文章出處:http://langyu.iteye.com/blog/708568

相關文章