hive 当我使用'STORED AS AVRO'子句创建配置单元表时,Avro架构存储在哪里?

tpgth1q7  于 2023-04-20  发布在  Hive
关注(0)|答案(3)|浏览(173)

创建支持Avro数据的配置单元表至少有两种不同的方法:
1.基于Avro架构创建表(在本例中,存储在hdfs中):
CREATE TABLE users_from_avro_schema 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.url'='hdfs:///user/root/avro/schema/user. avsc');
1.通过使用STORED AS AVRO子句显式指定配置单元列来创建表:
CREATE TABLE users_stored_as_avro(id INT,name STRING)STORED AS AVRO;
在第一种情况下,users_from_avro_schema表的元数据没有存储在Hive Metastore中,而是从阅读avro schema文件的SERDE类中推断出来的,我说的对吗?或者,表元数据存储在Metastore中,在创建表时添加,但是将Hive元数据与Avro schema同步的策略是什么?我指的是两种情况:
1.更新表元数据(添加/删除列)和
1.通过更改avro.schema.url属性更新Avro架构。
在第二种情况下,当我调用DESCRIBE FORMATTED users_stored_as_avro时,没有定义avro.schema.*属性,所以我不知道使用哪个Avro模式来读取/写入数据。它是基于存储在Metastore中的表的元数据动态生成的吗?
Programming Hive这本书讨论了从SerDe类中推断列的信息,但另一方面,HIVE-4703从列注解中删除了from deserializer信息。我如何检查给定表的列类型的来源(Metastore或Avro模式)?

rkttyhzu

rkttyhzu1#

我决定发表一个对@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 schema的表时会发生什么。下面是schema:

{
  "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已经正确地推断出了schema,我们可以通过调用来看到:

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有以下推断schema的策略:**它尝试从为给定表指定的SerDe类中获取schema。当SerDe无法提供schema时,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.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架构优先于元存储。
上面的例子表明,我们应该避免将hive模式更改与avro模式演化混合在一起,因为否则我们很容易陷入Hive元存储与阅读数据时使用的实际模式之间的混乱和不一致。第一个不一致发生在我们通过更新avro.schema.url属性来更改avro模式定义时,我还没有检查Hive的源代码,我对模式逻辑的怀疑是否正确,但是上面的例子说服了我下面发生了什么。
我扩展了我的答案,以表明即使Avro模式和Hive Metastore之间存在冲突,也可以读取符合Avro模式的数据。请再次查看我上面的示例。我们的表定义指向具有三个字段的Avro模式:

id    int
name  string
email string

而在Hive Metastore中有以下列:

id    int
name  string
phone string

电子邮件vs电话
让我们创建一个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
jv2fixgn

jv2fixgn2#

以下是不涉及模式文件的用例

架构存储在两个位置
1.元存储
2.作为数据文件的一部分
DESC/SHOW命令的所有信息都取自元存储区。
每个DDL更改都只影响元存储区。
当您查询数据时,两个模式之间的匹配是通过列名完成的。
如果列类型不匹配,您将得到一个错误。

Demo

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

(same schema作为原始schema)

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列)。
返回的数据取决于两个方案-文件方案中具有特定名称的字段将Map到Metastore方案中具有相同名称的列。
如果名称匹配但数据类型不匹配,则会出现错误。
数据文件中在元存储中没有对应列名的字段将不会显示。
Metastore中的列如果在数据文件模式中没有对应的字段,则将保存NULL值。

hs1rzwqc

hs1rzwqc3#

现在考虑到像Iceberg和Delta表这样的表格式,这可能有点过时了,因为它负责模式的更改和演变(这是AVRO开始的主要卖点)
结合Tomek和大卫的答案,是否可以得出结论,对于使用“存储为avro”创建的hive表,字段元数据是从3个不同的来源推断/引用/提取的
1.通过创建avro文件更新数据中的模式(参见Tomek的回答如何从JSON创建一个)
1.使用添加删除列更新配置单元元存储区
1.使用avro.schema.url属性更新avsc架构URL。这是可选的
以下是我对hive代码中不同操作所遵循的逻辑的解释(我会在了解更多信息时更新我的答案):

  1. select:数据中的schema优先,则仅当name匹配但datatype不匹配时才会引发配置单元元存储错误
    1.描述/显示:avsc架构URL优先,然后是配置单元元存储区
  2. insert:数据中的明显模式;但对avsc模式URL进行了一些验证

相关问题