当我使用 'STORED AS AVRO' 子句创建配置单元 table 时,Avro 模式存储在哪里?

Where is an Avro schema stored when I create a hive table with 'STORED AS AVRO' clause?

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

  1. 基于 Avro 架构创建 table(在本例中,存储在 hdfs 中):

    创建 TABLE users_from_avro_schema 行格式 SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe' 存储为输入格式 'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat' 输出格式 'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat' TBLPROPERTIES ('avro.schema.url'='hdfs:///user/root/avro/schema/user.avsc');

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

    创建TABLEusers_stored_as_avro( 身份证, 名称 STRING ) 存储为 AVRO;

我是否更正了第一种情况 users_from_avro_schema table 的元数据未存储在 Hive Metastore 中,而是从 SERDE class 读取 avro 模式文件中推断出来的?或者 table 元数据存储在 Metastore 中,在 table 创建时添加,但是将 Hive 元数据与 Avro 模式同步的策略是什么?我的意思是两种情况:

  1. 更新 table 元数据(adding/removing 列)和
  2. 通过更改 avro.schema.url 属性.
  3. 更新 Avro 架构

在第二种情况下,当我调用 DESCRIBE FORMATTED users_stored_as_avro 时,没有定义 avro.schema.* 属性,所以我不知道哪个 Avro 模式用于 read/write 数据.它是根据 Metastore 中存储的 table 的元数据动态生成的吗?

fragment of Programming Hive book discusses inferring info about columns from the SerDe class, but on the other hand HIVE-4703 删除此 from deserializer 信息表单列注释。那么我如何检查给定 table(Metastore 或 Avro 模式)的列类型的来源是什么?

以下为不涉及schema文件的用例

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

DESC/SHOW 命令的所有信息均取自 Metastore。
每个 DDL 更改仅影响 Metastore。

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

演示

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 |
+-------+----------+------------+

Metastore

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 |
+--------+----------+------------+-------+

Metastore

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

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
  } ]
}

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

我决定发布对@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'

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

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

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

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}
  ]
}

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

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

是否更改了 table 元数据?

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 具有以下推断模式的策略: 它试图从为给定 table 指定的 SerDe class 中获取它。当 SerDe 无法提供架构时,Hive 会查看 Metastore。
让我们通过删除 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.avsc 时,Hive 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 数据之间存在冲突也可以读取。 请再看看我上面的例子。我们的 table 定义指向具有三个字段的 avro 模式:

id    int
name  string
email string

而在 Hive Metastore 中有以下列:

id    int
name  string
phone string

电子邮件与phone
让我们创建一个包含符合 user_v2.avsc 模式的单个用户记录的 avro 文件。这是它的 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 列,但我们能够查询我们的 table:

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