0


【大数据】详解 AVRO 格式

详解 AVRO 格式 

1.Avro 介绍

Apache Avro 是 Hadoop 中的一个子项目,也是一个数据序列化系统,其数据最终以二进制格式,采用行式存储的方式进行存储。

Avro提供了:

  • ✅ 丰富的数据结构。
  • ✅ 可压缩、快速的二进制数据格式。
  • ✅ 一个用来存储持久化数据的容器文件。
  • ✅ 远程过程调用。
  • ✅ 与动态语言的简单集成,代码生成不需要读取或写入数据文件,也不需要使用或实现 RPC 协议。代码生成是一种可选的优化,只值得在静态类型语言中实现。

基于以上这些优点,Avro 在 Hadoop 体系中被广泛使用。除此之外,在 Hudi、Iceberg 中也都有用到 Avro 作为元数据信息的存储格式。

2.schema

Avro 依赖

schema

模式)来实现数据结构的定义,

schema

通过

json

对象来进行描述表示,具体表现为:

  • 一个 json 字符串命名一个定义的类型。
  • 一个 json 对象,其格式为 {"type":"typeName" ... attributes ...},其中 typeName原始类型名称复杂类型名称
  • 一个 json 数组,表示嵌入类型的联合。
schema

中的类型由 原始类型(也就是 基本类型)(

null

boolean

int

long

float

double

bytes

string

)和 复杂类型

record

enum

array

map

union

fixed

)组成。

2.1 原始类型

原始类型包括如下几种:

  • null:没有值
  • boolean:布尔类型的值
  • int: 32 32 32 位整形
  • long: 64 64 64 位整形
  • float: 32 32 32 位浮点
  • double: 64 64 64 位浮点
  • bytes: 8 8 8 位无符号类型
  • stringunicode 字符集序列

原始类型没有指定的属性值,原始类型的名称也就是定义的类型的名称,因此,

schema

中的

"string"

等价于

{"type":"string"}

2.2 复杂类型

Avro 支持 6 种复杂类型:

records

enums

arrays

maps

unions

fixed

2.2.1 Records

reocrds

使用类型名称

"record"

,并支持以下属性

  • name:提供记录名称的 json 字符串(必选)
  • namespace:限定名称的 json 字符串
  • doc:一个 json 字符串,为用户提供该模式的说明(可选)
  • aliases:字符串的 json 数组,为该记录提供备用名称
  • fields:一个 json 数组,罗列所有字段(必选),每个字段又都是一个 json 对象,并包含如下属性: - name:字段的名称(必选)- doc:字段的描述(可选)- type:一个 schema,定义如上- default:字段的默认值- order:指定字段如何影响记录的排序顺序,有效值为 "ascending"(默认值)、"descending""ignore"。- aliases:别名

一个简单示例:

{"type":"record","name":"LongList","aliases":["LinkedLongs"],"fields",[{"name":"value","type":"long"},{"name":"next","type":["null","LongList"]}]}

2.2.2 Enums

Enum

使用类型名称

enum

,并支持以下属性

  • name:提供记录名称的 json 字符串(必选)
  • namespace:限定名称的 json 字符串
  • aliases:字符串的 json 数组,为该记录提供备用名称
  • doc:一个 json 字符串,为用户提供该模式的说明(可选)
  • symbols:一个 json 数组,以 json 字符串的形式列出符号。在枚举中每个符号必须唯一,不能重复,每个符号都必须匹配正则表达式 "[A-Za-z_][A-Za-z0-9_]*"
  • default:该枚举的默认值。

示例:

{"type":"enum",
    "name":"Suit",
    "symbols":["SPADES", "HEARTS", "DIAMONDS", "CLUBS"]}

2.2.3 Arrays

  • item:数组中元素的 schema

一个例子:声明一个

value

string

array
{"type":"array",
    "items":"string",
    "default":[]}

2.2.4 Maps

  • valuesmap 的值(value)的 schema,其 key 被假定为字符串

一个例子:声明一个

value

long

类型,(

key

类型为

string

)的

map
{"type":"map",
    "values":"long",
    "default":{}}

2.2.5 Unions

union

使用

json

数组表示,例如

[null, "test"]

声明一个模式,它可以是空值或字符串。

需要注意的是:当为

union

类型的字段指定默认值时,默认值的类型必须与

union

第一个元素匹配,因此,对于包含

"null"

union

,通常先列出

"null"

,因为此类型的

union

的默认值通常为空。

另外,

union

不能包含多个相同类型的

schema

,类型为

record

fixed

enum

除外。

2.2.6 Fixed

Fixed

使用类型名称

"fixed"

