跳转到主要内容
Chinese, Simplified

模式演化

数据管理的一个重要方面是模式演化。在定义了初始模式之后,应用程序可能需要随着时间的推移对其进行改进。当这种情况发生时,下游消费者能够无缝地处理新旧模式编码的数据是至关重要的。这是一个容易在实践中被忽视的领域,直到您遇到第一个生产问题。如果不仔细考虑数据管理和模式演化,人们通常会在以后付出更高的成本。

在使用Avro时,最重要的事情之一是管理它的模式,并考虑这些模式应该如何发展。Confluent模式注册表正是为此目的而构建的。模式兼容性检查是在模式注册表中通过对每个模式进行版本控制来实现的。兼容性类型决定模式注册中心如何将给定主题的新模式与模式的先前版本进行比较。当第一次为主题创建模式时,它将获得一个惟一id和一个版本号,即版本1。当模式被更新时(如果它通过兼容性检查),它将获得一个新的惟一id和一个递增的版本号,即版本2。

兼容性类型

总结

下表总结了针对给定主题的不同兼容性类型允许的模式更改类型。Confluent模式注册表默认兼容性类型是向后的。所有兼容性类型将在下面的小节中详细描述。

Compatibility Type Changes allowed Check against which schemas Upgrade first
BACKWARD
  • Delete fields
  • Add optional fields
Last version Consumers
BACKWARD_TRANSITIVE
  • Delete fields
  • Add optional fields
All previous versions Consumers
FORWARD
  • Add fields
  • Delete optional fields
Last version Producers
FORWARD_TRANSITIVE
  • Add fields
  • Delete optional fields
All previous versions Producers
FULL
  • Modify optional fields
Last version Any order
FULL_TRANSITIVE
  • Modify optional fields
All previous versions Any order
NONE
  • All changes are accepted
Compatibility checking disabled Depends

 

向后兼容性

向后兼容性意味着使用新模式的用户可以读取使用最后一个模式生成的数据。例如,如果一个主题有三个模式按照X-2、X-1和X顺序变化,那么向后兼容性可以确保使用新模式X的消费者可以处理使用模式X或X-1(但不一定是X-2)的生产者编写的数据。如果使用新模式的使用者需要能够处理由所有已注册模式(而不仅仅是最后两个模式)编写的数据,那么使用BACKWARD_TRANSITIVE而不是BACKWARD_TRANSITIVE。例如,如果一个主题有三个模式按照X-2、X-1和X顺序变化,那么BACKWARD_TRANSITIVE兼容性可以确保使用新模式X的消费者可以处理使用模式X、X-1或X-2的生产者编写的数据。

  • 向后:使用模式X的使用者可以处理使用模式X或X-1生成的数据
  • BACKWARD_TRANSITIVE:使用模式X的使用者可以处理模式X、X-1或X-2生成的数据

请注意

Confluent模式注册表默认兼容性类型是向后的,而不是向后传递的。

向后兼容更改的一个例子是删除字段。开发用于处理事件而没有该字段的使用者将能够处理用旧模式编写的事件并包含该字段——使用者将忽略该字段。

考虑这样一种情况,Kafka中的所有数据也都加载到HDFS中,我们希望在所有数据上运行SQL查询(例如,使用Apache Hive)。在这里,重要的是,即使随着时间的推移数据正在发生变化,相同的SQL查询也要继续工作。为了支持这种用例,我们可以以向后兼容的方式演进模式。Avro有一组规则,规定允许在新模式中进行哪些更改,以使其向后兼容。如果所有模式都以向后兼容的方式发展,我们总是可以使用最新的模式统一地查询所有数据。

例如,一个应用程序可以通过添加一个新的字段favorite_color将用户模式从上一节发展到下一节:

{"namespace": "example.avro",
 "type": "record",
 "name": "user",
 "fields": [
     {"name": "name", "type": "string"},
     {"name": "favorite_number",  "type": "int"},
     {"name": "favorite_color", "type": "string", "default": "green"}
 ]
}

注意,新字段favorite_color的默认值是“green”。这允许用旧模式编码的数据可以用新模式读取。当反序列化用旧模式编码的数据时,新模式中指定的默认值将用于缺失的字段。如果在新字段中省略了默认值,新模式就不会与旧模式向后兼容,因为不清楚应该为新字段分配什么值,而旧数据中没有这个值。

请注意

Avro实现细节:查看Apache Avro项目中的ResolvingDecoder,以了解对于使用旧模式编码的数据,Avro如何使用更新的、向后兼容的模式对数据进行解码。

向前兼容性

正向兼容性意味着使用新模式生成的数据可以被使用最后一个模式的消费者读取,即使他们不能使用新模式的全部功能。例如,如果一个主题有三个模式按照X-2、X-1和X顺序变化,那么正向兼容性可以确保使用新模式X的生产者编写的数据可以由使用模式X或X-1的消费者处理,但不一定是X-2。如果使用新模式生成的数据需要由消费者使用所有已注册的模式(而不仅仅是最后两个模式)读取,则使用FORWARD_TRANSITIVE而不是FORWARD。例如,如果一个主题有三个模式按照X-2、X-1和X顺序变化,那么FORWARD_TRANSITIVE兼容性可以确保使用新模式X的生产者编写的数据可以被使用模式X、X-1或X-2的消费者处理。

  • 向前:使用模式X生成的数据可以由使用模式X或X-1的消费者准备好
  • FORWARD_TRANSITIVE:使用模式X生成的数据可以由模式X、X-1或X-2的消费者准备好

