簡體   English   中英

Avro架構演變是否需要訪問新舊架構?

[英]Does Avro schema evolution require access to both old and new schemas?

如果我使用模式版本1序列化對象,然后將模式更新為版本2(例如通過添加字段) - 我是否需要在稍后反序列化對象時使用模式版本1? 理想情況下,我只想使用模式版本2,並且反序列化對象具有在最初序列化對象后添加到模式的字段的默認值。

也許一些代碼會更好地解釋......

schema1:

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

SCHEMA2:

{"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有不同的方法來處理版本控制,哪種方法更好取決於您的用例。

在協議緩沖區中,您必須使用數字顯式標記每個字段,並將這些數字與字段的值一起存儲在二進制表示中。 因此,只要您在后續架構版本中永遠不更改數字的含義,您仍然可以解碼以不同架構版本編碼的記錄。 如果解碼器看到它無法識別的標簽號,則可以簡單地跳過它。

Avro采用了不同的方法:沒有標簽號,而二進制布局完全取決於執行編碼的程序 - 這是編寫器的架構。 (記錄的字段只是在二進制編碼中一個接一個地存儲,沒有任何標記或分隔符,並且順序由編寫者的模式決定。)這使得編碼更緊湊,並且使您不必手動維護標記。架構。 但它確實意味着,對於閱讀,您必須知道數據寫入的確切模式,否則您將無法理解它。

如果知道編寫器的模式對於解碼Avro至關重要,那么讀者的模式就是它的一層好處。 如果您在需要讀取Avro數據的程序中進行代碼生成,則可以從讀取器的模式中執行codegen,這樣您就不必在每次編寫器的模式更改時重新生成它(假設它以可以的方式更改)得到解決)。 但它不會讓你不必知道作者的架構。

優點缺點

Avro的方法在一個環境中是很好的,在這種環境中你有許多已知具有完全相同的模式版本的記錄,因為你可以只在文件開頭的元數據中包含模式,並且知道下一百萬條記錄都可以使用該架構解碼。 這在MapReduce上下文中發生了很多,這解釋了為什么Avro從Hadoop項目中走出來。

協議緩沖區的方法可能更適用於RPC,其中單個對象通過網絡發送(作為請求參數或返回值)。 如果您在此處使用Avro,則可能有不同的客戶端和不同的服務器都具有不同的架構版本,因此您必須使用它正在使用的Avro架構版本標記每個二進制編碼的blob,並維護架構的注冊表。 此時,您可能還使用了Protocol Buffers的內置標記。

要執行您要執行的操作,您需要通過允許空值使last_name字段成為可選字段。 last_name的類型應為[“null”,“string”]而不是“string”

我試圖繞過這個問題。 我把它放在這里:

我還嘗試使用兩個模式一個模式,只是使用Avro的refection API將另一個列添加到另一個模式。 我有以下架構:

Employee (having name, age, ssn)
ExtendedEmployee (extending Employee and having gender column)

我假設現在早些時候有Employee對象的文件也有ExtendedEmployee對象,我試圖將該文件讀取為:

    RecordHandler rh = new RecordHandler();
    if (rh.readObject(employeeSchema, dbLocation) instanceof Employee) {
        Employee e = (Employee) rh.readObject(employeeSchema, dbLocation);
        System.out.print(e.toString());
    } else if (rh.readObject(schema, dbLocation) instanceof ExtendedEmployee) {
        ExtendedEmployee e = (ExtendedEmployee) rh.readObject(schema, dbLocation);
        System.out.print(e.toString());
    }

這解決了這里的問題。 但是,我很想知道是否有一個API,我們可以讓ExtendedEmployee模式讀取Employee的對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM