13

至少有两种不同的方法可以创建支持 Avro 数据的配置单元表:

  1. 基于 Avro 模式创建表(在本例中,存储在 hdfs 中):

    CREATE TABLE users_from_avro_schema ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe' 存储为 INPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive .ql.io.avro.AvroContainerOutputFormat' TBLPROPERTIES ('avro.schema.url'='hdfs:///user/root/avro/schema/user.avsc');

  2. STORED AS AVRO通过使用子句显式指定配置单元列来创建表:

    创建表 users_stored_as_avro( id INT, name STRING ) 存储为 AVRO;

我是否正确,在第一种情况下,users_from_avro_schema表的元数据没有存储在 Hive Metastore 中,而是从读取 avro 模式文件的 SERDE 类中推断出来的?或者,表元数据可能存储在 Metastore 中,在创建表时添加,但是将配置单元元数据与 Avro 模式同步的策略是什么?我的意思是两种情况:

  1. 更新表元数据(添加/删除列)和
  2. 通过更改avro.schema.url属性来更新 Avro 架构。

在我调用的第二种情况下,DESCRIBE FORMATTED users_stored_as_avro没有avro.schema.*定义属性,所以我不知道使用哪个 Avro 模式来读取/写入数据。它是根据存储在 Metastore 中的表的元数据动态生成的吗?

Programming Hive 书的此片段讨论了从 SerDe 类中推断有关列的信息,但另一方面,HIVE-4703删除了此from deserializer信息表单列注释。那么如何检查给定表(Metastore 或 Avro 模式)的列类型的来源是什么?

4

2 回答 2

19

我决定发布对@DuduMarkovitz 给出的答案的补充答案。

为了使代码示例更简洁,让我们澄清一下子STORED AS 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'

让我们看看当我们创建一个表时会发生什么,该表引用了存储在 hdfs 中的 avro 模式。这是架构:

{
  "namespace": "io.sqooba",
  "name": "user",
  "type": "record",
  "fields": [
    {"name": "id", "type": "int"},
    {"name": "name", "type": "string"}
  ]
}

我们使用以下命令创建表:

CREATE TABLE users_from_avro_schema
STORED AS AVRO
TBLPROPERTIES ('avro.schema.url'='hdfs:///user/tulinski/user.avsc');

Hive 已经正确推断出架构,我们可以通过调用来查看:

hive> DESCRIBE users_from_avro_schema;
OK
id                      int
name                    string

Hive Metastore 向我们展示了同样的情况(我使用 @DuduMarkovitz 的查询):

+------------------------+-------------+-------------+-----------+
| tbl_name               | column_name | integer_idx | type_name |
+------------------------+-------------+-------------+-----------+
| users_from_avro_schema | id          |           0 | int       |
| users_from_avro_schema | name        |           1 | string    |
+------------------------+-------------+-------------+-----------+

到目前为止,一切都很好,一切都按我们的预期进行。但是让我们看看当我们更新avro.schema.url属性以指向我们模式的下一个版本(users_v2.avsc)时会发生什么,如下所示:

{
  "namespace": "io.sqooba",
  "name": "user",
  "type": "record",
  "fields": [
    {"name": "id", "type": "int"},
    {"name": "name", "type": "string"},
    {"name": "email", "type": ["null", "string"], "default":null}
  ]
}

我们只是添加了另一个名为电子邮件的字段。
现在我们更新一个指向 hdfs 中 avro 模式的表属性:

ALTER TABLE users_from_avro_schema SET TBLPROPERTIES('avro.schema.url'='hdfs:///user/tulinski/user_v2.avsc');

表元数据是否已更改?

hive> DESCRIBE users_from_avro_schema;
OK
id                      int
name                    string
email                   string

是的,酷!但是您是否期望 Hive Metastore 包含这个附加列?
不幸的是,在 Metastore 中没有任何改变

+------------------------+-------------+-------------+-----------+
| tbl_name               | column_name | integer_idx | type_name |
+------------------------+-------------+-------------+-----------+
| users_from_avro_schema | id          |           0 | int       |
| users_from_avro_schema | name        |           1 | string    |
+------------------------+-------------+-------------+-----------+

我怀疑 Hive 具有以下推断模式的策略:它尝试从为给定表指定的 SerDe 类中获取它。当 SerDe 无法提供架构时,Hive 会查看元存储。
让我们通过删除avro.schema.url属性来检查:

hive> ALTER TABLE users_from_avro_schema UNSET TBLPROPERTIES ('avro.schema.url');
OK
Time taken: 0.33 seconds
hive> DESCRIBE users_from_avro_schema;
OK
id                      int
name                    string
Time taken: 0.363 seconds, Fetched: 2 row(s)

Describe 向我们展示了存储在 Metastore 中的数据。让我们通过添加一列来修改它们:

ALTER TABLE users_from_avro_schema ADD COLUMNS (phone string);

它当然会改变 Hive Metastore:

+------------------------+-------------+-------------+-----------+
| tbl_name               | column_name | integer_idx | type_name |
+------------------------+-------------+-------------+-----------+
| users_from_avro_schema | id          |           0 | int       |
| users_from_avro_schema | name        |           1 | string    |
| users_from_avro_schema | phone       |           2 | string    |
+------------------------+-------------+-------------+-----------+

但是当我们avro.schema.url再次回到user_v2.avscHive Metastore 中的内容时不再重要:

hive> ALTER TABLE users_from_avro_schema SET TBLPROPERTIES('avro.schema.url'='hdfs:///user/tulinski/user_v2.avsc');
OK
Time taken: 0.268 seconds
hive> DESCRIBE users_from_avro_schema;
OK
id                      int
name                    string
email                   string

