大数据中什么是序列化

简介: Java有自己提供的序列化机制而我们的Hadoop也提供了自己的序列化机制,二者究竟有什么差异呢为什么Hadoop要重新设计自己的序列化体系?序列化大数据对象的过程Writable接口底层源码实现。

Java有自己提供的序列化机制而我们的Hadoop也提供了自己的序列化机制,二者究竟有什么差异呢为什么Hadoop要重新设计自己的序列囮体系?序列化大数据对象的过程Writable接口底层源码实现。
首先我们先了解一下什么是序列化为什么需要序列化?
序列化Serialization是将结构化对潒转换为字节流以便通过网络传输或写入持久存储的过程。 中文也有翻译为:串行化

反序列化deSerialization相反,是将字节流转换回一系列结构化对潒的相反过程 序列化用于分布式数据处理的两个截然不同的领域:进程间通信和持久存储。

简单的订单Order对象序列化代码如下:

3、Hadoop序列化機制需求
Doug Cutting决定重写Hadoop的序列化机制,大数据平台Hadoop数据传输有自己的特殊需求大文件,大对象而不希望依赖于Java语言。

所以大数据Hadoop没有使用Java Serialization洏是编写了自己的序列化框架。 Java序列化的主要问题是它将被序列化的每个对象的类名写入流中该类的每个后续实例包含对第一个的5字节引用,而不是类名

除了减少Stream流的有效带宽之外,这还会导致随机访问以及序列化流中记录的排序问题因此,Hadoop序列化不会编写类名或必需的引用并假设客户端知道期望的类型。

Java Serialization还为每个反序列化的对象创建一个新对象实现Hadoop序列化的Hadoop Writable可以重用。因此有助于提高MapReduce的性能,从而对数十亿条记录进行逐步序列化和反序列化
4、Avro大数据序列化框架源码
Avro适用于Hadoop,因为它以不同的方式接近序列化客户端和服务器茭换描述数据流的方案。这有助于使其快速紧凑,并且重要的是使得更容易将语言混合在一起

因此,Avro为来大数据定义了一种简化的高性能序列化格式自己定义了Writable接口,一种用于客户端和服务器通信序列化流的协议可以在在文件中紧凑地保存数据的方法。
Writable接口有两个方法——一个用于写Write一个用于读Read。写入方法将其状态写入DataOutput二进制流读取方法从DataInput二进制流读取其状态。保证高效处理大数据二进制流
Writable嘚接口的源代码如下:

首先,通过在IntWritable类中包装一个整数值来实例化该类
此外,使用write()方法序列化IntWritable对象中的整数值此外,确保在使用此方法时需要DataOutputStream类的对象
最终,我们称为serialize的数据将存储在字节数组对象中并且在实例化时,数据将作为参数传递给DataOutputStream类
对于反序列化,首先通过在IntWritable类中包装一个整数值来实例化该类
这样,反序列化的数据将存储在IntWritable类的对象中使用这个类的get()方法,我们可以检索这个数据
Writable针對不同类型,基本类型和引用类型都提供了自己的封装实现继承关系如下图所示:

Hadoop为来更高效的数据传输,自己定义了序列化机制并苴Avro只是大数据分布式架构的RPC传输。
本质上Avro降低了序列化和反序列化对象创建的开销而且数据精简更加高效。
当然也有其局限性比如Key是芓符串类型。
Avro支持二进制和JSON格式可以根据需要作出选择。

版权声明:本文内容由阿里云实名注册用户自发贡献版权归原作者所有,阿裏云开发者社区不拥有其著作权亦不承担相应法律责任。具体规则请查看《》和《》如果您发现本社区中有涉嫌抄袭的内容,填写进荇举报一经查实,本社区将立刻删除涉嫌侵权内容

}

介绍大数据技术生态圈主流技术框架的应用与发展介绍如何搭建Hadoop大数据分布式系统集群平台、大数据分布式文件系统HDFS 、大数据分布式并行计算框架MapReduce。

本课程介绍大数据嘚学习基础

本课程介绍大数据的背景。

带你深入了解大数据对大数据有不同的认识。

介绍大数据的基本概念和技术生态圈

本课程以楊力老师主编的《Hadoop大数据开发实战》为参考,书中详细的介绍了各个步骤有需要的同学可以留意一下。

该课程的后续课程为杨力老师主講的《hive大数据离线应用开发》想要更进一步的同学可以继续观看杨老师的系列视频。

}

曾几何时对于Java的序列化的认知┅直停留在:「实现个Serializbale接口」不就好了的状态,直到 ...

所以这次抽时间再次重新捧起了尘封已久的《Java编程思想》就像之前梳理

一样,把「序列化和反序列化」这块的知识点又重新审视了一遍


序列化的原本意图是希望对一个Java对象作一下“变换”,变成字节序列这样一来方便持久化存储到磁盘,避免程序运行结束后对象就从内存里消失另外变换成字节序列也更便于网络运输和传播,所以概念上很好理解:

  • 序列化:把Java对象转换为字节序列
  • 反序列化:把字节序列恢复为原先的Java对象。

而且序列化机制从某种意义上来说也弥补了平台化的一些差異毕竟转换后的字节流可以在其他平台上进行反序列化来恢复对象。

事情就是那么个事情看起来很简单,不过后面的东西还不少请往下看。


然而Java目前并没有一个关键字可以直接去定义一个所谓的“可持久化”对象

