Avro - 序列化



数据序列化有两个目标:

  • 用于持久存储

  • 通过网络传输数据

什么是序列化?

序列化是将数据结构或对象状态转换为二进制或文本形式以通过网络传输数据或存储在某些持久存储中的过程。一旦数据通过网络传输或从持久存储中检索,就需要再次反序列化。序列化称为 **封送处理**,反序列化称为 **解封送处理**。

Java 中的序列化

Java 提供了一种称为 **对象序列化** 的机制,其中对象可以表示为字节序列,其中包括对象的数据以及有关对象类型和存储在对象中的数据类型的信息。

将序列化后的对象写入文件后,可以从文件中读取并反序列化。也就是说,表示对象及其数据的信息和字节可用于在内存中重新创建对象。

**ObjectInputStream** 和 **ObjectOutputStream** 类分别用于在 Java 中序列化和反序列化对象。

Hadoop 中的序列化

通常在像 Hadoop 这样的分布式系统中,序列化概念用于 **进程间通信** 和 **持久存储**。

进程间通信

  • 为了建立连接在网络中的节点之间的进程间通信,使用了 RPC 技术。

  • RPC 使用内部序列化将消息转换为二进制格式,然后再通过网络发送到远程节点。在另一端,远程系统将二进制流反序列化为原始消息。

  • RPC 序列化格式需要如下所示:

    • **紧凑** - 为了最大限度地利用网络带宽,这是数据中心中最稀缺的资源。

    • **快速** - 由于节点之间的通信在分布式系统中至关重要,因此序列化和反序列化过程应该很快,产生较少的开销。

    • **可扩展** - 协议会随着时间的推移而改变以满足新的需求,因此应该能够以受控的方式轻松地为客户端和服务器发展协议。

    • **互操作性** - 消息格式应支持以不同语言编写的节点。

持久存储

持久存储是一种数字存储设施,不会因电源中断而丢失数据。文件、文件夹、数据库是持久存储的示例。

Writable 接口

这是 Hadoop 中提供序列化和反序列化方法的接口。下表描述了这些方法:

序号 方法和描述
1

void readFields(DataInput in)

此方法用于反序列化给定对象字段。

2

void write(DataOutput out)

此方法用于序列化给定对象字段。

Writable Comparable 接口

它是 **Writable** 和 **Comparable** 接口的组合。此接口继承了 Hadoop 的 **Writable** 接口以及 Java 的 **Comparable** 接口。因此,它提供了数据序列化、反序列化和比较的方法。

序号 方法和描述
1

int compareTo(class obj)

此方法将当前对象与给定对象 obj 进行比较。

除了这些类之外,Hadoop 还支持许多实现 WritableComparable 接口的包装类。每个类都包装了一个 Java 基本类型。Hadoop 序列化的类层次结构如下所示:

Hadoop Serialization Hierarchy

这些类可用于序列化 Hadoop 中各种类型的数据。例如,让我们考虑一下 **IntWritable** 类。让我们看看如何使用此类在 Hadoop 中序列化和反序列化数据。

IntWritable 类

此类实现了 **Writable、Comparable** 和 **WritableComparable** 接口。它在其内部包装了一个整数数据类型。此类提供了用于序列化和反序列化整数类型数据的方法。

构造函数

序号 摘要
1 IntWritable()
2 IntWritable( int value)

方法

序号 摘要
1

int get()

使用此方法,您可以获取当前对象中存在的整数值。

2

void readFields(DataInput in)

此方法用于反序列化给定 **DataInput** 对象中的数据。

3

void set(int value)

此方法用于设置当前 **IntWritable** 对象的值。

4

void write(DataOutput out)

此方法用于将当前对象中的数据序列化到给定的 **DataOutput** 对象。

在 Hadoop 中序列化数据

下面讨论了序列化整数类型数据的过程。

  • 通过在其内部包装一个整数值来实例化 **IntWritable** 类。

  • 实例化 **ByteArrayOutputStream** 类。

  • 实例化 **DataOutputStream** 类并将 **ByteArrayOutputStream** 类的对象传递给它。

  • 使用 **write()** 方法序列化 IntWritable 对象中的整数值。此方法需要一个 DataOutputStream 类的对象。

  • 序列化后的数据将存储在作为参数传递给 **DataOutputStream** 类(在实例化时)的字节数组对象中。将对象中的数据转换为字节数组。

示例

以下示例演示了如何在 Hadoop 中序列化整数类型的数据:

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;

public class Serialization {
   public byte[] serialize() throws IOException{
		
      //Instantiating the IntWritable object
      IntWritable intwritable = new IntWritable(12);
   
      //Instantiating ByteArrayOutputStream object
      ByteArrayOutputStream byteoutputStream = new ByteArrayOutputStream();
   
      //Instantiating DataOutputStream object
      DataOutputStream dataOutputStream = new
      DataOutputStream(byteoutputStream);
   
      //Serializing the data
      intwritable.write(dataOutputStream);
   
      //storing the serialized object in bytearray
      byte[] byteArray = byteoutputStream.toByteArray();
   
      //Closing the OutputStream
      dataOutputStream.close();
      return(byteArray);
   }
	
   public static void main(String args[]) throws IOException{
      Serialization serialization= new Serialization();
      serialization.serialize();
      System.out.println();
   }
}

在 Hadoop 中反序列化数据

下面讨论了反序列化整数类型数据的过程:

  • 通过在其内部包装一个整数值来实例化 **IntWritable** 类。

  • 实例化 **ByteArrayOutputStream** 类。

  • 实例化 **DataOutputStream** 类并将 **ByteArrayOutputStream** 类的对象传递给它。

  • 使用 IntWritable 类的 **readFields()** 方法反序列化 **DataInputStream** 对象中的数据。

  • 反序列化的数据将存储在 IntWritable 类的对象中。您可以使用此类的 **get()** 方法检索此数据。

示例

以下示例演示了如何在 Hadoop 中反序列化整数类型的数据:

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;

import org.apache.hadoop.io.IntWritable;

public class Deserialization {

   public void deserialize(byte[]byteArray) throws Exception{
   
      //Instantiating the IntWritable class
      IntWritable intwritable =new IntWritable();
      
      //Instantiating ByteArrayInputStream object
      ByteArrayInputStream InputStream = new ByteArrayInputStream(byteArray);
      
      //Instantiating DataInputStream object
      DataInputStream datainputstream=new DataInputStream(InputStream);
      
      //deserializing the data in DataInputStream
      intwritable.readFields(datainputstream);
      
      //printing the serialized data
      System.out.println((intwritable).get());
   }
   
   public static void main(String args[]) throws Exception {
      Deserialization dese = new Deserialization();
      dese.deserialize(new Serialization().serialize());
   }
}

Hadoop 相对于 Java 序列化的优势

Hadoop 基于 Writable 的序列化能够通过重用 Writable 对象来减少对象创建开销,这在 Java 的原生序列化框架中是不可能的。

Hadoop 序列化的缺点

要序列化 Hadoop 数据,有两种方法:

  • 您可以使用 Hadoop 原生库提供的 **Writable** 类。

  • 您还可以使用 **Sequence 文件**,这些文件以二进制格式存储数据。

这两种机制的主要缺点是 **Writable** 和 **Sequence 文件** 只有 Java API,并且不能用任何其他语言编写或读取。

因此,使用上述两种机制在 Hadoop 中创建的任何文件都无法被任何其他第三方语言读取,这使得 Hadoop 成为一个有限的盒子。为了解决这个缺点,Doug Cutting 创建了 **Avro**,这是一种 **与语言无关的数据结构**。

广告