gpt4 book ai didi

protocol-buffers - Avro 模式演变是否需要访问新旧模式?

转载 作者:行者123 更新时间:2023-12-04 01:23:56 26 4
gpt4 key购买 nike

如果我使用模式版本 1 序列化对象,然后将模式更新到版本 2(例如通过添加字段) - 稍后反序列化对象时是否需要使用模式版本 1?理想情况下,我只想使用模式版本 2,并使反序列化的对象具有在对象最初序列化后添加到模式的字段的默认值。

也许一些代码会更好地解释......

架构1:

{"type": "record",
"name": "User",
"fields": [
{"name": "firstName", "type": "string"}
]}

架构2:
{"type": "record",
"name": "User",
"fields": [
{"name": "firstName", "type": "string"},
{"name": "lastName", "type": "string", "default": ""}
]}

使用通用的非代码生成方法:
// serialize
ByteArrayOutputStream out = new ByteArrayOutputStream();
Encoder encoder = EncoderFactory.get().binaryEncoder(out, null);
GenericDatumWriter writer = new GenericDatumWriter(schema1);
GenericRecord datum = new GenericData.Record(schema1);
datum.put("firstName", "Jack");
writer.write(datum, encoder);
encoder.flush();
out.close();
byte[] bytes = out.toByteArray();

// deserialize
// I would like to not have any reference to schema1 below here
DatumReader<GenericRecord> reader = new GenericDatumReader<GenericRecord>(schema2);
Decoder decoder = DecoderFactory.get().binaryDecoder(bytes, null);
GenericRecord result = reader.read(null, decoder);

导致 EOFException。使用 jsonEncoder导致 AvroTypeException。

我知道如果我将 schema1 和 schema2 都传递给 GenericDatumReader 它将起作用构造函数,但我不想保留所有以前模式的存储库,也不想以某种方式跟踪使用哪个模式来序列化每个特定对象。

我还尝试了代码生成方法,首先使用从 schema1 生成的 User 类序列化到文件:
User user = new User();
user.setFirstName("Jack");
DatumWriter<User> writer = new SpecificDatumWriter<User>(User.class);
FileOutputStream out = new FileOutputStream("user.avro");
Encoder encoder = EncoderFactory.get().binaryEncoder(out, null);
writer.write(user, encoder);
encoder.flush();
out.close();

然后将架构更新到版本 2,重新生成 User 类,并尝试读取文件:
DatumReader<User> reader = new SpecificDatumReader<User>(User.class);
FileInputStream in = new FileInputStream("user.avro");
Decoder decoder = DecoderFactory.get().binaryDecoder(in, null);
User user = reader.read(null, decoder);

但它也会导致 EOFException。

只是为了比较,我正在尝试做的似乎适用于 protobufs ......

格式:
option java_outer_classname = "UserProto";
message User {
optional string first_name = 1;
}

连载:
UserProto.User.Builder user = UserProto.User.newBuilder();
user.setFirstName("Jack");
FileOutputStream out = new FileOutputStream("user.data");
user.build().writeTo(out);

添加可选的 last_name 以格式化、重新生成 UserProto 和反序列化:
FileInputStream in = new FileInputStream("user.data");
UserProto.User user = UserProto.User.parseFrom(in);

正如预期的那样, user.getLastName()是空字符串。

Avro 可以做这样的事情吗?

最佳答案

Avro 和 Protocol Buffers 有不同的处理版本控制的方法,哪种方法更好取决于您的用例。

在 Protocol Buffer 中,您必须用数字明确标记每个字段,这些数字与字段的值一起存储在二进制表示中。因此,只要您从不更改后续模式版本中数字的含义,您仍然可以解码以不同模式版本编码的记录。如果解码器看到一个它无法识别的标签号,它可以简单地跳过它。

Avro 采用了不同的方法:没有标签号,二进制布局完全由执行编码的程序决定——这是作者的模式。 (一个记录的字段只是简单地以二进制编码一个接一个地存储,没有任何标记或分隔符,顺序由作者的模式决定。)这使编码更加紧凑,并且您不必手动维护标签中的标签架构。但这确实意味着对于读取,您必须知道写入数据的确切模式,否则您将无法理解它。

如果了解作者的模式对于解码 Avro 至关重要,那么读者的模式是它之上的一层漂亮。如果您在需要读取 Avro 数据的程序中进行代码生成,您可以从读取器的架构中执行代码生成,这样您就不必在每次编写器的架构更改时重新生成它(假设它以一种可以更改的方式进行更改)得到解决)。但这并不能使您不必知道作者的架构。

优点缺点

Avro 的方法在您有许多已知具有完全相同模式版本的记录的环境中很好,因为您可以在文件开头的元数据中包含模式,并且知道接下来的一百万条记录可以全部使用该模式解码。这在 MapReduce 上下文中经常发生,这解释了为什么 Avro 来自 Hadoop 项目。

Protocol Buffers 的方法可能更适合 RPC,其中单个对象通过网络发送(作为请求参数或返回值)。如果你在这里使用 Avro,你可能有不同的客户端和不同的服务器,它们都具有不同的架构版本,所以你必须用它正在使用的 Avro 架构版本标记每个二进制编码的 blob,并维护一个架构注册表。那时您可能已经使用了 Protocol Buffers 的内置标记。

关于protocol-buffers - Avro 模式演变是否需要访问新旧模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12165589/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com