【AICon】AI 基础设施、LLM运维、大模型训练与推理,一场会议,全方位涵盖! >>> 了解详情
写点什么

使用 Apache Avro

  • 2011-03-11
  • 本文字数:12482 字

    阅读完需:约 41 分钟

Avro [1] 是最近加入到 Apache 的 Hadoop 家族的项目之一。为支持数据密集型应用,它定义了一种数据格式并在多种编程语言中支持这种格式。

Avro 提供的功能类似于其他编组系统,如 Thrift、Protocol Buffers 等。而 Avro 的主要不同之处在于 [2] :

  • “动态类型:Avro 无需生成代码。数据总是伴以模式定义,这样就可以在不生成代码、静态数据类型的情况下对数据进行所有处理。这样有利于构建通用的数据处理系统和语言。
  • 无标记数据: 由于在读取数据时有模式定义,这就大大减少了数据编辑所需的类型信息,从而减少序列化空间。
  • 不用手动分配的字段 ID: 当数据模式发生变化,处理数据时总是同时提供新旧模式,差异就可以用字段名来做符号化的分析。”

由于性能高、基本代码少和产出数据量精简等特点,Avro 周围展开了众多活动——许多 NoSQL 实现,包括 Hadoop、Cssandra 等,都把 Avro 整合到它们的客户端 API 和储存功能中;已经有人对 Avro 与其他流行序列化框架做了 Benchmark 测试并得到结果 [3] ,但是,目前尚无可供人们学习使用 Avro 的代码示例 [4]

在这篇文章中我将试着描述我使用 Avro 的经验,特别是:

  • 如何建立组件化 Avro 模式,使用组件搭建整体模式,分别保存在多个文件中
  • 在 Avro 中实现继承
  • 在 Avro 中实现多态
  • Avro 文档的向后兼容性。

组件化 Apache Avro 模式

如 Avro 规范所述 [5] Avro 文档模式定义成 JSON 文件。在当前 Avro 实现中,模式类需要一个文件(或字符串)来表示内部模式。同 XML 模式不一样,Avro 当前版本不支持向模式文档中导入(一个或多个)子模式,这往往迫使开发者编写非常复杂的模式定义 [6] ,并大大复杂化了模式的重用。下面的代码示例给出了一个有趣的拆分和组合模式文件的例子。它基于模式类提供的一个 toString()方法,该方法返回一个 JSON 字符串以表示给定的模式定义。用这种办法,我提供了一个简单 AvroUtils,能够自动完成上述功能:

复制代码
package com.navteq.avro.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.avro.Schema;
public class AvroUtils {
private static Map<String, Schema> schemas = new HashMap<String, Schema>();
private AvroUtils(){}
public static void addSchema(String name, Schema schema){
schemas.put(name, schema);
}
public static Schema getSchema(String name){
return schemas.get(name);
}
public static String resolveSchema(String sc){
String result = sc;
for(Map.Entry<String, Schema> entry : schemas.entrySet())
result = replace(result, entry.getKey(),
entry.getValue().toString());
return result;
}
static String replace(String str, String pattern, String replace) {
int s = 0;
int e = 0;
StringBuffer result = new StringBuffer();
while ((e = str.indexOf(pattern, s)) >= 0) {
result.append(str.substring(s, e));
result.append(replace);
s = e+pattern.length();
}
result.append(str.substring(s));
return result.toString();
}
public static Schema parseSchema(String schemaString){
String completeSchema = resolveSchema(schemaString);
Schema schema = Schema.parse(completeSchema);
String name = schema.getFullName();
schemas.put(name, schema);
return schema;
}
public static Schema parseSchema(InputStream in)throws IOException {
StringBuffer out = new StringBuffer();
byte[] b = new byte[4096];
for (int n; (n = in.read(b)) != -1;) {
out.append(new String(b, 0, n));
}
return parseSchema(out.toString());
}
public static Schema parseSchema(File file)throws IOException {
FileInputStream fis = new FileInputStream(file);
return parseSchema(fis);
}
}

清单 1 AvroUtils 类

这个简单实现基于全局(静态)模式注册表,它由完全限定的模式名及与之对应的对象构成。对于每一个要解析的新模式,该实现在注册表中搜索已保存的完全限定模式名,并且在给定的模式中做字符串替换。模式字符串被解析之后,它的全名和模式名都存储在注册表中。

下面是一个简单的测试,展示如何使用这个类:

复制代码
package com.navteq.avro.common;
import java.io.File;
import org.junit.Test;
public class AvroUtilsTest {
private static final String schemaDescription =
"{ \n" +
" \"namespace\": \"com.navteq.avro\", \n" +
" \"name\": \"FacebookUser\", \n" +
" \"type\": \"record\",\n" +
" \"fields\": [\n" +
" {\"name\": \"name\", \"type\": [\"string\", \"null\"] },\n" +
" {\"name\": \"num_likes\", \"type\": \"int\"},\n" +
" {\"name\": \"num_photos\", \"type\": \"int\"},\n" +
" {\"name\": \"num_groups\", \"type\": \"int\"} ]\n" +
"}";
private static final String schemaDescriptionExt =
" { \n" +
" \"namespace\": \"com.navteq.avro\", \n" +
" \"name\": \"FacebookSpecialUser\", \n" +
" \"type\": \"record\",\n" +
" \"fields\": [\n" +
" {\"name\": \"user\", \"type\": com.navteq.avro.FacebookUser },\n" +
" {\"name\": \"specialData\", \"type\": \"int\"} ]\n" +
"}";
@Test
public void testParseSchema() throws Exception{
AvroUtils.parseSchema(schemaDescription1);
Schema extended = AvroUtils.parseSchema(schemaDescriptionExt);
System.out.println(extended.toString(true));
}
}

清单 2 AvroUtils 测试

在这个测试中,第一个模式的完全限定名是 com.navteq.avro.FacebookUser,替换正常运行并打印出以下结果:

复制代码
{
"type" : "record",
"name" : "FacebookSpecialUser",
"namespace" : "com.navteq.avro",
"fields" : [ {
"name" : "user",
"type" : {
"type" : "record",
"name" : "FacebookUser",
"fields" : [ {
"name" : "name",
"type" : [ "string", "null" ]
}, {
"name" : "num_likes",
"type" : "int"
}, {
"name" : "num_photos",
"type" : "int"
}, {
"name" : "num_groups",
"type" : "int"
} ]
}
}, {
"name" : "specialData",
"type" : "int"
} ]
}

清单 3 AvroUtilsTest 的执行结果

使用 Apache Avro 实现继承

一种常见的定义数据的方法是通过继承——使用现有的数据定义并添加参数。虽然技术上 Avro 不支持继承 [7] ,但要是实现一个类继承的结构非常简单。

如果我们有一个基类的定义——FacebookUser,如下:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookUser",
"type": "record",
"fields": [
{"name": "name", "type": ["string", "null"] },
{"name": "num_likes", "type": "int"},
{"name": "num_photos", "type": "int"},
{"name": "num_groups", "type": "int"} ]
}

清单 4 Facebook 用户记录的定义

要创建一个 FacebookSpecialUser 定义非常简单,它大概是这样的:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUser",
"type": "record",
"fields": [
{"name": "user", "type": com.navteq.avro.FacebookUser },
{"name": "specialData", "type": "int"}
]
}

清单 5 Facebook 特殊的用户记录的定义

一个特殊的用户定义包含两个字段——Facebook 的用户类型的用户和一个 int 类型的数据字段。

特殊 Facebook 用户的简单测试类如下:

