在Hive中使用Avro进行数据序列化[译]

本文将深入了解Avro在Hive中使用。在这里,我们讨论了Avro的重要性和必要性,以及如何在Hive中实现它。通过这篇文章,您将了解Avro及如何在您Hadoop项目中实现。

什么是Avro?

Avro是首选的数据序列化系统之一,因为它不限定语言。
由于在Hadoop可写类中缺乏语言可移植性,avro成为一种自然选择,因为它能够处理多种数据格式,可以由多种语言进一步处理。
Avro非常适合在Hadoop中序列化数据。
它使用JSON来定义数据类型和协议,并以紧凑的二进制格式序列化数据。它的主要用途是在Apache Hadoop中,它可以提供持久数据的序列化格式,以及用于Hadoop节点之间从客户端程序到Hadoop服务之间的通信格式。
通过这个,我们可以将Avro定义为Hadoop中的文件格式,该文件格式可以用于任何Hadoop的工具,如Pig和Hive。

在Hive中实现Avro文件格式

在我们讨论Avro文件格式具体实现之前,让我们快速介绍Avro schema及如何创建Avro记录,Hive表等等。

Avro Schema

Avro依赖于Schema。读取Avro数据时,用于写入的Schema始终存在。这允许每个数据被写入时没有额外开销,使得序列化快速又小。这也方便了它与动态脚本语言一起使用,因为数据及其Schema是完全自我描述的。
当Avro数据存储在文件中时,其Schema与其存储在一起,以便以后可以由任何程序处理文件。如果读取数据的程序需要不同的Schema,这可以很容易地解决,因为两个Schema都存在。
当在RPC中使用Avro时,客户端和服务器在连接握手的时候交换Schema。 (这可以被优化,对于大多数调用,实际上并没有传输Schema)。由于客户端和服务器都具有另一个的Schema,所以相同命名字段,缺失字段,额外字段等之间的对应可以容易地解决。
Avro Schema是使用JSON定义的。这有助于已经具有JSON库的语言的实现。使用Avro,我们可以使用Schema将非结构化和半结构化数据转换为合适的结构化数据。

创建一个表以存储avro格式的数据

此过程从创建基于JSON的Schema开始,以具有内置Schema的格式序列化数据。
Avro有自己的解析器来将提供的Schema作为对象返回。
创建的对象允许我们使用该Schema创建记录。
我们可以在创建Hive表的同时在表属性中创建我们的模式
TBLPROPERTIES (‘avro.schema.literal’=’{json schema here}’);

现在,让我们用olympic数据创建一个Avro文件格式。您可以从以下链接下载数据:
Olympic Dataset

创建Hive表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
create table olympic_avro
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat'
tblproperties ('avro.schema.literal'='{
"name": "my_record",
"type": "record",
"fields": [
{"name":"athelete", "type":"string"},
{"name":"age", "type":"int"},
{"name":"country", "type":"string"},
{"name":"year", "type":"string"},
{"name":"closing", "type":"string"},
{"name":"sport", "type":"string"},
{"name":"gold", "type":"int"},
{"name":"silver", "type":"int"},
{"name":"bronze", "type":"int"},
{"name":"total", "type":"int"}
]}');

tblproperties中,您可以看到数据的Schema。 tblproperties中的每个记录将成为一个列。这里,’Name’定义列名称,’type’定义特定列的数据类型。上述字段用于olympic数据。
我们可以使用命令describe olympic_avro查看表中的字段。
Creating+Avro_table1

我们已经成功创建了一个avro表!
以下是用于创建表的命令背后的深入解释:

ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe'

我们在创建表时使用了上面的命令。这意味着数据提供是序列化和反序列化的。如果我们有自己的序列化数据,则不需要使用这一行。否则,上面的行应该存在,其作为数据的默认序列化。

STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'

这行说,我们将文件存储为AvroContainerInputFormat,它存在于上面指定的默认hive包中。使用此行时,文件将以扩展名.avro(Avro的扩展名)存储。

OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat'

此行说,文件的输出也是Avro格式,将具有扩展名.avro。
下一步是将数据插入表中。
默认解析器将作用于Schema,并为Schema创建一个对象,从而适应适合创建的Schema的数据类型。现在,可以将数据插入使用相同模式创建的Avro表中。

