如何在 Avro 中将记录与地图混合?

Posted

技术标签:

【中文标题】如何在 Avro 中将记录与地图混合?【英文标题】:How to mix record with map in Avro? 【发布时间】:2015-12-15 00:29:53 【问题描述】:

我正在处理 JSON 格式的服务器日志,我想以 Parquet 格式将我的日志存储在 AWS S3 上(并且 Parquet 需要 Avro 模式)。首先,所有日志都有一组共同的字段,其次,所有日志都有很多不属于共同组的可选字段。

例如,以下是三个日志:

 "ip": "172.18.80.109", "timestamp": "2015-09-17T23:00:18.313Z", "message":"blahblahblah"
 "ip": "172.18.80.112", "timestamp": "2015-09-17T23:00:08.297Z", "message":"blahblahblah", "microseconds": 223
 "ip": "172.18.80.113", "timestamp": "2015-09-17T23:00:08.299Z", "message":"blahblahblah", "thread":"http-apr-8080-exec-1147"

三个日志都有 3 个共享字段:iptimestampmessage,部分日志还有额外字段,例如 microsecondsthread

如果我使用以下架构,那么我将丢失所有其他字段。:

"namespace": "example.avro",
 "type": "record",
 "name": "Log",
 "fields": [
     "name": "ip", "type": "string",
     "name": "timestamp",  "type": "String",
     "name": "message", "type": "string"
 ]

以下架构可以正常工作:

"namespace": "example.avro",
 "type": "record",
 "name": "Log",
 "fields": [
     "name": "ip", "type": "string",
     "name": "timestamp",  "type": "String",
     "name": "message", "type": "string",
     "name": "microseconds", "type": [null,long],
     "name": "thread", "type": [null,string]
 ]

但唯一的问题是,除非我扫描所有日志,否则我不知道所有可选字段的名称,而且将来还会有新的附加字段。

然后我想出了一个结合recordmap的想法:

"namespace": "example.avro",
 "type": "record",
 "name": "Log",
 "fields": [
     "name": "ip", "type": "string",
     "name": "timestamp",  "type": "String",
     "name": "message", "type": "string",
     "type": "map", "values": "string"  // error
 ]

不幸的是,这不会编译:

java -jar avro-tools-1.7.7.jar compile schema example.avro .

它会抛出一个错误:

Exception in thread "main" org.apache.avro.SchemaParseException: No field name: "type":"map","values":"long"
    at org.apache.avro.Schema.getRequiredText(Schema.java:1305)
    at org.apache.avro.Schema.parse(Schema.java:1192)
    at org.apache.avro.Schema$Parser.parse(Schema.java:965)
    at org.apache.avro.Schema$Parser.parse(Schema.java:932)
    at org.apache.avro.tool.SpecificCompilerTool.run(SpecificCompilerTool.java:73)
    at org.apache.avro.tool.Main.run(Main.java:84)
    at org.apache.avro.tool.Main.main(Main.java:73)

有没有办法以 Avro 格式存储 JSON 字符串,可以灵活地处理未知的可选字段?

基本上这是一个模式演化问题,Spark可以通过Schema Merging处理这个问题。我正在寻找 Hadoop 的解决方案。

【问题讨论】:

您的地图没有名称属性。给它一个。 :) 我猜你从来没有尝试过 avro。它行不通。 "namespace": "example.avro", "type": "record", "name": "Log", "fields": [ "name": "ip", "type": "string", "name": "timestamp", "type": "string", "name": "message", "type": "string", "name": "addtional", "type": "map", "values": "string" ] 【参考方案1】:

地图类型是 avro 术语中的“复杂”类型。以下 sn-p 有效:


  "namespace": "example.avro",
  "type": "record",
  "name": "Log",
  "fields": [
    "name": "ip", "type": "string",
    "name": "timestamp",  "type": "string",
    "name": "message", "type": "string",
    "name": "additional", "type": "type": "map", "values": "string"
  ]

【讨论】:

谢谢!此架构将通过编译。此架构将所有可选字段放在addtional 字段中,例如"ip": "172.18.80.109", "timestamp": "2015-09-17T23:00:18.313Z", "message": "blah blash", "addtional": "microseconds": "123", "thread": "http-apr-8080-exec-1147",但我希望所有可选字段与公共字段处于同一级别,就像我的问题中的三个示例日志一样。 在 avro 中的记录被定义为具有固定数量的预定义字段的对象。或者,将您的地图作为***对象,并将您的所有字段作为该地图的键。 如果我使用map作为***类型,例如"type": "map", "values": "string",那么所有字段都必须是string类型,如果有不同类型的字段,那么map是无奈。 您可以将映射值类型定义为联合或包含联合的命名记录类型。 Avro 在这方面非常灵活。

以上是关于如何在 Avro 中将记录与地图混合?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spark 中将 Avro Schema 对象转换为 StructType

如何在 jupyter notebook 中将 spark 数据帧写入 avro 文件格式?

如何防止 R Expss 在输出数据框中将变量名称与行标签混合?

如何在 HugSQL 结果中将返回的地图转换为 json csv

如何在 Avro 模式中嵌套记录?

如何在 Adob​​e Animate CC 2019 中将主时间线影片剪辑与 html5 画布混合