Avro 模式优先于 Metastore。

上面的示例表明,我们应该避免将 hive 模式更改与 avro 模式演变混为一谈,否则我们很容易陷入 Hive Metastore 与读取和写入数据时使用的实际模式之间的大混乱和不一致。第一个不一致发生在我们通过更新avro.schema.url属性来更改我们的 avro 模式定义时,但如果我们知道 Hive 推断模式的策略,我们可以忍受这种情况。我没有检查 Hive 的源代码是否我对模式逻辑的怀疑是正确的,但是上面的例子让我相信下面发生的事情。

我扩展了我的答案以表明即使 Avro 模式和符合 Avro 模式的 Hive Metastore 数据之间存在冲突,也可以读取。请再看看我上面的例子。我们的表定义指向具有三个字段的 avro 模式:

id    int
name  string
email string

而在 Hive Metastore 中有以下列:

id    int
name  string
phone string

电子邮件与电话
让我们创建一个 avro 文件,其中包含一个符合用户记录的user_v2.avsc架构。这是它的 json 表示:

{
  "id": 123,
  "name": "Tomek",
  "email": {"string": "tomek@tomek"}
}

要创建 avro 文件,我们调用:

java -jar avro-tools-1.8.2.jar fromjson --schema-file user_v2.avsc user_tomek_v2.json > user_tomek_v2.avro

尽管 Hive Metastore 不包含email列并且它包含phone列,但我们仍然能够查询我们的表:

hive> set hive.cli.print.header=true;
hive> select * from users_from_avro_schema;
OK
users_from_avro_schema.id   users_from_avro_schema.name users_from_avro_schema.email
123 Tomek   tomek@tomek
于 2017-05-31T10:53:28.470 回答
8

以下是不涉及架构文件的用例

架构存储在 2 个地方
1. 元存储
2. 作为数据文件的一部分

DESC/SHOW 命令的所有信息都取自元存储。
每个 DDL 更改仅影响元存储。

当您查询数据时,两个模式之间的匹配由列名完成。
如果列类型不匹配,您将收到错误消息。

演示

create table mytable 
stored as avro 
as 
select  1               as myint
       ,'Hello'         as mystring
       ,current_date    as mydate
;

select * from mytable
;

+-------+----------+------------+
| myint | mystring |   mydate   |
+-------+----------+------------+
|     1 | Hello    | 2017-05-30 |
+-------+----------+------------+

元存储

select      c.column_name
           ,c.integer_idx
           ,c.type_name

from                metastore.DBS        as d
            join    metastore.TBLS       as t on t.db_id = d.db_id
            join    metastore.SDS        as s on s.sd_id = t.sd_id
            join    metastore.COLUMNS_V2 as c on c.cd_id = s.cd_id

where       d.name     = 'local_db'
        and t.tbl_name = 'mytable'

order by    integer_idx

+-------------+-------------+-----------+
| column_name | integer_idx | type_name |
+-------------+-------------+-----------+
| myint       |           0 | int       |
| mystring    |           1 | string    |
| mydate      |           2 | date      |
+-------------+-------------+-----------+

avro工具

bash-4.1$ avro-tools getschema 000000_0 

{
  "type" : "record",
  "name" : "mytable",
  "namespace" : "local_db",
  "fields" : [ {
    "name" : "myint",
    "type" : [ "null", "int" ],
    "default" : null
  }, {
    "name" : "mystring",
    "type" : [ "null", "string" ],
    "default" : null
  }, {
    "name" : "mydate",
    "type" : [ "null", {
      "type" : "int",
      "logicalType" : "date"
    } ],
    "default" : null
  } ]
}

alter table mytable change myint dummy1 int;

select * from mytable;

+--------+----------+------------+
| dummy1 | mystring |   mydate   |
+--------+----------+------------+
| (null) | Hello    | 2017-05-30 |
+--------+----------+------------+

alter table mytable add columns (myint int);

select * from mytable;

+--------+----------+------------+-------+
| dummy1 | mystring |   mydate   | myint |
+--------+----------+------------+-------+
| (null) | Hello    | 2017-05-30 |     1 |
+--------+----------+------------+-------+

元存储

+-------------+-------------+-----------+
| column_name | integer_idx | type_name |
+-------------+-------------+-----------+
| dummy1      |           0 | int       |
| mystring    |           1 | string    |
| mydate      |           2 | date      |
| myint       |           3 | int       |
+-------------+-------------+-----------+

avro-tools
(与原始模式相同的模式)

bash-4.1$ avro-tools getschema 000000_0 

{
  "type" : "record",
  "name" : "mytable",
  "namespace" : "local_db",
  "fields" : [ {
    "name" : "myint",
    "type" : [ "null", "int" ],
    "default" : null
  }, {
    "name" : "mystring",
    "type" : [ "null", "string" ],
    "default" : null
  }, {
    "name" : "mydate",
    "type" : [ "null", {
      "type" : "int",
      "logicalType" : "date"
    } ],
    "default" : null
  } ]
}

针对该表的任何工作都是基于存储在 Metastore 中的元数据完成的。
查询表时,会使用额外的元数据,即存储在数据文件中的元数据。
查询结果结构是从 Metastore 构造的(在我的示例中,表被更改后返回4列)。
返回的数据取决于两种方案 - 文件模式中具有特定名称的字段将映射到 Metastore 模式中具有相同名称的列。
如果名称匹配但数据类型不匹配,则会出现错误。
不会显示数据文件中在 Metastore 中没有相应列名的字段。
Metastore 中没有数据文件架构中相应字段的列将包含 NULL 值。

于 2017-05-30T11:05:55.917 回答