将数据插入到Avro表中

有两种方法可以将数据插入到Avro表中:
1.如果我们有一个扩展名为“.avro”的文件,并且该文件的Schema与您指定的Schema相同,则可以使用命令直接导入该文件

LOAD DATA LOCAL  INPATH  'PATH OF THE FILE';

2.您可以将先前创建的表的内容复制到新创建的Avro表中。
让我们来看看第二种数据插入方法,将数据导入到Avro表中。我们将首先创建一个由制表符分隔的表。
文本文件创建

1
create tableolympic(athelete STRING,age INT,country STRING,year STRING,closing STRING,sport STRING,gold INT,silver INT,bronzeINT,total INT) row format delimited fields terminated by '\t' stored as textfile;

在这里,我们创建一个名为“olympic”的表。表的模式是指定的,输入文件中的数据由制表符空格分隔。在命令结束时,我们指定了“存储为文本文件”,这意味着我们使用的是TEXTFILE格式。可以使用命令describe olympic检查创建的表的模式;
现在,可以将数据加载到创建的表中,如下所示:
将数据从本地插入到olympic表
Creating+%26+loading+data+textfile_table

我们已经成功地将数据导入到TEXTFILE格式的表中。
现在,要将数据从“olympic”表复制到新创建的Avro表,使用该命令

1
insert overwrite table olympic_avro select * from olympic limit 20

copying+limited+data+from+textfile+to+avrofile

这个olympic数据有一些NULL值,Avro架构无法在默认情况下处理空值。要使Avro表使用NULL值,需要更改表的Schema。我们将看到如何做到这一点。
现在,让我们使用命令对Avro表执行平均操作

1
select AVG(age) from olympic_avro;

Average+operation+on+Avro1+table

输出 运动员的平均年龄为23.65

在Avro表中处理NULL值

如前所述,需要更改表的Schema,以使表可以接受NULL值。在这种情况下,Schema如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
create table olympic_avro1
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat'
tblproperties ('avro.schema.literal'='{
"namespace":"com.example.avro",
"name": "my_record",
"type": "record",
"fields": [
{"name":"athelete", "type":["string","null"],"default":null},
{"name":"age", "type":["int","null"],"default":0},
{"name":"country", "type":["string","null"],"default":null},
{"name":"year", "type":["string","null"],"default":null},
{"name":"closing", "type":["string","null"],"default":null},
{"name":"sport", "type":["string","null"],"default":null},
{"name":"gold", "type":["int","null"],"default":0},
{"name":"silver", "type":["int","null"],"default":0},
{"name":"bronze", "type":["int","null"],"default":0},
{"name":"total", "type":["int","null"],"default":0}
]}');

常规Avro Schema与上述指定Schema之间的区别如下:
在上面的代码中type,我们给出了两个值,一个是Sting,另一个是null,这意味着如果值是指定的数据类型,它接受该值,否则该值为NULL。我们用default属性指定默认值。我们已经为string指定了默认值为null,为int指定了0(Zero)
注意:当我们将默认值指定为null或0时,我们需要在双引号中指定它。现在,让我们创建一个具有上述指定模式的表,它可以接受NULL值。

avro+schema_handles+null+values

我们可以使用以下命令查看表中创建的列:

1
describe olympic_avro1

escribe_columns_avro1_table

此表现在也将接受NULL值。让我们尝试将一些包含NULL值的数据加载到表中。我们知道奥林匹克数据中有一些NULL值,所以让我们尝试使用命令加载奥林匹克表的内容和新创建的表

1
insert overwrite table olympic_avro1 select * from olympic

copying+all+the+data+from+textfile+to+avrofile

我们已成功将包含NULL值的olympic表的内容加载到olympic_avro1表中。
现在,让我们尝试使用以下命令对新创建的表执行AVERAGE操作:

1
select AVG(age) from olympic_avro1

Average+operation+on+Avro1+table

我们已经成功地对整个奥林匹克数据执行了AVERAGE操作。

输出:运动员的平均年龄为26.405433646812956

这就是在Avro表中处理NULL值的方法。

