AVRO 模式是否也在二进制部分进行编码?

Does AVRO schema also get encoded in the binary part?

Avro 文件包含纯文本模式,后跟二进制格式的数据。我想知道模式(或它的一部分)是否也存在于二进制部分?我有一种预感,模式(或只是字段名称)也被编码在二进制部分中,因为当我在 AVRO 文件的普通模式部分中进行一些更改时,我在使用 Avro-tool.jar.

当使用二进制编码时,整个文件都使用二进制格式。

文件以 4 个字节 header 开头,然后是包含一些元数据的地图。该地图包含一个 "avro.schema" 条目。此条目的值是存储为字符串的架构。在地图之后你会找到你的数据。

如果您手动编辑架构,读取更改其大小,则存储在该字符串之前的长度前缀将不连贯并且文件已损坏。

请参阅 Binary encoding specification 了解各种类型如何进行二进制编码。

我不确定您要达到什么目的,而且我很确定不应该这样做。但为了好玩,让我们尝试就地编辑架构。

对于这个例子,我将使用来自 avro 源代码树的 weather.avro 文件:

$ java -jar avro-tools-1.8.0.jar getmeta weather-orig.avro avro.codec null avro.schema {"type":"record","name":"Weather","namespace":"test","fields":[{"name":"station","type":"string"},{"name":"time","type":"long"},{"name":"temp","type":"int"}],"doc":"A weather reading."}

$ java -jar avro-tools-1.8.0.jar getschema weather-orig.avro { "type" : "record", "name" : "Weather", "namespace" : "test", "doc" : "A weather reading.", "fields" : [ {"name" : "station", "type" : "string"}, {"name" : "time", "type" : "long"}, {"name" : "temp", "type" : "int"} ] }

$ java -jar /avro-tools-1.8.0.jar tojson weather-orig.avro {"station":"011990-99999","time":-619524000000,"temp":0} {"station":"011990-99999","time":-619506000000,"temp":22} {"station":"011990-99999","time":-619484400000,"temp":-11} {"station":"012650-99999","time":-655531200000,"temp":111} {"station":"012650-99999","time":-655509600000,"temp":78}

好的。这是我们的源文件。简单明了,两个元数据条目和模式定义了三个字段。现在,我们将尝试了解事物如何以二进制形式存储以及我们如何编辑文件以更改重命名 station int station-id

$ hexdump weather-orig.avro -n 256 -C 00000000 4f 62 6a 01 04 14 61 76 72 6f 2e 63 6f 64 65 63 |Obj...avro.codec| 00000010 08 6e 75 6c 6c 16 61 76 72 6f 2e 73 63 68 65 6d |.null.avro.schem| 00000020 61 f2 02 7b 22 74 79 70 65 22 3a 22 72 65 63 6f |a..{"type":"reco| 00000030 72 64 22 2c 22 6e 61 6d 65 22 3a 22 57 65 61 74 |rd","name":"Weat| 00000040 68 65 72 22 2c 22 6e 61 6d 65 73 70 61 63 65 22 |her","namespace"| 00000050 3a 22 74 65 73 74 22 2c 22 66 69 65 6c 64 73 22 |:"test","fields"| 00000060 3a 5b 7b 22 6e 61 6d 65 22 3a 22 73 74 61 74 69 |:[{"name":"stati| 00000070 6f 6e 22 2c 22 74 79 70 65 22 3a 22 73 74 72 69 |on","type":"stri| 00000080 6e 67 22 7d 2c 7b 22 6e 61 6d 65 22 3a 22 74 69 |ng"},{"name":"ti| 00000090 6d 65 22 2c 22 74 79 70 65 22 3a 22 6c 6f 6e 67 |me","type":"long| 000000a0 22 7d 2c 7b 22 6e 61 6d 65 22 3a 22 74 65 6d 70 |"},{"name":"temp| 000000b0 22 2c 22 74 79 70 65 22 3a 22 69 6e 74 22 7d 5d |","type":"int"}]| 000000c0 2c 22 64 6f 63 22 3a 22 41 20 77 65 61 74 68 65 |,"doc":"A weathe| 000000d0 72 20 72 65 61 64 69 6e 67 2e 22 7d 00 b0 81 b3 |r reading."}....| 000000e0 c4 0a 0c f6 62 fa c9 38 fd 7e 52 00 a7 0a cc 01 |....b..8.~R.....| 000000f0 18 30 31 31 39 39 30 2d 39 39 39 39 39 ff a3 90 |.011990-99999...|

  • 前四个字节,4f 62 6a 01,是header
  • 接下来是 long 描述“元数据”映射的第一个块的大小。 long 使用 variable-length zig-zag 编码进行编码,因此这里 04 表示 2,与 getmeta 的输出一致。 (记得阅读 Avro 规范以了解各种数据类型是如何编码的)
  • 紧接着你会找到地图的第一个钥匙。键是一个字符串,字符串以字节长度为前缀。这里的0x14表示10个字节,也就是UTF-8编码时“avro.codec”的长度。
  • 然后您可以跳过接下来的 10 个字节并转到下一个元素。等等。您可以继续前进,直到找到 avro.schema 部分。
  • 在此字符串之后是地图值的长度(这是一个字符串,因为它是我们的模式)。那就是你要修改的。我们正在将 station 重命名为 station-id 所以你想在当前长度上加 3,所以 f2 02 现在应该是 f8 02 (还记得可变长度之字形编码吗?)。
  • 您现在可以更新架构字符串以添加“-id”
  • 享受

java -jar /home/cmathieu/Sources/avro-trunk/lang/java/tools/target/avro-tools-1.8.0-SNAPSHOT.jar tojson weather.avro {"station-id":"011990-99999","time":-619524000000,"temp":0} {"station-id":"011990-99999","time":-619506000000,"temp":22} {"station-id":"011990-99999","time":-619484400000,"temp":-11} {"station-id":"012650-99999","time":-655531200000,"temp":111} {"station-id":"012650-99999","time":-655509600000,"temp":78}

但正如我所说,您很可能不想这样做。