Apache Avro
Avro是一個(gè)數(shù)據(jù)序列化系統(tǒng),設(shè)計(jì)用于支持大批量數(shù)據(jù)交換的應(yīng)用。
它的主要特點(diǎn)有:支持二進(jìn)制序列化方式,可以便捷,快速地處理大量數(shù)據(jù);動(dòng)態(tài)語言友好,Avro提供的機(jī)制使動(dòng)態(tài)語言可以方便地處理Avro數(shù)據(jù)。
當(dāng)前市場(chǎng)上有很多類似的序列化系統(tǒng),如Google的Protocol Buffers, Facebook的Thrift。這些系統(tǒng)反響良好,完全可以滿足普通應(yīng)用的需求。針對(duì)重復(fù)開發(fā)的疑惑,Doug Cutting撰文解釋道:Hadoop現(xiàn)存的RPC系統(tǒng)遇到一些問題,如性能瓶頸(當(dāng)前采用IPC系統(tǒng),它使用Java自帶的DataOutputStream和DataInputStream);需要服務(wù)器端和客戶端必須運(yùn)行相同版本的Hadoop;只能使用Java開發(fā)等。但現(xiàn)存的這些序列化系統(tǒng)自身也有毛病,以Protocol Buffers為例,它需要用戶先定義數(shù)據(jù)結(jié)構(gòu),然后根據(jù)這個(gè)數(shù)據(jù)結(jié)構(gòu)生成代碼,再組裝數(shù)據(jù)。如果需要操作多個(gè)數(shù)據(jù)源的數(shù)據(jù)集,那么需要定義多套數(shù)據(jù)結(jié)構(gòu)并重復(fù)執(zhí)行多次上面的流程,這樣就不能對(duì)任意數(shù)據(jù)集做統(tǒng)一處理。其次,對(duì)于Hadoop中Hive和Pig這樣的腳本系統(tǒng)來說,使用代碼生成是不合理的。并且Protocol Buffers在序列化時(shí)考慮到數(shù)據(jù)定義與數(shù)據(jù)可能不完全匹配,在數(shù)據(jù)中添加注解,這會(huì)讓數(shù)據(jù)變得龐大并拖慢處理速度。其它序列化系統(tǒng)有如Protocol Buffers類似的問題。所以為了Hadoop的前途考慮,Doug Cutting主導(dǎo)開發(fā)一套全新的序列化系統(tǒng),這就是Avro,于09年加入Hadoop項(xiàng)目族中。
上面通過與Protocol Buffers的對(duì)比,大致清楚了Avro的特長(zhǎng)。下面著重關(guān)注Avro的細(xì)節(jié)部分。
Avro依賴模式(Schema)來實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)定義??梢园涯J嚼斫鉃镴ava的類,它定義每個(gè)實(shí)例的結(jié)構(gòu),可以包含哪些屬性??梢愿鶕?jù)類來產(chǎn)生任意多個(gè)實(shí)例對(duì)象。對(duì)實(shí)例序列化操作時(shí)必須需要知道它的基本結(jié)構(gòu),也就需要參考類的信息。這里,根據(jù)模式產(chǎn)生的Avro對(duì)象類似于類的實(shí)例對(duì)象。每次序列化/反序列化時(shí)都需要知道模式的具體結(jié)構(gòu)。所以,在Avro可用的一些場(chǎng)景下,如文件存儲(chǔ)或是網(wǎng)絡(luò)通信,都需要模式與數(shù)據(jù)同時(shí)存在。Avro數(shù)據(jù)以模式來讀和寫(文件或是網(wǎng)絡(luò)),并且寫入的數(shù)據(jù)都不需要加入其它標(biāo)識(shí),這樣序列化時(shí)速度快且結(jié)果內(nèi)容少。由于程序可以直接根據(jù)模式來處理數(shù)據(jù),所以Avro更適合于腳本語言的發(fā)揮。
Avro的模式主要由JSON對(duì)象來表示,它可能會(huì)有一些特定的屬性,用來描述某種類型(Type)的不同形式。Avro支持八種基本類型(Primitive Type)和六種混合類型(Complex Type)。基本類型可以由JSON字符串來表示。每種不同的混合類型有不同的屬性(Attribute)來定義,有些屬性是必須的,有些是可選的,如果需要的話,可以用JSON數(shù)組來存放多個(gè)JSON對(duì)象定義。在這幾種Avro定義的類型的支持下,可以由用戶來創(chuàng)造出豐富的數(shù)據(jù)結(jié)構(gòu)來,支持用戶紛繁復(fù)雜的數(shù)據(jù)。
Avro支持兩種序列化編碼方式:二進(jìn)制編碼和JSON編碼。使用二進(jìn)制編碼會(huì)高效序列化,并且序列化后得到的結(jié)果會(huì)比較小;而JSON一般用于調(diào)試系統(tǒng)或是基于WEB的應(yīng)用。對(duì)Avro數(shù)據(jù)序列化/反序列化時(shí)都需要對(duì)模式以深度優(yōu)先(Depth-First),從左到右(Left-to-Right)的遍歷順序來執(zhí)行?;绢愋偷男蛄谢菀捉鉀Q,混合類型的序列化會(huì)有很多不同規(guī)則。對(duì)于基本類型和混合類型的二進(jìn)制編碼在文檔中規(guī)定,按照模式的解析順序依次排列字節(jié)。對(duì)于JSON編碼,聯(lián)合類型(Union Type)就與其它混合類型表現(xiàn)不一致。 Avro為了便于MapReduce的處理定義了一種容器文件格式(Container File Format)。這樣的文件中只能有一種模式,所有需要存入這個(gè)文件的對(duì)象都需要按照這種模式以二進(jìn)制編碼的形式寫入。對(duì)象在文件中以塊(Block)來組織,并且這些對(duì)象都是可以被壓縮的。塊和塊之間會(huì)存在同步標(biāo)記符(Synchronization Marker),以便MapReduce方便地切割文件用于處理。下圖是根據(jù)文檔描述畫出的文件結(jié)構(gòu)圖:
點(diǎn)擊查看原始大小圖片
上圖已經(jīng)對(duì)各塊做肢解操作,但還是有必要再詳細(xì)說明下。一個(gè)存儲(chǔ)文件由兩部分組成:頭信息(Header)和數(shù)據(jù)塊(Data Block)。而頭信息又由三部分構(gòu)成:四個(gè)字節(jié)的前綴(類似于Magic Number),文件Meta-data信息和隨機(jī)生成的16字節(jié)同步標(biāo)記符。這里的Meta-data信息讓人有些疑惑,它除了文件的模式外,還能包含什么。文檔中指出當(dāng)前Avro認(rèn)定的就兩個(gè)Meta-data:schema和codec。這里的codec表示對(duì)后面的文件數(shù)據(jù)塊(File Data Block)采用何種壓縮方式。Avro的實(shí)現(xiàn)都需要支持下面兩種壓縮方式:null(不壓縮)和deflate(使用Deflate算法壓縮數(shù)據(jù)塊)。除了文檔中認(rèn)定的兩種Meta-data,用戶還可以自定義適用于自己的Meta-data。這里用long型來表示有多少個(gè)Meta-data數(shù)據(jù)對(duì),也是讓用戶在實(shí)際應(yīng)用中可以定義足夠的Meta-data信息。對(duì)于每對(duì)Meta-data信息,都有一個(gè)string型的key(需要以“avro.”為前綴)和二進(jìn)制編碼后的value。對(duì)于文件中頭信息之后的每個(gè)數(shù)據(jù)塊,有這樣的結(jié)構(gòu):一個(gè)long值記錄當(dāng)前塊有多少個(gè)對(duì)象,一個(gè)long值用于記錄當(dāng)前塊經(jīng)過壓縮后的字節(jié)數(shù),真正的序列化對(duì)象和16字節(jié)長(zhǎng)度的同步標(biāo)記符。由于對(duì)象可以組織成不同的塊,使用時(shí)就可以不經(jīng)過反序列化而對(duì)某個(gè)數(shù)據(jù)塊進(jìn)行操作。還可以由數(shù)據(jù)塊數(shù),對(duì)象數(shù)和同步標(biāo)記符來定位損壞的塊以確保數(shù)據(jù)完整性。
上面是將Avro對(duì)象序列化到文件的操作。與之相應(yīng)的,Avro也被作為一種RPC框架來使用??蛻舳讼M?wù)器端交互時(shí),就需要交換雙方通信的協(xié)議,它類似于模式,需要雙方來定義,在Avro中被稱為消息(Message)。通信雙方都必須保持這種協(xié)議,以便于解析從對(duì)方發(fā)送過來的數(shù)據(jù),這也就是傳說中的握手階段。
消息從客戶端發(fā)送到服務(wù)器端需要經(jīng)過傳輸層(Transport Layer),它發(fā)送消息并接收服務(wù)器端的響應(yīng)。到達(dá)傳輸層的數(shù)據(jù)就是二進(jìn)制數(shù)據(jù)。通常以HTTP作為傳輸模型,數(shù)據(jù)以POST方式發(fā)送到對(duì)方去。在Avro中,它的消息被封裝成為一組緩沖區(qū)(Buffer),類似于下圖的模型:
如上圖,每個(gè)緩沖區(qū)以四個(gè)字節(jié)開頭,中間是多個(gè)字節(jié)的緩沖數(shù)據(jù),最后以一個(gè)空緩沖區(qū)結(jié)尾。這種機(jī)制的好處在于,發(fā)送端在發(fā)送數(shù)據(jù)時(shí)可以很方便地組裝不同數(shù)據(jù)源的數(shù)據(jù),接收方也可以將數(shù)據(jù)存入不同的存儲(chǔ)區(qū)。還有,當(dāng)往緩沖區(qū)中寫數(shù)據(jù)時(shí),大對(duì)象可以獨(dú)占一個(gè)緩沖區(qū),而不是與其它小對(duì)象混合存放,便于接收方方便地讀取大對(duì)象。
下面聊下Avro的其它方面信息。前文中引述Doug Cutting的話說,Protocol Buffer在傳輸數(shù)據(jù)時(shí),往數(shù)據(jù)中加入注釋(annotation),以應(yīng)對(duì)數(shù)據(jù)結(jié)構(gòu)與數(shù)據(jù)不匹配的問題。但直接導(dǎo)致數(shù)據(jù)量變大,解析困難等缺點(diǎn)。那Avro是如何應(yīng)對(duì)模式與數(shù)據(jù)的不同呢?為了保證Avro的高效,假定模式至少大部分是匹配的,然后定義一些驗(yàn)證規(guī)則,如果在規(guī)則滿足的前提下,做數(shù)據(jù)驗(yàn)證。如果模式不匹配就會(huì)報(bào)錯(cuò)。相同模式,交互數(shù)據(jù)時(shí),如果數(shù)據(jù)中缺少某個(gè)域(field),用規(guī)范中的默認(rèn)值設(shè)置;如果數(shù)據(jù)中多了些與模式不匹配的數(shù)據(jù)。則忽視這些值。
Avro列出的優(yōu)點(diǎn)中還有一項(xiàng)是:可排序的。就是說,一種語言支持的Avro程序在序列化數(shù)據(jù)后,可由其它語言的Avro程序?qū)ξ捶葱蛄谢臄?shù)據(jù)排序。我不知道這種機(jī)制是在什么樣的場(chǎng)景下使用,但看起來還是挺不錯(cuò)的。 當(dāng)前關(guān)于Avro的資料挺少的,上面的文章也是我由官方文檔和作者的文章來總結(jié)的。我相信其中肯定有很多錯(cuò)誤,或許有些方面根本就理解錯(cuò)了?,F(xiàn)在放出這篇總結(jié),便于不斷修訂和補(bǔ)充,也是對(duì)這兩天學(xué)習(xí)成果的分享,希望對(duì)想了解Avro的人有些許幫助,更希望大家指證我理解錯(cuò)誤的地方,利于提高。
其它資料:
 Avro規(guī)范:http://avro.apache.org/docs/current/spec.html
 Doug Cutting文章:http://www.cloudera.com/blog/2009/11/avro-a-new-format-for-data-interchange/
 各序列化系統(tǒng)性能比較:http://wiki.github.com/eishay/jvm-serializers/
 ?
總結(jié)
以上是生活随笔為你收集整理的Apache Avro的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 嵌套的JSON数据与AVRO文件的相互转
- 下一篇: spring-boot配置readonl
