avro格式详解
【Avro介紹】
Apache Avro是hadoop中的一個子項(xiàng)目,也是一個數(shù)據(jù)序列化系統(tǒng),其數(shù)據(jù)最終以二進(jìn)制格式,采用行式存儲的方式進(jìn)行存儲。
Avro提供了:
豐富的數(shù)據(jù)結(jié)構(gòu)
可壓縮、快速的二進(jìn)制數(shù)據(jù)格式
一個用來存儲持久化數(shù)據(jù)的容器文件
遠(yuǎn)程過程調(diào)用
與動態(tài)語言的簡單集成,代碼生成不需要讀取或?qū)懭霐?shù)據(jù)文件,也不需要使用或?qū)崿F(xiàn)RPC協(xié)議。代碼生成是一種可選的優(yōu)化,只值得在靜態(tài)類型語言中實(shí)現(xiàn)。
基于以上這些優(yōu)點(diǎn),avro在hadoop體系中被廣泛使用。除此之外,在hudi、iceberg中也都有用到avro作為元數(shù)據(jù)信息的存儲格式。
【schema】
Avro依賴"schema"(模式)來實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)的定義,schema通過json對象來進(jìn)行描述表示,具體表現(xiàn)為:
一個json字符串命名一個定義的類型
一個json對象,其格式為`{"type":"typeName" ...attributes...}`,其中`typeName`為原始類型名稱或復(fù)雜類型名稱。
一個json數(shù)組,表示嵌入類型的聯(lián)合
schema中的類型由原始類型(也就是基本類型)(null、boolean、int、long、float、double、bytes和string)和復(fù)雜類型(record、enum、array、map、union和fixed)組成。
1、原始類型
原始類型包括如下幾種:
null:沒有值
boolean:布爾類型的值
int:32位整形
long:64位整形
float:32位浮點(diǎn)
double:64位浮點(diǎn)
bytes:8位無符號類型
string:unicode字符集序列
原始類型沒有指定的屬性值,原始類型的名稱也就是定義的類型的名稱,因此,schema中的"string"等價于{"type":"string"}。
2、復(fù)雜類型
Avro支持6種復(fù)雜類型:records、enums、arrays、maps、unions和fixed。
1)Records
reocrds使用類型名稱"record",并支持以下屬性
name:提供記錄名稱的json字符串(必選)
namespace:限定名稱的json字符串
doc:一個json字符串,為用戶提供該模式的說明(可選)
aliases:字符串的json數(shù)組,為該記錄提供備用名稱
fields:一個json數(shù)組,羅列所有字段(必選),每個字段又都是一個json對象,并包含如下屬性:
name:字段的名稱(必選)
doc:字段的描述(可選)
type:一個schema,定義如上
default:字段的默認(rèn)值
order:指定字段如何影響記錄的排序順序,有效值為`"ascending"`(默認(rèn)值)、"descending"和"ignore"。
aliases:別名
一個簡單示例:
{"type": "record","name": "LongList","aliases": ["LinkedLongs"],"fields", [{"name": "value", "type": "long"},{"name": "next", "type": ["null", "LongList"]}] }2)Enums
Enum使用類型名稱"enum",并支持以下屬性
name:提供記錄名稱的json字符串(必選)
namespace:限定名稱的json字符串
aliases:字符串的json數(shù)組,為該記錄提供備用名稱
doc:一個json字符串,為用戶提供該模式的說明(可選)
symbols:一個json數(shù)組,以json字符串的形式列出符號。在枚舉中每個符號必須唯一,不能重復(fù),每個符號都必須匹配正則表達(dá)式"[A-Za-z_][A-Za-z0-9_]*"。
default:該枚舉的默認(rèn)值。
示例:
{"type": "enum","name": "Suit","symbols": ["SPADES", "HEARTS", "DIAMONDS", "CLUBS"] }3)?Arrays
item:數(shù)組中元素的schema
一個例子:聲明一個value為string的array
{"type": "array","items": "string","default": [] }4)Maps
values:map的值(value)的schema,其key被假定為字符串
一個例子:聲明一個value為long類型,(key類型為string)的map
{"type": "map","values": "long","default": {} }5)Unions
聯(lián)合使用json數(shù)組表示,例如[null, "test"]聲明一個模式,它可以是空值或字符串。
需要注意的是:當(dāng)為union類型的字段指定默認(rèn)值時,默認(rèn)值的類型必須與union第一個元素匹配,因此,對于包含"null"的union,通常先列出"null",因?yàn)榇祟愋偷膗nion的默認(rèn)值通常為空。
另外, union不能包含多個相同類型的schema,類型為record、fixed和eum除外。
6)Fixed
Fixed使用類型名稱"fixed"并支持以下屬性:
name:提供記錄名稱的json字符串(必選)
namespace:限定名稱的json字符串
aliases:字符串的json數(shù)組,為該記錄提供備用名稱
doc:一個json字符串,為用戶提供該模式的說明(可選)
size:一個整數(shù),指定每個值的字節(jié)數(shù)(必須)
例如,16字節(jié)的數(shù)可以聲明為:
{"type": "fixed","name": "md5","size": 16 }【Avro的文件存儲格式】
1、數(shù)據(jù)編碼
1)原始類型
對于null類型:不寫入內(nèi)容,即0字節(jié)長度的內(nèi)容表示;
對于boolean類型:以1字節(jié)的0或1來表示false或true;
對于int、long:以zigzag的方式編碼寫入
對于float:固定4字節(jié)長度,先通過floatToIntBits轉(zhuǎn)換為32位整數(shù),然后按小端編碼寫入。
對于double:固定8字節(jié)長度,先通過doubleToLongBits轉(zhuǎn)換為64位整型,然后按小端編碼寫入。
對于bytes:先寫入長度(采用zigzag編碼寫入),然后是對應(yīng)長度的二進(jìn)制數(shù)據(jù)內(nèi)容
對于string:同樣先寫入長度(采用zigzag編碼寫入),然后再寫入字符串對應(yīng)utf8的二進(jìn)制數(shù)據(jù)。
2)復(fù)雜類型
對于enums:只需要將enum的值所在的Index作為結(jié)果進(jìn)行編碼即可,例如,枚舉值為["A","B","C","D"],那么0就表示”A“,3表示"D"。
對于maps:被編碼為一系列的塊。每個塊由一個長整數(shù)的計數(shù)表示鍵值對的個數(shù)(采用zigzag編碼寫入),其后是多個鍵值對,計數(shù)為0的塊表示map的結(jié)束。每個元素按照各自的schema類型進(jìn)行編碼。
對于arrays:與map類似,同樣被編碼為一系列的塊,每個塊包含一個長整數(shù)的計數(shù),計數(shù)后跟具體的數(shù)組項(xiàng)內(nèi)容,最后以0計數(shù)的塊表示結(jié)束。數(shù)組項(xiàng)中的每個元素按照各自的schema類型進(jìn)行編碼。
對于unions:先寫入long類型的計數(shù)表示每個value值的位置序號(從零開始),然后再對值按對應(yīng)schema進(jìn)行編碼。
對于records:直接按照schema中的字段順序來進(jìn)行編碼。
對于fixed:使用schema中定義的字節(jié)數(shù)對實(shí)例進(jìn)行編碼。
2、存儲格式
在一個標(biāo)準(zhǔn)的avro文件中,同時存儲了schema的信息,以及對應(yīng)的數(shù)據(jù)內(nèi)容。具體格式由三部分組成:
魔數(shù)
固定4字節(jié)長度,內(nèi)容為字符'O','b','j',以及版本號標(biāo)識,通常為1。
元數(shù)據(jù)信息
文件的元數(shù)據(jù)屬性,包括schema、數(shù)據(jù)壓縮編碼方式等。整個元數(shù)據(jù)屬性以一個map的形式編碼存儲,每個屬性都以一個KV的形式存儲,屬性名對應(yīng)key,屬性值對應(yīng)value,并以字節(jié)數(shù)組的形式存儲。最后以一個固定16字節(jié)長度的隨機(jī)字符串標(biāo)識元數(shù)據(jù)的結(jié)束。
數(shù)據(jù)內(nèi)容
而數(shù)據(jù)內(nèi)容則由一個或多個數(shù)據(jù)塊構(gòu)成。每個數(shù)據(jù)塊的最前面是一個long型(按照zigzag編碼存儲)的計數(shù)表示該數(shù)據(jù)塊中實(shí)際有多少條數(shù)據(jù),后面再跟一個long型的計數(shù)表示編碼后的(N條)數(shù)據(jù)的長度,隨后就是按照編碼進(jìn)行存儲的一條條數(shù)據(jù),在每個數(shù)據(jù)塊的最后都有一個16字節(jié)長度的隨機(jī)字符串標(biāo)識塊的結(jié)束。
整體存儲內(nèi)容如下圖所示:
3、存儲格式
我們通過一個實(shí)際例子來對照分析下。
首先定義schema的內(nèi)容,具體為4個字段的表,名稱(字符串)、年齡(整型)、技能(數(shù)組)、其他(map類型),詳細(xì)如下所示:
{"type":"record","name":"person","fields": [{"name": "name","type": "string"},{"name": "age","type": "int"},{"name": "skill","type": {"type":"array","items": "string"}},{"name": "other","type": {"type": "map","values": "string"}}] }再按照上面的schema定義兩條數(shù)據(jù)(person.json):
{"name":"hncscwc","age":20,"skill":["hadoop","flink","spark","kafka"],"other":{"interests":"basketball"}} {"name":"tom","age":18, "skill":["java","scala"],"other":{}}通過avro-tools可以生成一個avro文件:
java?-jar?avro-tools-1.7.4.jar?fromjson?--schema-file?person.avsc?person.json?> person.avro通過二進(jìn)制的方式查看生成的avro文件內(nèi)容:
另外,對于一個已存在的文件,也可以通過avro-tools工具查看schema內(nèi)容、數(shù)據(jù)內(nèi)容。
【小結(jié)】
本文對avro的格式定義、編碼方式、以及實(shí)際存儲的文件格式進(jìn)行了詳細(xì)說明,最后也以一個實(shí)際例子進(jìn)行了對照說明。另外, 在官網(wǎng)中還涉及rpc的使用、mapreduce的使用,這里就沒有展開說明,有興趣的可移步官網(wǎng)進(jìn)行查閱。
好了,這就是本文的全部內(nèi)容,如果覺得本文對您有幫助,請點(diǎn)贊+轉(zhuǎn)發(fā),如果覺得有不正確的地方,也可以拍磚指點(diǎn),最后,歡迎加我微信交流~
總結(jié)
- 上一篇: DEM水文分析_提取水系
- 下一篇: WordPress商业模板avada使用