通过下载.avro文件创建Avro表

根据以下步骤下载.avro扩展名的文件

注意:确保你Hadoop服务启动
Step 1:在浏览器中打开localhost:50070
hdfs_webview

Step 2: 在页面中点击‘utilities’然后点击‘Browse the file system’.
Step 3: 你可以看到HDFS里面的文件列表.在列表中点击‘user’.
在user目录里面,Hive文件夹中存储了Hive的所有数据

hdfs_file+list

Step 4: 点击user以后你会看到一个目录清单,然后点击其中的‘Hive’目录.

hdfs_user_directory

Step 5: 点击‘Hive’后继续点击‘Warehouse’.

hdfs_hive_directory

在点击 ‘Warehouse’后, 你可以看到所有在Hive中创建的数据库和表

Step 6: 从列表里面选择你创建的Avro表

hdfs_hive_warehouse

注意
因为没有创建额外的数据库,因此默认使用default数据库,也就所有的表都创建在warehouse目录内

点击以后你可以看到扩展名为‘.avro’的文件

hdfs_avro_extenstion+file

Step 7: 点击文件以后会有下载选项

hdfs_download_avro+file.

该文件将下载到您的“下载”文件夹中。
现在查看Avro架构,转到下载的.avro扩展文件(使用终端)所存的位置,然后键入以下命令:

1
cat filename(本文中的文件名为 000000_0.avro)

displaying_avro_schema

在上面的图片中,您可以看到创建的Avro表的Schema。直接看有点难以理解,所以我们需要将其转换为JSON格式。

将Avro转换成Json

要将Avro文件转换为JSON,我们需要下载一个名为’avro-tools-1.7.5 jar’的jar文件
,其中包含将Avro文件转换为JSON的选项。您可以从以下链接下载jar文件

avro-tools-1.7.5 jar

将该jar文件也下载到“下载”文件夹中。CD到“下载”文件夹,然后键入以下语法的命令。

java -jar avro-tools-1.7.5.jar tojson 'avro file name' >newfilename.json

本例中命令如下:

java -jar avro-tools-1.7.5.jar tojson 000000_0.avro >olympic.json

command_avro_to_json

通过cat olympic.json查看文件内容

contents_olympic_json_file

现在,您可以通过JSON格式查看表格的内容。
如上所述,Avro解析器已将Schema转换为对象,对象将包含数据。在上面的图片中,您可以看到,在Schema内部有一些内容,表示创建的对象持有我们的内容。
现在我们要把.avro扩展名的文件加载到Avro表中,创建另一个名为“olympic_avro2”的Avro表,Schema如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
create table olympic_avro2
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat'
tblproperties ('avro.schema.literal'='{
"name": "my_record",
"type": "record",
"fields": [
{"name":"athelete", "type":"string"},
{"name":"age", "type":"int"},
{"name":"country", "type":"string"},
{"name":"year", "type":"string"},
{"name":"closing", "type":"string"},
{"name":"sport", "type":"string"},
{"name":"gold", "type":"int"},
{"name":"silver", "type":"int"},
{"name":"bronze", "type":"int"},
{"name":"total", "type":"int"}
]}');

Create_olympic_avro2_table

让我们使用命令加载.avro扩展名文件000000_0.avro

LOAD DATA LOCAL INPATH '/path of the file' into table olympic_avro2

loading_.avro_file

我们已成功将.avro文件加载到Avro表中。现在,让我们使用以下命令对新创建的’olympic_avro2’表执行AVERAGE操作:

select AVG(age) from olympic_avro2;

Average_operation_avro2

Output: 我们得到的运动员的AVERAGE年龄为23.65,这与我们之前运行的结果一摸一样。

总结

  • 创建Avro表
  • 讨论了将数据加载到Avro表的两种方案
  • 对Avro表中的数据执行查询
  • 下载具有“.avro”扩展名的文件。
  • 查看了Avro schema。
  • 将Avro转换为JSON。
  • 查看JSON格式文件的内容。

原文:Data Serialization with Avro in Hive

参考

在Hadoop中分析数据(三) Avro-Tools Parquet-tools NIFI

打赏支持:如果你觉得我的文章对你有所帮助,可以打赏我哟。