web-dev-qa-db-ja.com

'STORED AS AVRO'句を使用してHiveテーブルを作成すると、Avroスキーマはどこに保存されますか?

Avroデータを基にしたHiveテーブルを作成するには、少なくとも2つの方法があります。

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');

2)STORED AS AVRO句でHive列を明示的に指定してテーブルを作成します。

CREATE TABLE users_stored_as_avro(
  id INT,
  name STRING
) STORED AS AVRO;

最初のケースでは、users_from_avro_schemaテーブルのメタデータはHive Metastoreに格納されていませんが、Avroスキーマファイルを読み取るSERDEクラスから推測されることは正しいですか?または、テーブルのメタデータがメタストアに格納され、テーブルの作成時に追加されたとしても、HiveメタデータをAvroスキーマと同期するためのポリシーは何ですか?つまり、テーブルメタデータの更新(列の追加/削除)と、avro.schema.urlプロパティの変更によるAvroスキーマの更新の両方です。

2番目のケースでは、DESCRIBE FORMATTED users_stored_as_avroを呼び出すときにavro.schema.*プロパティが定義されていないため、データの読み取り/書き込みにどのAvroスキーマが使用されているかわかりません。テーブルに保存されているメタデータに基づいて動的に生成されます。メタストア?

これは、プログラミングHiveの本の フラグメント は、SerDeクラスからの列に関する情報の推論について述べていますが、一方で Hive-47 は、このfrom deserializer情報フォームの列コメントを削除します。特定のテーブル(MetastoreまたはAvroスキーマ)の列タイプのソースを確認するにはどうすればよいですか?

10
tomek

私は、@ DuduMarkovitzからの回答に対する補足的な回答を公開することにしました。

コード例をより簡潔にするために、STORED AS AVRO句が次の3行と同等であることを明確にしましょう。

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

これまでのところ、とても良い、すべてが期待どおりに機能します。しかし、次のように、スキーマの次のバージョン(users_v2.avsc)を指すようにavro.schema.urlプロパティを更新するとどうなるかを見てみましょう。

{
  "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にこの追加の列が含まれていると思いますか?
残念ながらメタストアでは何も変更されていません

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

メタストアに保存されているデータを示します。列を追加して変更しましょう:

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.urluser_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戦略を知っていれば、それに耐えることができます。私はHiveのソースコードで、スキーマロジックに対する私の疑いが正しいかどうかを確認していませんが、上記の例では、その下で何が起こっているかがわかります。

AvroスキーマとHive Metastoreデータの間に競合がある場合でも、Avroスキーマに準拠するデータを読み取ることができることを示すために、私の回答を拡張しました。上記の私の例をもう一度見てください。テーブル定義は、3つのフィールドを持つavroスキーマをポイントしています。

id    int
name  string
email string

一方、Hive Metastoreには次の列があります。

id    int
name  string
phone string

メールと電話
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列が含まれているにもかかわらず、テーブルにクエリを実行できます。

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
11
tomek

以下は、スキーマファイルが含まれないユースケースを示します

スキーマは2か所に保存されます
1。メタストア
2。データファイルの一部として

DESC/SHOWコマンドのすべての情報は、メタストアから取得されます。
すべてのDDL変更はメタストアにのみ影響します。

データをクエリすると、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 |
+-------+----------+------------+

メタストア

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
(元のスキーマと同じスキーマ)

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

そのテーブルに対する作業は、メタストアに格納されているメタデータに基づいて行われます。
テーブルがクエリされているとき、追加メタデータが使用されています。これは、データファイルに格納されているメタデータです。
クエリ結果の構造はメタストアから作成されます(私の例では、テーブルが変更された後に4列が返されることを参照してください)。
返されるデータは両方スキームに依存します-ファイルスキーマの特定の名前のフィールドは、メタストアスキーマの同じ名前の列にマップされます。
名前は一致するがデータ型が一致しない場合、エラーが発生します。
メタストアに対応する列名がないデータファイルのフィールドは表示されません。
データファイルスキーマの対応するフィールドがないメタストアの列には、NULL値が保持されます。