复制代码
package com.navteq.avro.inheritance;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericDatumWriter;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.io.BinaryEncoder;
import org.apache.avro.io.Decoder;
import org.apache.avro.io.DecoderFactory;
import org.apache.avro.io.Encoder;
import org.apache.avro.util.Utf8;
import org.junit.Before;
import org.junit.Test;
import com.navteq.avro.common.AvroUtils;
public class TestSimpleInheritance {
private Schema schema;
private Schema subSchema;
@Before
public void setUp() throws Exception {
subSchema = AvroUtils.parseSchema(new File("resources/facebookUser.avro"));
schema = AvroUtils.parseSchema(new File("resources/FacebookSpecialUser.avro"));
}
@Test
public void testSimpleInheritance() throws Exception{
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GenericDatumWriter<genericrecord> writer =<br></br> new GenericDatumWriter<genericrecord>(schema);<br></br> Encoder encoder = new BinaryEncoder(outputStream);<p> GenericRecord subRecord1 = new GenericData.Record(subSchema);</p><br></br> subRecord1.put("name", new Utf8("Doctor Who"));<br></br> subRecord1.put("num_likes", 1);<br></br> subRecord1.put("num_photos", 0);<br></br> subRecord1.put("num_groups", 423);<br></br> GenericRecord record1 = new GenericData.Record(schema);<br></br> record1.put("user", subRecord1);<br></br> record1.put("specialData", 1);<p> writer.write(record1, encoder);</p><p> GenericRecord subRecord2 = new GenericData.Record(subSchema);</p><br></br> subRecord2.put("name", new org.apache.avro.util.Utf8("Doctor WhoWho"));<br></br> subRecord2.put("num_likes", 2);<br></br> subRecord2.put("num_photos", 0);<br></br> subRecord2.put("num_groups", 424);<br></br> GenericRecord record2 = new GenericData.Record(schema);<br></br> record2.put("user", subRecord2);<br></br> record2.put("specialData", 2);<p> writer.write(record2, encoder);</p><p> encoder.flush();</p><p> ByteArrayInputStream inputStream =</p><br></br> new ByteArrayInputStream(outputStream.toByteArray());<br></br> Decoder decoder = DecoderFactory.defaultFactory().<br></br> createBinaryDecoder(inputStream, null);<br></br> GenericDatumReader<genericrecord> reader =<br></br> new GenericDatumReader<genericrecord>(schema);<br></br> while(true){<br></br> try{<br></br> GenericRecord result = reader.read(null, decoder);<br></br> System.out.println(result);<br></br> }<br></br> catch(EOFException eof){<br></br> break;<br></br> }<br></br> catch(Exception ex){<br></br> ex.printStackTrace();<br></br> }<br></br> }<br></br> }<br></br>}<a href="#_ftn8_7758" name="_ftnref8_7758">[8]</a></genericrecord></genericrecord></genericrecord></genericrecord>

清单 6 一个特殊的 Facebook 用户的测试类

运行这个测试类产生预期的结果:

复制代码
{"user": {"name": "Doctor Who", "num_likes": 1, "num_photos": 0,
"num_groups": 423}, "specialData": 1}
{"user": {"name": "Doctor WhoWho", "num_likes": 2, "num_photos": 0,
"num_groups": 424}, "specialData": 2}

清单 7 Facebook 特殊用户的测试结果

如果唯一需要的是有包含基础数据和其他参数的记录,此代码工作正常,但它不提供多态性——读取相同记录时,没办法知道到底读的是哪个类型的记录。

使用 ApacheAvro 实现多态性

与谷歌 protocol buffers 不同 [9] ,Avro 不支持可选参数 [10] ,上述继承的实现不适应于多态性的实现——这是由于必须具备特殊的数据参数。幸运的是,Avro 支持联合体,允许省略某些记录的参数。下面的定义可用于创建一个多态的纪录。对于基准纪录,我将使用清单 4 中描述的例子。为了扩展我们将使用以下两个定义:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUserExtension1",
"type": "record",
"fields": [
{"name": "specialData1", "type": "int"}
]
}

清单 8 首条扩展记录的定义

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUserExtension2",
"type": "record",
"fields": [
{"name": "specialData2", "type": "int"}
]
}

清单 9 第二条扩展记录的定义