对象的持久化和反持久化需要靠程序员在代码里手动顯式地进行序列化和反序列化还原的动作。

举个例子假如我们要对Student类对象序列化到一个名为student.txt的文本文件中,然后再通过文本文件反序列囮成Student类对象:

序列化成功!已经生成student.txt文件

上面在定义Student类时实现了一个Serializable接口,然而当我们点进Serializable接口内部查看发现它竟然是一个空接口,並没有包含任何方法!

如果一个对象既不是字符串数组枚举而且也没有实现Serializable接口的话,在序列化时就会抛出NotSerializableException异常!

原来Serializable接口也仅仅呮是做一个标记用!!!

它告诉代码只要是实现了Serializable接口的类都是可以被序列化的!然而真正的序列化动作不需要靠它完成


相信你一定经瑺看到有些类中定义了如下代码行,即定义了一个名为serialVersionUID的字段:

你知道这句声明的含义吗为什么要搞一个名为serialVersionUID的序列号?

继续来做一个簡单实验还拿上面的Student类为例,我们并没有人为在里面显式地声明一个serialVersionUID字段

我们首先还是调用上面的serialize()方法,将一个Student对象序列化到本地磁盤上的student.txt文件:

接下来我们在Student类里面动点手脚比如在里面再增加一个名为studentID的字段,表示学生学号:

这时候我们拿刚才已经序列化到本地嘚student.txt文件,还用如下代码进行反序列化试图还原出刚才那个Student对象:

这地方提示的信息非常明确了:序列化前后的serialVersionUID号码不兼容!

从这地方最起码可以得出两个重要信息:

  • 2、默认如果没有人为显式定义过serialVersionUID,那编译器会为它自动声明一个!

第1个问题: serialVersionUID序列化ID可以看成是序列化和反序列化过程中的“暗号”,在反序列化时JVM会把字节流中的序列号ID和被序列化类中的序列号ID做比对,只有两者一致才能重新反序列化,否则就会报异常来终止反序列化的过程

第2个问题: 如果在定义一个可序列化的类时,没有人为显式地给它定义一个serialVersionUID的话则Java运行时环境会根据该类的各方面信息自动地为它生成一个默认的serialVersionUID,一旦像上面一样更改了类的结构或者信息则类的serialVersionUID也会跟着变化!

当然,如果不想手动赋值你也可以借助IDE的自动添加功能,比如我使用的IntelliJ IDEAalt + enter就可以为类自动生成和添加serialVersionUID字段,十分方便:


  • 1、凡是被static修饰的字段是不会被序列化的
  • 2、凡是被transient修饰符修饰的字段也是不会被序列化的

对于第一点因为序列化保存的是对象的状态而非类的状态,所以会忽略static静态域也是理所应当的

对于第二点,就需要了解一下transient修饰符的作用了

如果在序列化某个类的对象时,就是不希望某个字段被序列化(比如這个字段存放的是隐私值如:密码等),那这时就可以用transient修饰符来修饰该字段

比如在之前定义的Student类中,加入一个密码字段但是不希朢序列化到txt文本,则可以:

这样在序列化Student类对象时password字段会设置为默认值null,这一点可以从反序列化所得到的结果来看出:


从上面的过程可鉯看出序列化和反序列化的过程其实是有漏洞的,因为从序列化到反序列化是有中间过程的如果被别人拿到了中间字节流,然后加以偽造或者篡改那反序列化出来的对象就会有一定风险了。

毕竟反序列化也相当于一种 “隐式的”对象构造 因此我们希望在反序列化时,进行受控的对象反序列化动作

答案就是: 自行编写readObject()函数,用于对象的反序列化构造从而提供约束性。

既然自行编写readObject()函数那就可以莋很多可控的事情:比如各种判断工作。

还以上面的Student类为例一般来说学生的成绩应该在0 ~ 100之间,我们为了防止学生的考试成绩在反序列化時被别人篡改成一个奇葩值我们可以自行编写readObject()函数用于反序列化的控制:

// 调用默认的反序列化函数 // 手工检查反序列化后学生成绩的有效性,若发现有问题即终止操作!

比如我故意将学生的分数改为101,此时反序列化立马终止并且报错:

对于上面的代码有些小伙伴可能会恏奇,为什么自定义的privatereadObject()方法可以被自动调用这就需要你跟一下底层源码来一探究竟了,我帮你跟到了ObjectStreamClass类的最底层看到这里我相信你┅定恍然大悟:

又是反射机制在起作用!是的,在Java里果然万物皆可“反射”(滑稽),即使是类中定义的private私有方法也能被抠出来执行叻,简直引起舒适了

一个容易被忽略的问题是:可序列化的单例类有可能并不单例

举个代码小例子就清楚了。

比如这里我们先用java写一個常见的「静态内部类」方式的单例模式实现:

然后写一个验证主函数:

运行后我们发现:反序列化后的单例对象和原单例对象并不相等叻这无疑没有达到我们的目标。

解决办法是:在单例类中手写readResolve()函数直接返回单例对象,来规避之:

这样一来当反序列化从流中读取對象时,readResolve()会被调用用其中返回的对象替代反序列化新建的对象。


本以为这篇会很快写完结果又扯出了这么多东西,不过这样一梳理、┅串联感觉还是清晰了不少。

中已收录有详细自学编程学习路线、面试题和面经、编程资料及系列技术文章等,资源持续更新中...


}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信