单一模式中相同类型的 Avro 多条记录

Posted

技术标签:

【中文标题】单一模式中相同类型的 Avro 多条记录【英文标题】:Avro multiple record of same type in single schema 【发布时间】:2018-06-14 11:55:40 【问题描述】:

我喜欢在 Avro 架构中多次使用相同的记录类型。考虑这个架构定义

“类型”:“记录”, "name": "订单簿", “命名空间”:“my.types”, "doc": "测试订单更新", “领域”:[ “名称”:“出价”, “类型”: “类型”:“数组”, “项目”: “类型”:“记录”, "name": "OrderBookVolume", “命名空间”:“my.types”, “领域”:[ “名称”:“价格”, “类型”:“双” , “名称”:“体积”, “类型”:“双” ] , “姓名”:“询问”, “类型”: “类型”:“数组”, “项目”: “类型”:“记录”, "name": "OrderBookVolume", “命名空间”:“my.types”, “领域”:[ “名称”:“价格”, “类型”:“双” , “名称”:“体积”, “类型”:“双” ] ]

这不是有效的 Avro 架构,并且 Avro 架构解析器失败

org.apache.avro.SchemaParseException:无法重新定义:my.types.OrderBookVolume

我可以通过将 OrderBookVolume 移动到两个不同的命名空间来使类型唯一来解决此问题:

“类型”:“记录”, "name": "订单簿", “命名空间”:“my.types”, "doc": "测试订单更新", “领域”:[ “名称”:“出价”, “类型”: “类型”:“数组”, “项目”: “类型”:“记录”, "name": "OrderBookVolume", “命名空间”:“my.types.bid”, “领域”:[ “名称”:“价格”, “类型”:“双” , “名称”:“体积”, “类型”:“双” ] , “姓名”:“询问”, “类型”: “类型”:“数组”, “项目”: “类型”:“记录”, "name": "OrderBookVolume", “命名空间”:“my.types.ask”, “领域”:[ “名称”:“价格”, “类型”:“双” , “名称”:“体积”, “类型”:“双” ] ]

这不是一个有效的解决方案,因为 Avro 代码生成会生成两个不同的类,如果我想将该类型也用于其他事情而不只是用于 deser 和 ser,这将非常烦人。

这个问题在这里与这个问题有关: Avro Spark issue #73

通过在命名空间前面加上外部记录名称来区分具有相同名称的嵌套记录。他们的用例可能纯粹与存储相关,因此可能对他们有用,但对我们无效。

有人知道更好的解决方案吗?这是 Avro 的硬性限制吗?

【问题讨论】:

【参考方案1】:

没有很好的文档记录,但是 Avro 允许您通过使用被引用名称的完整命名空间来引用以前定义的名称。在您的情况下,以下代码将只生成一个由每个数组引用的类。它还可以很好地干燥模式。


    "type": "record",
    "name": "OrderBook",
    "namespace": "my.types",
    "doc": "Test order update",
    "fields": [
        
            "name": "bids",
            "type": 
                "type": "array",
                "items": 
                    "type": "record",
                    "name": "OrderBookVolume",
                    "namespace": "my.types.bid",
                    "fields": [
                        
                            "name": "price",
                            "type": "double"
                        ,
                        
                            "name": "volume",
                            "type": "double"
                        
                    ]
                
            
        ,
        
            "name": "asks",
            "type": 
                "type": "array",
                "items": "my.types.bid.OrderBookVolume"
            
        
    ]

【讨论】:

【参考方案2】:

如the spec中所述:

A schema or protocol may not contain multiple definitions of a fullname.
Further, a name must be defined before it is used ("before" in the
depth-first, left-to-right traversal of the JSON parse tree, where the
types attribute of a protocol is always deemed to come "before" the
messages attribute.)

例如:


    "type": "record",
    "namespace": "my.types",
    "name": "OrderBook",
    "fields": [
        
            "name": "bids",
            "type": 
                "type": "array",
                "items": 
                    "type": "record",
                    "name": "OrderBookVolume",
                    "fields": [
                        "name": "price", "type": "double",
                        "name": "volume", "type": "double"
                    ]
                
            
        ,
        
            "name": "asks",
            "type": 
                "type": "array",
                "items": 
                    "type": "record",
                    "name": "my.types.OrderBookVolume"
                
            
        
    ]

第一次出现是OrderBookVolume 的完整架构。之后,您可以参考fullnamemy.types.OrderBookVolume

还值得注意的是,您不需要为每条记录指定一个命名空间。它从其父级继承。包含它会覆盖命名空间。

【讨论】:

我认为数组项的语法不正确 - 请参阅 John Hunter 接受的答案 @tarling 我不确定你的意思。你能说得更具体点吗? 当然。要指定以前的引用用作数组项的类型,您应该只将名称指定为字符串。我无法让 Confluent Schema Registry 识别您示例中的语法,但是当我将其换成以下语法时它起作用了。感谢您的回复 "name": "asks", "type": "type": "array", "items": "OrderBookVolume" 如果没有在第一个定义中添加命名空间并引用它,它就不起作用,就像 Hunter 所做的那样。

以上是关于单一模式中相同类型的 Avro 多条记录的主要内容,如果未能解决你的问题,请参考以下文章

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

我们可以使用嵌入在 avro 记录中的模式来反序列化值吗?

在 avro 中存储模式有啥好处?

如何从 Java 中的 avro 文件中提取模式

使用带有自动模式检索的 LoadJobs 时如何处理 Avro 到 BigQuery 的类型转换

具有逻辑类型的 Avro 模式不能与最新的 confluent-kafka 一起使用