Hadoop序列化与Writable接口(二)
上一篇文章Hadoop序列化与Writable接口(一)介绍了Hadoop序列化,Hadoop Writable接口以及如何定制自己的Writable类,在本文中我们继续Hadoop Writable类的介绍,这一次我们关注的是Writable实例序列化之后占用的字节长度,以及Writable实例序列化之后的字
上一篇文章Hadoop序列化与Writable接口(一)介绍了Hadoop序列化,Hadoop Writable接口以及如何定制自己的Writable类,在本文中我们继续Hadoop Writable类的介绍,这一次我们关注的是Writable实例序列化之后占用的字节长度,以及Writable实例序列化之后的字节序列的构成。
为什么要考虑Writable类的字节长度
大数据程序还需要考虑序列化对象占用磁盘空间的大小吗?也许你会认为大数据不是就是数据量很大吗,那磁盘空间一定是足够足够的大,一个序列化对象仅仅占用几个到几十个字节的空间,相对磁盘空间来说,当然是不需要考虑太多;如果你的磁盘空间不够大,还是不要玩大数据的好。
上面的观点没有什么问题,大数据应用自然需要足够的磁盘空间,但是能够尽量的考虑到不同Writable类占用磁盘空间的大小,高效的利用磁盘空间也未必就是没有必要的,选择适当的Writable类的另一个作用是通过减少Writable实例的字节数,可加快数据的读取和减少网络的数据传输。
Writable类占用的字节长度
下面的表格显示的是Hadoop对Java基本类型包装后相应的Writable类占用的字节长度:
Java基本类型 | Writable实现 | 序列化后字节数 (bytes) |
boolean | BooleanWritable | 1 |
byte | ByteWritable | 1 |
short | ShortWritable | 2 |
int | IntWritable | 4 |
? | VIntWritable | 1–5 |
float | FloatWritable | 4 |
long | LongWritable | 8 |
? | VLongWritable | 1–9 |
double | DoubleWritable | 8 |
不同的Writable类序列化后占用的字数长度是不一样的,需要综合考虑应用中数据特征选择合适的类型。对于整数类型有两种Writable类型可以选择,一种是定长(fixed-length)Writable类型,IntWritable和LongWritable;另一种是变长(variable-length)Writable类型,VIntWritable和VLongWritable。定长类型顾名思义使用固定长度的字节数表示,比如一个IntWritable类型使用4个长度的字节表示一个int;变长类型则根据数值的大小使用相应的字节长度表示,当数值在-112~127之间时使用1个字节表示,在-112~127范围之外的数值使用头一个字节表示该数值的正负符号以及字节长度(zero-compressed encoded integer)。
定长的Writable类型适合数值均匀分布的情形,而变长的Writable类型适合数值分布不均匀的情形,一般情况下变长的Writable类型更节省空间,因为大多数情况下数值是不均匀的,对于整数类型的Writable选择,我建议:
1. 除非对数据的均匀分布很有把握,否则使用变长Writable类型
2. 除非数据的取值区间确定在int范围之内,否则为了程序的可扩展性,请选择VLongWritable类型
整型Writable的字节序列
下面将以实例的方式演示Hadoop整型Writable对象占用的字节长度以及Writable对象序列化之后字节序列的结构,特别是变长整型Writable实例,请看下面的代码和程序输出:
package com.yoyzhou.example; import java.io.*; import org.apache.hadoop.io.*; import org.apache.hadoop.util.StringUtils; /** * Demos per how many bytes per each built-in Writable type takes and what does * their bytes sequences look like * * @author yoyzhou * */ public class WritableBytesLengthDemo { public static void main(String[] args) throws IOException { // one billion representations by different Writable object IntWritable int_b = new IntWritable(1000000000); LongWritable long_b = new LongWritable(1000000000); VIntWritable vint_b = new VIntWritable(1000000000); VLongWritable vlong_b = new VLongWritable(1000000000); // serialize writable object to byte array byte[] bs_int_b = serialize(int_b); byte[] bs_long_b = serialize(long_b); byte[] bs_vint_b = serialize(vint_b); byte[] bs_vlong_b = serialize(vlong_b); // print byte array in hex string and their length String hex = StringUtils.byteToHexString(bs_int_b); formatPrint("IntWritable", "1,000,000,000",hex, bs_int_b.length); hex = StringUtils.byteToHexString(bs_long_b); formatPrint("LongWritable", "1,000,000,000",hex, bs_long_b.length); hex = StringUtils.byteToHexString(bs_vint_b); formatPrint("VIntWritable", "1,000,000,000",hex, bs_vint_b.length); hex = StringUtils.byteToHexString(bs_vlong_b); formatPrint("VLongWritable", "1,000,000,000", hex, bs_vlong_b.length); } private static void formatPrint(String type, String param, String hex, int length) { String format = "%1$-50s %2$-16s with length: %3$2d%n"; System.out.format(format, "Byte array per " + type + "("+ param +") is:", hex, length); } /** * Utility method to serialize Writable object, return byte array * representing the Writable object * * */ public static byte[] serialize(Writable writable) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream dataOut = new DataOutputStream(out); writable.write(dataOut); dataOut.close(); return out.toByteArray(); } /** * Utility method to deserialize input byte array, return Writable object * * */ public static Writable deserialize(Writable writable, byte[] bytes) throws IOException { ByteArrayInputStream in = new ByteArrayInputStream(bytes); DataInputStream dataIn = new DataInputStream(in); writable.readFields(dataIn); dataIn.close(); return writable; } }
程序输出:
<code>Byte array per IntWritable(1,000,000,000) is: \ 3b9aca00 with length: 4 Byte array per LongWritable(1,000,000,000) is: \ 000000003b9aca00 with length: 8 Byte array per VIntWritable(1,000,000,000) is: \ 8c3b9aca00 with length: 5 Byte array per VLongWritable(1,000,000,000) is:\ 8c3b9aca00 with length: 5 </code>
从上面的输出我们可以看出:
+ 对1,000,000,000的表示不同的Writable占用了不同字节长度
+ 变长Writable类型并不总是比定长类型更加节省空间,当IntWritable占用4个字节、LongWritable占用8个字节时,相应的变长Writable需要一个额外的字节来存放正负信息和字节长度。所以回到前面的整数类型选择的问题上,选择出最合适的整数Writable类型,我们应该对数值的总体分布有一定的认识。
Text的字节序列
可以简单的认为Text类是java.lang.String的Writable类型,但是要注意的是Text类对于Unicode字符采用的是UTF-8编码,而不是使用Java Character类的UTF-16编码。
Java Character类采用遵循Unicode Standard version 4的UTF-16编码[1],每个字符采用定长的16位(两个字节)进行编码,对于代码点高于Basic Multilingual Plane(BMP,代码点U+0000~U+FFFF)的增补字符,采用两个代理字符进行表示。
Text类采用的UTF-8编码,使用变长的1~4个字节对字符进行编码。对于ASCII字符只使用1个字节,而对于High ASCII和多字节字符使用2~4个字节表示,我想Hadoop在设计时选择使用UTF-8而不是String的UTF-16就是基于上面的原因,为了节省字节长度/空间的考虑。
由于Text采用的是UTF-8编码,所以Text类没有提供String那样多的操作,并且在操作Text对象时,比如Indexing和Iteration,一定要注意这个区别,不过我们建议在进行Text操作时,如果可能可以将Text对象先转换成String,再进行操作。
Text类的字节序列表示为一个VIntWritable + UTF-8字节流,VIntWritable为整个Text的字符长度,UTF-8字节数组为真正的Text字节流。具体请看下面的代码片段:
...//omitted per conciseness Text myText = new Text("my text"); byte[] text_bs = serialize(myText); hex = StringUtils.byteToHexString(text_bs); formatPrint("Text", "\"my text\"", hex, text_bs.length); Text myText2 = new Text("我的文本"); byte[] text2_bs = serialize(myText2); hex = StringUtils.byteToHexString(text2_bs); formatPrint("Text", "\"我的文本\"", hex, text2_bs.length); ...
程序输出:
<code>Byte array per Text("my text") is: \ 076d792074657874 with length: 8 Byte array per Text("我的文本") is: \ 0ce68891e79a84e69687e69cac with length: 13 </code>
在上面的输出中,首个字节代表的该段Text/文本的长度,在UTF-8编码下“my text”占用的字节长度为7个字节(07),而中文“我的文本”的字节长度是12个字节(0c)。
定制Writable类的字节序列
本节中我们将使用上篇文章中的MyWritable类进行说明,回顾一下,MyWritable是一个由两个VLongWritable类构成的定制化Writable类型。
...//omitted per conciseness MyWritable customized = new MyWritable(new VLongWritable(1000), new VLongWritable(1000000000)); byte[] customized_bs = serialize(customized); hex = StringUtils.byteToHexString(customized_bs); formatPrint("MyWritable", "1000, 1000000000", hex, customized_bs.length); ...
程序输出:
<code>Byte array per MyWritable(1000, 1000000000) is: \ 8e03e88c3b9aca00 with length: 8 </code>
从输出我们可以很清楚的看到,定制的Writable类的字节序列实际上就是基本Writable类型的组合,输出“8e03e88c3b9aca00”的前三个字节是1000的VLongWritable的字节序列,“8c3b9aca00”是1000000000VLongWritable的字节序列,这一点可以从我们编写的MyWritable类的write方法中找到答案:
...//omitted per conciseness @Override public void write(DataOutput out) throws IOException { field1.write(out); field2.write(out); } ...
总结
本文通过实例介绍了Hadoop Writable类序列化时占用的字节长度,并分析了Writable类序列化后的字节序列的结构。需要注意的是Text类为了节省空间的目的采用了UTF-8的编码,而不是Java Character的UTF-16编码,自定义的Writable的字节序列与该Writable类的write()方法有关。
最后指出,Writable是Hadoop序列化的核心,理解Hadoop Writable的字节长度和字节序列对于选择合适的Writable对象以及在字节层面操作Writable对象至关重要。
参考资料
Tom White, Hadoop: The Definitive Guide, 3rd Edition
Hadoop序列化与Writable接口(一)
---EOF---
原文地址:Hadoop序列化与Writable接口(二), 感谢原作者分享。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

一、今日头条发布文章怎么才能有收益?今日头条发布文章获得更多收益方法!1.开通基础权益:原创文章选择投放广告可获得收益,视频必须要原创横屏才会有收益。2.开通百粉权益:粉丝量达到百粉以上,微头条、原创问答创作及问答均可获得收益。3.坚持原创作品:原创作品包含文章、微头条及问题等,要求300字以上。注意违规抄袭作品作为原创发布,会被扣信用分,即使有收益也会被扣除。4.垂直度:做专业领域一类的文章,不能随意跨领域写文章,会得不到合适的推荐,达不到作品的专和精,难以吸引粉丝读者。5.活跃度:活跃度高,

我们在电脑组装的过程中,安装过程虽然简单,不过往往都是在接线上遇到问题,经常有装机用户误将CPU散热器的供电线插到了SYS_FAN上,虽然风扇可以转动,不过在开机可能会有F1报错“CPUFanError”,同时也导致了CPU散热器无法智能调速。下面装机之家分享一下电脑主板上CPU_FAN、SYS_FAN、CHA_FAN、CPU_OPT接口知识科普。电脑主板上CPU_FAN、SYS_FAN、CHA_FAN、CPU_OPT接口知识科普1、CPU_FANCPU_FAN是CPU散热器专用接口,12V工作

Go语言作为一门现代化的、高效的编程语言,拥有丰富的编程范式和设计模式可以帮助开发者编写高质量、可维护的代码。本文将介绍Go语言中常见的编程范式和设计模式,并提供具体的代码示例。1.面向对象编程在Go语言中,可以使用结构体和方法实现面向对象编程。通过定义结构体和给结构体绑定方法,可以实现数据封装和行为绑定在一起的面向对象特性。packagemaini

PHP接口简介及其定义方式PHP是一种广泛应用于Web开发的开源脚本语言,具有灵活、简单、强大等特点。在PHP中,接口(interface)是一种定义多个类之间公共方法的工具,实现了多态性,让代码更加灵活和可重用。本文将介绍PHP接口的概念及其定义方式,同时提供具体的代码示例展示其用法。1.PHP接口概念接口在面向对象编程中扮演着重要的角色,定义了类应

序列化对Java性能的影响:序列化过程依赖于反射,会显着影响性能。序列化需要创建字节流存储对象数据,导致内存分配和处理成本。序列化大对象会消耗大量内存和时间。序列化后的对象在网络上传输时会增加负载量。

C++函数库序列化和反序列化指南序列化:创建输出流并将其转换为存档格式。将对象序列化到存档中。反序列化:创建输入流并将其从存档格式恢复。从存档中反序列化对象。实战示例:序列化:创建输出流。创建存档对象。创建对象并将其序列化到存档中。反序列化:创建输入流。创建存档对象。创建对象并从存档中反序列化。

Java允许在接口和抽象类中定义内部类,为代码重用和模块化提供灵活性。接口中的内部类可实现特定功能,而抽象类中的内部类可定义通用功能,子类提供具体实现。

鸿蒙系统作为华为推出的全新操作系统,在行业内引起了不小的轰动。作为华为在美国禁令之后的一次全新尝试,鸿蒙系统被寄予了厚望和期待。近日,我有幸得到了一部搭载鸿蒙系统的华为手机,经过一段时间的使用和实测,我将分享一些关于鸿蒙系统的功能实测和使用感受。首先,让我们来看一下鸿蒙系统的界面和功能。鸿蒙系统整体采用了华为自家的设计风格,简洁清晰,操作流畅。在桌面上,各种