向前兼容模式修改的一个例子是添加一个新字段。在大多数数据格式中,即使在接收包含新字段的新事件时,为处理事件而编写的使用者也可以继续这样做。

考虑这样一个用例,其中消费者将应用程序逻辑绑定到模式的特定版本。当模式发展时,应用程序逻辑可能不会立即更新。因此,我们需要能够将具有新模式的数据投影到应用程序能够理解的(旧的)模式上。为了支持这个用例,我们可以以向前兼容的方式演进模式:用新模式编码的数据可以用旧模式读取。例如,我们在前一节中讨论的向后兼容性的新用户模式也与旧用户模式向前兼容。当将使用新模式编写的数据投影到旧模式时,只需删除新字段。有新模式把原始字段favorite_number(数字,不是颜色),它不会向前兼容原始用户模式,因为我们不知道如何填写的值favorite_number为新数据,因为最初的模式没有为该字段指定一个默认值。

完整的兼容性

完全兼容意味着模式既向后兼容又向前兼容。模式以完全兼容的方式发展:可以用新模式读取旧数据,也可以用最后一个模式读取新数据。例如,如果有三个模式为主体,改变X - 2, X - 1,然后X完全兼容性确保消费者使用新模式可以处理数据由生产者使用模式X或者X - 1,但不一定是X - 2,使用新的模式和数据由生产商可以由消费者使用模式处理X或者X - 1,但不一定是X - 2。如果新模式需要向前和向后兼容所有已注册的模式,而不仅仅是后两个模式,那么使用FULL_TRANSITIVE而不是FULL。例如,如果有三个模式为主体,改变X - 2, X - 1,然后X FULL_TRANSITIVE兼容性确保消费者使用新模式X可以处理数据由生产者使用X, X - 1,或X - 2,和数据由生产商使用新模式可以被消费者使用模式处理X, X - 1,或X - 2。

  • FULL:模式X和X-1之间的向后和向前兼容
  • FULL_TRANSITIVE:模式X、X-1和X-2之间的向后和向前兼容

在一些数据格式中,如JSON,没有完全兼容的更改。每个修改要么只向前兼容,要么只向后兼容。但是在其他数据格式中,比如Avro,可以使用默认值定义字段。在这种情况下,添加或删除具有默认值的字段是完全兼容的更改。

没有兼容性检查

无兼容性类型意味着禁用模式兼容性检查。

有时候我们会做出不兼容的改变。例如,将字段类型从Number修改为String。在这种情况下,您将需要升级所有的生产者和消费者同时新模式版本,或者更有可能的是,创建一个全新的话题,开始将应用程序迁移到使用新的话题和新的模式,避免了需要处理两个不兼容的版本相同的话题。

传递属性

一旦给定主题的模式有两个以上版本时,传递兼容性检查就非常重要。如果兼容性被配置为传递的,那么它将检查新模式与所有以前注册的模式的兼容性;否则,它只检查新模式与最新模式的兼容性。

例如,如果一个主题有三个模式以X-2、X-1和X的顺序变化,则:

  • 传递性:确保X-2 <==> X-1和X-1 <==> X和X-2 <==> X之间的兼容性
  • 非传递性:确保X-2 <==> X-1和X-1 <==> X之间的兼容性,但不一定是X-2 <==> X

请参考模式更改的示例,这些更改在增量上兼容,但在传递上不兼容。

Confluent模式注册表默认向后兼容类型是非传递的,这意味着它不是BACKWARD_TRANSITIVE。因此,仅针对最新模式检查新模式的兼容性。

升级客户端顺序

所配置的兼容性类型对升级客户机应用程序的顺序有影响,即,生产者使用模式将事件写入Kafka,消费者使用模式从Kafka读取事件。取决于兼容性类型:

BACKWARD_TRANSITIVE或BACKWARD_TRANSITIVE:不能保证使用旧模式的用户可以读取使用新模式生成的数据。因此,在开始生成新事件之前升级所有消费者。

FORWARD或FORWARD_TRANSITIVE:不能保证使用新模式的用户能够读取使用旧模式生成的数据。因此,首先将所有生产者升级到使用新模式,并确保使用旧模式生成的数据对消费者不可用,然后升级消费者。

FULL或FULL_TRANSITIVE:可以保证使用旧模式的消费者可以读取使用新模式生成的数据,使用新模式的消费者可以读取使用旧模式生成的数据。因此,您可以独立地升级生产者和消费者。

NONE:禁用兼容性检查。因此,您需要对何时升级客户机保持谨慎。

例子

上面的每一部分都有一个兼容性类型的示例。另一个参考是Avro兼容性测试套件,它提供了具有两个模式的多个测试用例,以及它们之间兼容性测试的各自结果。

 

原文:https://docs.confluent.io/current/schema-registry/avro.html

本文:https://pub.intelligentx.net/message-schema-evolution-and-compatibility

讨论:请加入知识星球或者小红圈【首席架构师圈】

Article
知识星球
 
微信公众号
 
视频号