大數據之數據交換和存儲序列化利器 Avro

Avro(讀音類似於[ævrə])是Hadoop的一個子項目,由Hadoop的創始人Doug Cutting(也是Lucene,Nutch等項目的創始人)牽頭開發。Avro是一個基於二進制數據傳輸高性能的中間件。在Hadoop的其他項目中例如HBase(Ref)和Hive(Ref)的Client端與服務端的數據傳輸也採用了這個工具。Avro是一個數據序列化的系統。Avro可以將數據結構或對象轉化成便於存儲或傳輸的格式。Avro設計之初就用來支持數據密集型應用,適合於遠程或本地大規模數據的存儲和交換。它的主要特點有:

  • 豐富的數據結構類型;

  • 快速可壓縮的二進制數據形式,對數據二進制序列化後可以節約數據存儲空間和網絡傳輸帶寬;

  • 存儲持久數據的文件容器;

  • 可以實現遠程過程調用RPC;

  • 簡單的動態語言結合功能。

當前市場上有很多類似的序列化系統,如Google的Protocol Buffers, Facebook的Thrift。Avro提供着與諸如Thrift和Protocol Buffers等系統相似的功能,但是在一些基礎方面還是有區別的,主要是:

  • 動態類型: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字節同步標記符。文檔中指出當前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框架來使用。當在RPC中使用Avro時,服務器和客戶端可以在握手連接時交換模式。服務器和客戶端有彼此全部的模式,因此相同命名字段、缺失字段和多餘字段等信息之間通信中需要處理的一致性問題就可以容易解決。如圖所示,協議中定義了用於傳輸的消息,消息使用框架後放入緩衝區中進行傳輸,由於傳輸的初始就交換了各自的協議定義,因此即使傳輸雙方使用的協議不同所傳輸的數據也能夠正確解析。

Avro作爲RPC框架來使用。客戶端希望同服務器端交互時,就需要交換雙方通信的協議,它類似於模式,需要雙方來定義,在Avro中被稱爲消息(Message)。通信雙方都必須保持這種協議,以便於解析從對方發送過來的數據,這也就是傳說中的握手階段。

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

每個緩衝區以四個字節開頭,中間是多個字節的緩衝數據,最後以一個空緩衝區結尾。這種機制的好處在於,發送端在發送數據時可以很方便地組裝不同數據源的數據,接收方也可以將數據存入不同的存儲區。還有,當往緩衝區中寫數據時,大對象可以獨佔一個緩衝區,而不是與其它小對象混合存放,便於接收方方便地讀取大對象。對象容器文件是Avro的數據存儲的具體實現,數據交換則由RPC服務提供,與對象容器文件類似,數據交換也完全依賴Schema,所以與Hadoop目前的RPC不同,Avro在數據交換之前需要通過握手過程先交換Schema。

參考資料:

  • 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/

 

往期推薦

1、Spark速度比MapReduce快,不僅是內存計算

2、乾貨 | Kafka 內核知識梳理,附思維導圖

3、MapReduce Shuffle 和 Spark Shuffle 結業篇

4、實時數倉 | 你想要的數倉分層設計與技術選型

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章