并支持以下属性:

  • name:提供记录名称的 json 字符串(必选)
  • namespace:限定名称的 json 字符串
  • aliases:字符串的 json 数组,为该记录提供备用名称
  • doc:一个 json 字符串,为用户提供该模式的说明(可选)
  • size:一个整数,指定每个值的字节数(必须)

例如,16 字节的数可以声明为:

{"type":"fixed",
    "name":"md5",
    "size":16}

3.Avro 的文件存储格式

3.1 数据编码

3.1.1 原始类型

  • 对于 null 类型:不写入内容,即 0 字节长度的内容表示。
  • 对于 boolean 类型:以 1 字节的 0 或 1 来表示 falsetrue
  • 对于 intlong:以 zigzag 的方式编码写入。
  • 对于 float:固定 4 字节长度,先通过 floatToIntBits 转换 32 位整数,然后按小端编码写入。
  • 对于 double:固定 8 字节长度,先通过 doubleToLongBits 转换为 64 位整型,然后按小端编码写入。
  • 对于 bytes:先写入长度(采用 zigzag 编码写入),然后是对应长度的二进制数据内容。
  • 对于 string:同样先写入长度(采用 zigzag 编码写入),然后再写入字符串对应 utf8 的二进制数据。

3.1.2 复杂类型

  • 对于 enums:只需要将 enum 的值所在的 Index 作为结果进行编码即可,例如,枚举值为 ["A","B","C","D"],那么 0 就表示 "A",3 表示 "D"
  • 对于 maps:被编码为一系列的块。每个块由一个长整数的计数表示键值对的个数(采用 zigzag 编码写入),其后是多个键值对,计数为 0 的块表示 map 的结束。每个元素按照各自的 schema 类型进行编码。
  • 对于 arrays:与 map 类似,同样被编码为一系列的块,每个块包含一个长整数的计数,计数后跟具体的数组项内容,最后以 0 计数的块表示结束。数组项中的每个元素按照各自的 schema 类型进行编码。
  • 对于 unions:先写入 long 类型的计数表示每个 value 值的位置序号(从零开始),然后再对值按对应 schema 进行编码。
  • 对于 records:直接按照 schema 中的字段顺序来进行编码。
  • 对于 fixed:使用 schema 中定义的字节数对实例进行编码。

3.2 存储格式

在一个标准的

avro

文件中,同时存储了

schema

的信息,以及对应的数据内容。具体格式由三部分组成:

  • 魔数:固定 4 字节长度,内容为字符 'O''b''j',以及版本号标识,通常为 1 1 1。
  • 元数据信息:文件的元数据属性,包括 schema、数据压缩编码方式等。整个元数据属性以一个 map 的形式编码存储,每个属性都以一个 KV 的形式存储,属性名对应 key,属性值对应 value,并以字节数组的形式存储。最后以一个固定 16 字节长度的随机字符串标识元数据的结束。
  • 数据内容:而数据内容则由一个或多个数据块构成。每个数据块的最前面是一个 long 型(按照 zigzag 编码存储)的计数表示该数据块中实际有多少条数据,后面再跟一个 long 型的计数表示编码后的( N N N 条)数据的长度,随后就是按照编码进行存储的一条条数据,在每个数据块的最后都有一个 16 字节长度的随机字符串标识块的结束。

整体存储内容如下图所示:
在这里插入图片描述

3.3 存储格式

我们通过一个实际例子来对照分析下。

首先定义

schema

的内容,具体为 4 个字段的表,名称(字符串)、年龄(整型)、技能(数组)、其他(

map

类型),详细如下所示:

{"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

定义两条数据(

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

通过二进制的方式查看生成的

avro

文件内容:
在这里插入图片描述
另外,对于一个已存在的文件,也可以通过

avro-tools

工具查看

schema

内容、数据内容。

[root@localhost avro]$ java-jar avro-tools-1.7.4.jar getschema ./person.avro
{"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"}}]}
[root@localhost avro]$ java-jar avro-tools-1.7.4.jar tojson ./person.avro
{"name":"hncscwc","age":20,"skill":["hadoop","flink","spark","kafka"],"other":{"interests":"basketball"}}{"name":"tom","age":18,"skill":["java","scala"],"other":{}}

4.小结

本文对

avro

的格式定义、编码方式、以及实际存储的文件格式进行了详细说明,最后也以一个实际例子进行了对照说明。另外, 在官网中还涉及

rpc

的使用、

mapreduce

的使用,这里就没有展开说明,有兴趣的可移步官网进行查阅。


本文转载自: https://blog.csdn.net/be_racle/article/details/134961707
版权归原作者 G皮T 所有, 如有侵权,请联系我们删除。

“【大数据】详解 AVRO 格式”的评论:

还没有评论