有了以上两个定义一个多态记录可以定义如下:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUser",
"type": "record",
"fields": [
{"name": "type", "type": "string" },
{"name": "user", "type": com.navteq.avro.FacebookUser },
{"name": "extension1", "type":
[com.navteq.avro.FacebookSpecialUserExtension1, "null"]},
{"name": "extension2", "type":
[com.navteq.avro.FacebookSpecialUserExtension2, "null"]}
]
}

清单 10 Facebook 特殊用户的多态定义

这里扩展 1 和扩展 2 都是可选的且二者皆可。为了使处理更简单,我添加了一个类型字段,可以用来明确定义的记录类型。

下面给出一个更好的多态记录的定义:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUser1",
"type": "record",
"fields": [
{"name": "type", "type": "string" },
{"name": "user", "type": com.navteq.avro.FacebookUser },
{"name": "extension", "type":
[com.navteq.avro.FacebookSpecialUserExtension1,
com.navteq.avro.FacebookSpecialUserExtension2,
"null"]}
]
}

清单 11 Facebook 特殊用户的改进多态定义

下面给出一个多态 Facebook 特殊用户的简单测试类:

复制代码
package com.navteq.avro.inheritance;
<p>package com.navteq.avro.inheritance;</p><p>import java.io.ByteArrayInputStream;</p><br></br>import java.io.ByteArrayOutputStream;<br></br>import java.io.EOFException;<br></br>import java.io.File;<p>import org.apache.avro.Schema;</p><br></br>import org.apache.avro.generic.GenericData;<br></br>import org.apache.avro.generic.GenericDatumReader;<br></br>import org.apache.avro.generic.GenericDatumWriter;<br></br>import org.apache.avro.generic.GenericRecord;<br></br>import org.apache.avro.io.BinaryEncoder;<br></br>import org.apache.avro.io.Decoder;<br></br>import org.apache.avro.io.DecoderFactory;<br></br>import org.apache.avro.io.Encoder;<br></br>import org.apache.avro.io.JsonDecoder;<br></br>import org.apache.avro.io.JsonEncoder;<br></br>import org.apache.avro.util.Utf8;<br></br>import org.junit.Before;<br></br>import org.junit.Test;<p>import com.navteq.avro.common.AvroUtils;</p><p>public class TestInheritance {</p><p> private Schema FBUser;</p><br></br> private Schema base;<br></br> private Schema ext1;<br></br> private Schema ext2;<p> @Before</p><br></br> public void setUp() throws Exception {<p> base = AvroUtils.parseSchema(new File("resources/facebookUser.avro"));</p><br></br> ext1 = AvroUtils.parseSchema(<br></br> new File("resources/FacebookSpecialUserExtension1.avro"));<br></br> ext2 = AvroUtils.parseSchema(<br></br> new File("resources/FacebookSpecialUserExtension2.avro"));<br></br> FBUser = AvroUtils.parseSchema(new File("resources/FacebooklUserInheritance.avro"));<br></br>}<p> @Test</p><br></br> public void testInheritanceBinary() throws Exception{<br></br> ByteArrayOutputStream outputStream = new ByteArrayOutputStream();<br></br> GenericDatumWriter<genericrecord> writer =<br></br> new GenericDatumWriter<genericrecord>(FBUser);<br></br> Encoder encoder = new BinaryEncoder(outputStream);<p> GenericRecord baseRecord = new GenericData.Record(base);</p><br></br> baseRecord.put("name", new Utf8("Doctor Who"));<br></br> baseRecord.put("num_likes", 1);<br></br> baseRecord.put("num_photos", 0);<br></br> baseRecord.put("num_groups", 423);<br></br> GenericRecord FBrecord = new GenericData.Record(FBUser);<br></br> FBrecord.put("type", "base");<br></br> FBrecord.put("user", baseRecord);<p> writer.write(FBrecord, encoder);</p><p> baseRecord = new GenericData.Record(base);</p><br></br> baseRecord.put("name", new Utf8("Doctor WhoWho"));<br></br> baseRecord.put("num_likes", 1);<br></br> baseRecord.put("num_photos", 0);<br></br> baseRecord.put("num_groups", 423);<br></br> GenericRecord extRecord = new GenericData.Record(ext1);<br></br> extRecord.put("specialData1", 1);<br></br> FBrecord = new GenericData.Record(FBUser);<br></br> FBrecord.put("type", "extension1");<br></br> FBrecord.put("user", baseRecord);<br></br> FBrecord.put("extension", extRecord);<p> writer.write(FBrecord, encoder);</p><p> baseRecord = new GenericData.Record(base);</p><br></br> baseRecord.put("name", new org.apache.avro.util.Utf8("Doctor WhoWhoWho"));<br></br> baseRecord.put("num_likes", 2);<br></br> baseRecord.put("num_photos", 0);<br></br> baseRecord.put("num_groups", 424);<br></br> extRecord = new GenericData.Record(ext2);<br></br> extRecord.put("specialData2", 2);<br></br> FBrecord = new GenericData.Record(FBUser);<br></br> FBrecord.put("type", "extension2");<br></br> FBrecord.put("user", baseRecord);<br></br> FBrecord.put("extension", extRecord);<p> writer.write(FBrecord, encoder);</p><p> encoder.flush();</p><p> byte[] data = outputStream.toByteArray();</p><br></br> ByteArrayInputStream inputStream = new ByteArrayInputStream(data);<br></br> Decoder decoder =<br></br> DecoderFactory.defaultFactory().createBinaryDecoder(inputStream, null);<br></br> GenericDatumReader<genericrecord> reader =<br></br> new GenericDatumReader<genericrecord>(FBUser);<br></br> while(true){<br></br> try{<br></br> GenericRecord result = reader.read(null, decoder);<br></br> System.out.println(result);<br></br> }<br></br> catch(EOFException eof){<br></br> break;<br></br> }<br></br> catch(Exception ex){<br></br> ex.printStackTrace();<br></br> }<br></br> }<br></br> }<br></br>}</genericrecord></genericrecord></genericrecord></genericrecord>

