hadoop深入研究:(十八)——Avro schema兼容

Avro记录兼容性解析
本文深入探讨了Avro在Hadoop应用中的记录兼容性问题,包括如何处理读写schema不一致的情况,如增加或减少字段,以及通过别名实现字段名称的转换。通过实例演示了新旧版本schema之间的兼容策略。

转载请写明来源地址:http://blog.youkuaiyun.com/lastsweetop/article/details/9900129

所有源码在github上,https://github.com/lastsweetop/styhadoop

 

兼容条件

在实际的应用中,因为应用版本的问题经常遇到读和写的schema不相同的情况,幸运的是avro已经提供了相关的解决方案。

下面图示说明:

 

record兼容

在hadoop的实际应用中,更多是以record的形式进行交互,接下来我们重点讲解下record的兼容。
首先从读写schema的角度取考虑,读写schema的不同无外乎就两种,读的schema比写的schema多了一个field,读的schema比写的schema少了一个field,这两种情况处理起来都很简单。
先看下写的schema:
{
    "type":"record",
    "name":"com.sweetop.styhadoop.StringPair",
    "doc":"A pair of strings",
    "fields":[
        {"name":"left","type":"string"},
        {"name":"right","type":"string"}
    ]
}

增加了field的情况

增加了field后的schema
{
    "type":"record",
    "name":"com.sweetop.styhadoop.StringPair",
    "doc":"A pair of strings",
    "fields":[
        {"name":"left","type":"string"},
        {"name":"right","type":"string"},
        {"name":"description","type":"string","default":""}
    ]
}
用增加了field的schema取读数据。new GenericDatumReader<GenericRecord>(null, newSchema),第一个参数为写的schema,第二个参数为读的schema,
由于读的是avro datafile,schema已经在文件的头部指定,所以写的schema可以忽略掉。
    @Test
    public void testAddField() throws IOException {
        //将schema从newStringPair.avsc文件中加载
        Schema.Parser parser = new Schema.Parser();
        Schema newSchema = parser.parse(getClass().getResourceAsStream("/addStringPair.avsc"));

        File file = new File("data.avro");
        DatumReader<GenericRecord> reader = new GenericDatumReader<GenericRecord>(null, newSchema);
        DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(file, reader);
        for (GenericRecord record : dataFileReader) {
            System.out.println("left=" + record.get("left") + ",right=" + record.get("right") + ",description="
                    + record.get("description"));
        }
    }

输出结果为
left=L,right=R,description=
left=L,right=R,description=
description用默认值空字符串代替

减少了field的情况

减少了field的schema
{
    "type":"record",
    "name":"com.sweetop.styhadoop.StringPair",
    "doc":"A pair of strings",
    "fields":[
        {"name":"left","type":"string"}
    ]
}
用减少了field的schema取读取
    @Test
    public void testRemoveField() throws IOException {
        //将schema从StringPair.avsc文件中加载
        Schema.Parser parser = new Schema.Parser();
        Schema newSchema = parser.parse(getClass().getResourceAsStream("/removeStringPair.avsc"));

        File file = new File("data.avro");
        DatumReader<GenericRecord> reader = new GenericDatumReader<GenericRecord>(null, newSchema);
        DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(file, reader);
        for (GenericRecord record : dataFileReader) {
            System.out.println("left=" + record.get("left"));
        }
    }
输出结果为:
left=L
left=L
删除的field被忽略掉

新旧版本schema

如果从新旧版本的角度取考虑。
新版本schema比旧版本schema增加了一个字段
1.新版本取读旧版本的数据,使用新版本schema里新增field的默认值
2.旧版本读新版本的数据,新版本schema里新增field被旧版本的忽略掉
新版本schema比旧版半schema较少了一个字段
1.新版本读旧版本的数据,减少的field被新版本忽略掉
2.旧版本读新版本的数据,旧版本的schema使用起被删除field的默认值,如果没有就会报错,那么升级旧版本

别名

别名是另一个用于schema兼容的方法,可以将写的schema的field名字转换成读的schema的field,记住并不是加了aliases字段。
而是将写的filed的name属性变为aliases,读的时候只认name属性。
来看下加了别名的schema
{
    "type":"record",
    "name":"com.sweetop.styhadoop.StringPair",
    "doc":"A pair of strings",
    "fields":[
        {"name":"first","type":"string","aliases":["left"]},
        {"name":"second","type":"string","aliases":["right"]}
    ]
}
使用别名schema去读数据,这里不能再用left,right,而要用first,second
    @Test
    public void testAliasesField() throws IOException {
        //将schema从StringPair.avsc文件中加载
        Schema.Parser parser = new Schema.Parser();
        Schema newSchema = parser.parse(getClass().getResourceAsStream("/aliasesStringPair.avsc"));

        File file = new File("data.avro");
        DatumReader<GenericRecord> reader = new GenericDatumReader<GenericRecord>(null, newSchema);
        DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(file, reader);
        for (GenericRecord record : dataFileReader) {
            System.out.println("first=" + record.get("first")+",second="+record.get("second"));
        }
    }
输出结果
first=L,second=R
first=L,second=R
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值