如何在AWS Glue / Athena上使用AVRO格式

时间:2019-06-25 19:56:19

标签: amazon-web-services avro amazon-athena aws-glue

我在Kafka中有几个主题正在将AVRO文件写入S3存储桶,我想使用AWS Athena对存储桶执行一些查询。

我正在尝试创建表,但是AWS Glue搜寻器会运行并且不会添加我的表(如果我将文件类型更改为JSON,它将起作用)。我试图从Athena控制台创建一个表,但是它不显示对AVRO文件的支持。

关于如何使其工作的任何想法?

2 个答案:

答案 0 :(得分:0)

我建议手动而不是通过胶水进行。不幸的是,胶水仅适用于最基本的情况。

您可以在以下位置找到有关如何创建Avro表的文档:https://docs.aws.amazon.com/athena/latest/ug/avro.html

Avro表的警告是,您需要同时指定表列和Avro模式。这看起来很奇怪和多余,但这就是Athena / Presto的工作方式。它需要一个架构来知道如何解释文件,然后需要知道文件中哪些属性要公开为列(以及它们的类型,它们可能与Avro类型匹配,也可能不匹配)。

CREATE EXTERNAL TABLE avro_table (
   foo STRING,
   bar INT
)
ROW FORMAT
SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
WITH SERDEPROPERTIES ('avro.schema.literal' = '
{
  "type": "record",
  "name": "example",
  "namespace": "default",
  "fields": [
    {
      "name": "foo",
      "type": ["null", "string"],
      "default": null
    },
    {
      "name": "bar",
      "type": ["null", "int"],
      "default": null
    }
  ]
}
')
STORED AS AVRO
LOCATION 's3://some-bucket/data/';

请注意,Avro模式如何在SERDE属性值(单引号)内显示为JSON文档–格式是可选的,但使此示例易于阅读。

答案 1 :(得分:-1)

手动执行似乎是使其工作的一种方式。 这是一些直接从文字avro模式生成Athena模式的代码。它适用于avro-python3上的python3.7。它是从这里获取的:https://github.com/dataqube-GmbH/avro2athena(我是仓库的所有者)

from avro.schema import Parse, RecordSchema, PrimitiveSchema, ArraySchema, MapSchema, EnumSchema, UnionSchema, FixedSchema


def create_athena_schema_from_avro(avro_schema_literal: str) -> str:
    avro_schema: RecordSchema = Parse(avro_schema_literal)

    column_schemas = []
    for field in avro_schema.fields:
        column_name = field.name.lower()
        column_type = create_athena_column_schema(field.type)
        column_schemas.append(f"`{column_name}` {column_type}")

    return ', '.join(column_schemas)


def create_athena_column_schema(avro_schema) -> str:
    if type(avro_schema) == PrimitiveSchema:
        return rename_type_names(avro_schema.type)

    elif type(avro_schema) == ArraySchema:
        items_type = create_athena_column_schema(avro_schema.items)
        return f'array<{items_type}>'

    elif type(avro_schema) == MapSchema:
        values_type = avro_schema.values.type
        return f'map<string,{values_type}>'

    elif type(avro_schema) == RecordSchema:
        field_schemas = []
        for field in avro_schema.fields:
            field_name = field.name.lower()
            field_type = create_athena_column_schema(field.type)
            field_schemas.append(f'{field_name}:{field_type}')

        field_schema_concatenated = ','.join(field_schemas)
        return f'struct<{field_schema_concatenated}>'

    elif type(avro_schema) == UnionSchema:
        # pick the first schema which is not null
        union_schemas_not_null = [s for s in avro_schema.schemas if s.type != 'null']
        if len(union_schemas_not_null) > 0:
            return create_athena_column_schema(union_schemas_not_null[0])
        else:
            raise Exception('union schemas contains only null schema')

    elif type(avro_schema) in [EnumSchema, FixedSchema]:
        return 'string'

    else:
        raise Exception(f'unknown avro schema type {avro_schema.type}')


def rename_type_names(typ: str) -> str:
    if typ in ['long']:
        return 'bigint'
    else:
        return typ