清单 12 一条多态 Facebook 用户记录的测试类

运行这个测试类产生的预期结果:

复制代码
{"type": "base", "user": {"name": "Doctor Who", "num_likes": 1, "num_photos":
0, "num_groups": 423}, "extension": null}
{"type": "extension1", "user": {"name": "Doctor WhoWho", "num_likes": 1,
"num_photos": 0, "num_groups": 423}, "extension": {"specialData1": 1}}
{"type": "extension2", "user": {"name": "Doctor WhoWhoWho", "num_likes": 2,
"num_photos": 0, "num_groups": 424}, "extension": {"specialData2": 2}}

清单 13 多态 Facebook 用户记录测试的执行结果

使用 ApacheAvro 的向后兼容性

XML 的优势之一就是当模式定义使用可选参数扩展时具备向后兼容性。我们介绍一个第三扩展记录的定义来测试 Avro 的这个特性:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUserExtension3",
"type": "record",
"fields": [
{"name": "specialData3", "type": "int"}
]
}

清单 14 第三扩展记录的定义

多态记录的变更定义如下:

复制代码
{
"namespace": "com.navteq.avro",
"name": "FacebookSpecialUser11",
"type": "record",
"fields": [
{"name": "type", "type": "string" },
{"name": "user", "type": com.navteq.avro.FacebookUser },
{"name": "extension", "type":
[com.navteq.avro.FacebookSpecialUserExtension1,
com.navteq.avro.FacebookSpecialUserExtension2,
com.navteq.avro.FacebookSpecialUserExtension3,
"null"]}
]
}

清单 15 Facebook 特殊用户的改进多态定义

为了能读取清单 15 中记录定义中的记录,清单 12 中的代码在修改后(但仍然用清单 11 中的记录定义来写数据)生成下列结果:

复制代码
{"type": "base", "user": {"name": "Doctor Who", "num_likes": 1, "num_photos":
0, "num_groups": 423}, "extension": {"specialData3": 10}}
<u><span color="#0000a0">java.lang.ArrayIndexOutOfBoundsException</span></u>
      <span color="#ff0000">at java.lang.System.arraycopy(<u><span color="#0000a0">Native Method</span></u>)</span>
      <span color="#ff0000">at org.apache.avro.io.BinaryDecoder.doReadBytes(<u><span color="#0000a0">BinaryDecoder.java:331</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.io.BinaryDecoder.readString(<u><span color="#0000a0">BinaryDecoder.java:265</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.io.ValidatingDecoder.readString(<u><span color="#0000a0">ValidatingDecoder.java:99</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.readString(<u><span color="#0000a0">GenericDatumReader.java:318</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.readString(<u><span color="#0000a0">GenericDatumReader.java:312</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.read(<u><span color="#0000a0">GenericDatumReader.java:120</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.readRecord(<u><span color="#0000a0">GenericDatumReader.java:142</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.read(<u><span color="#0000a0">GenericDatumReader.java:114</span></u>)<br></br></span>      <span color="#ff0000">at org.apache.avro.generic.GenericDatumReader.read(<u><span color="#0000a0">GenericDatumReader.java:105</span></u>)<br></br></span>      <span color="#ff0000">at com.navteq.avro.inheritance.TestInheritance.testInheritanceBinary(<u><span color="#0000a0">TestInheritance.java:119</span></u>)<br></br></span>      <span color="#ff0000">at sun.reflect.NativeMethodAccessorImpl.invoke0(<u><span color="#0000a0">Native Method</span></u>)<br></br></span>      <span color="#ff0000">at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)<br></br></span>      <span color="#ff0000">at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)<br></br></span>      <span color="#ff0000">at java.lang.reflect.Method.invoke(Unknown Source)</span>

清单 16 多态 Facebook 用户记录对扩展定义测试的执行结果

虽然 Avro 提供了一个能够解决这个问题的 API——GenericDatumReader构造函数可以使用两个参数——分别用来写记录与读记录的模式,但这不总是解决向后兼容问题的一定可行的方法,因为它必须要记住用来写每条记录的所有模式。

一个更合适的解决方案是:从二进制编码器 / 解码器(它建立记录的二进制表象)切换到 JSON 编码器 / 解码器。在这种情况下代码有效,并产生以下结果:

复制代码
{"type": "base", "user": {"name": "Doctor Who", "num_likes": 1, "num_photos":
0, "num_groups": 423}, "extension": null}
{"type": "extension1", "user": {"name": "Doctor WhoWho", "num_likes": 1,
"num_photos": 0, "num_groups": 423}, "extension": {"specialData1": 1}}
{"type": "extension2", "user": {"name": "Doctor WhoWhoWho", "num_likes": 2,
"num_photos": 0, "num_groups": 424}, "extension": {"specialData2": 2}}

清单 17 应用 JSON 编码多态 Facebook 用户记录对扩展定义测试的执行结果

通过 JSON 的编码器,实际的数据转换成 JSON:

复制代码
{"type":"base","user":{"name":{"string":"Doctor
Who"},"num_likes":1,"num_photos":0,"num_groups":423},"extension":null}
{"type":"extension1","user":{"name":{"string":"Doctor
WhoWho"},"num_likes":1,"num_photos":0,"num_groups":423},"extension":{"FacebookSpecialUserExtension1":{"specialData1":1}}}
{"type":"extension2","user":{"name":{"string":"Doctor
WhoWhoWho"},"num_likes":2,"num_photos":0,"num_groups":424},"extension":{"FacebookSpecialUserExtension2":{"specialData2":2}}}

清单 18 JSON 编码下所转换的数据

还有一个需要考虑的问题,在我的测试中,同样的数据在二进制编码下产生的 Avro 记录的大小为 89 字节,而在 JSON 编码下产生了 473 字节。

结论

当前实现的 Avro 不直接支持模式的组件化或模式组件重用,但像本文中描述的一个简单的框架能够为这些特性提供支持。尽管 Avro 不直接支持多态性,文中利用适当的模式设计可以简单地实现多态数据模式。至于真正意义上向后兼容性问题,只有使用 JSON 编码的时候 Avro 才支持 [11] 。最后一点和 Avro 的特性没有多大关系,更多的是来自 JSON。最后一点严重限制了 Avro 适用性(如果向后兼容性是必须的),使其使用范围局限为一种高级的 JSON 编组和处理 API。

除了一般的(这里所用到的)Avro 方法,也可以使用一个特定的 Avro。这时候,可通过(Avro)生产特定的记录而非普通的记录。尽管有些说法指出 [12] Avro 的特定应用能够获得性能提升,以我使用当前 Avro 版本(1.4.1)的经验来看,两者有着同样的性能表现。


[1] http://hadoop.apache.org/avro/

[2] http://avro.apache.org/docs/1.4.1/

[3] http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

[4] 我在 Avro 编组 Avro Map Reduce 发现的几篇

[5] http://avro.apache.org/docs/current/spec.html

[6] 很有趣,Avro IDL 支持子 IDL

[7] 与明确支持类型定义中的基类型的 XML 不同

[8] 关于上面的代码需要指出的一点是,模式解析是在构造函数中完成的,原因在于构造解析是 Avro 实现中最昂贵的操作。

[9] http://code.google.com/p/protobuf/

[10] Avro 支持“Null”,这不同于可选参数,在 Avro 中“Null”表示某个属性没有值

[11] 或者如果有旧版本的模式

[12] http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

查看英文原文: Using Apache Avro


感谢马国耀对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2011-03-11 00:0022442

评论

发布
暂无评论
发现更多内容

区块链和人工智能是否可以保护森林不再被砍伐?

CECBC

模块四:如何设计业务高性能高可用存储架构?-- 学习总结

小鹿

Python Qt GUI设计:QPushButton、QRadioButton和QCheckBox按钮类(基础篇—12)

不脱发的程序猿

GUI设计 Python Qt QPushButton QRadioButton QCheckBox

和不安全访问say goodbye,手把手教大家如何给域名申请免费SSL证书

老表

https 云服务器 SSL证书 跟老表学云服务器

【死磕Java并发】-----J.U.C之AQS:AQS简介

chenssy

11月日更 死磕 Java 死磕 Java 并发

元宇宙:区块链时代的新名字

CECBC

微服务治理之自适应降载

万俊峰Kevin

微服务 微服务架构 服务治理 go-zero Go 语言

模块四作业 千万级学生管理系统的考试试卷存储方案

小朱

架构实战营

Prometheus Exporter (六)SNMP Exporter

耳东@Erdong

Prometheus snmp exporter 11月日更 snmp Exporter

040022-week4-design

InfoQ_70156470130f

Golang Gin 框架上传介绍(四)

liuzhen007

11月日更

hw4-千万级存储方案

WWH

架构实战营

个人博客网站升级

北漂码农有话说

网站

flutter中的列表的性能优化前奏

坚果

flutter 11月日更

主客体建模法

少个分号

21 K8S之容器存储接口CSI

穿过生命散发芬芳

k8s 11月日更

千万级考试试卷存储方案

张靖

#架构实战营

linux之拖拽上传文件

入门小站

Linux

GitLab CI/CD 自动化构建与发布实践

Se7en

考试试卷存储架构

AHUI

「架构实战营」

在线等比数列求和计算器

入门小站

工具

公司给我股份,有什么需要注意的吗?

石云升

创业公司 11月日更

[Pulsar] 一个消息的生命历程(一)

Zike Yang

Apache Pulsar 11月日更

模块四

侠客行

架构实战营 「架构实战营」

模块四作业

心怀架构

架构实战营模块四作业

spark99

架构实战营

用AI走出“沙丘”

白洞计划

模块4作业

忘记喝水的猫

架构训练营

Go语言学习查缺补漏ing Day3

Regan Yue

Go 语言 11月日更

spring的事务隔离级别是何许人也

卢卡多多

Spring Framework 隔离级别 11月日更

Lua-Table

Changing Lin

11月日更

使用Apache Avro_SOA_Boris Lublinsky_InfoQ精选文章