Hadoop Serialization -- hadoop序列化具体解释 (2)【Text,BytesWritable,NullWritable】

回想:

回想序列化,事实上原书的结构非常清晰,我截图给出书中的章节结构:

序列化最基本的,最底层的是实现writable接口,wiritable规定读和写的游戏规则 (void write(DataOutput out) throws IOException;  void readFields(DataInput in) throws IOException;)。为了适应hadoop的mapreduce的运算特性,也就是map
和reduce对key的比較,排序的功能,就要实现Comparable接口,这个接口规定 public int compareTo(T o);这种方法。为了增强处理大数据集的能力。我们不能老是先序列化,传输,反序列化。然后进行比較compare,太消耗时间和性能了。我们有了增强的RawComparator,RawComparator是Comparator的增强版,能够比較没有被反序列化的数据。

hadoop须要处理的数据五花八门,java具有的基本数据类型都有可能在hadoop中出现,hadoop因此包装了java的基本数据类型使他们实现以上的接口而且给予实现细节。这些类都实现了WritableComparable接口。插上飞翔的翅膀,能够在不同的hadoop节点之间毫无障碍的传输了。如入无人之境。

既然Text拿出来单独讨论。

自然就要好好研究一下Text的实现细节,对于我们对hadoop的设计细节和思想太重要太重要。

Text是UTF-8字符串的Writable实现。被看做是java String类型的替换。Text 类取代了UTF8 类, UTF8 类不支持编码大于32767 个字节的字符.使用了Java 改进过的UTF-8.Text 使用int 型(使用一个可变长度的编码方案)在字符感编码中存储字节数.
最大
值是2 GB 。此外。 Text 使用标准的UTF芯,使其更易于与理解U T F-8 的其它工具协同工作.

为什么是2GB,我预计非常少人会思考这个问题,我们简单计算一下:

利用int存储字节长度,int最大是2^31-1,那么字节最大长度就是2^31-1

Text可以容纳的大小R=(2^31-1)/1024/1024/1024=1.99999999=2GB

因此我们使用他的时候要知道他的大小是有限制的。

因为强调使用标准的UTF8,所以Text 和Java 的String 类之间还是有一些差别的。Text 类的索引位于编码后的字节系列中,而不是字符串中的Unicode 字符.或Java 的char 编码单元{如同String 一样)。举比例如以下:

这方面的差异用中文就非常好的说明这个问题。

String line = "滚滚长江东逝水";

System.out.println(line.length());

Text text = new Text(line);

System.out.println(text.getLength());

System.out.println(line.charAt(2));

System.out.println(text.charAt(2));

输出:

7

21

-1

String line = "merry christmas";

System.out.println(line.length());

Text text = new Text(line);

System.out.println(text.getLength());

System.out.println(line.charAt(2));

System.out.println(text.charAt(2));

输出:

15

15

r

114

能够看出来,他们的索引(Index)是真的不一样。同一个索引值取出来的并非同一个东西。

注意, charAt ( )返回了一个int 类型来表示Unicode 代码点, 而不是像String 变量那样返回一个char 类型。

在開始使用一个以上字节进行编码的字符(比如中文。!

), Text 和String 之间的差别是非常明显的。

下表展示了Unicode的代码点。

U+0041 代码点相应大写字母A 一直到U+00DFUTF-8都是一个字节编码。剩下的都是两个字节以上。而对于java,最后一行,仅仅有最后一个代码点是两个。其它的都是一个字节的。这点区别非常大。

怕非常多人不懂代码点,我再解释一下:

Unicode 是通用字符编码标准。用于表示文本以供计算机处理。Unicode 提供了一种对多语种文本进行一致编码的方法,便于国际文本文件的交换。每一个 Unicode 字符均映射到一个代码点,代码点是一个介于
0 和 1,114,111 之间的整数。Unicode 代码点使用 U+nnnn 形式的表示法来表示(当中 nnnn 是代码点的十六进制数),或使用描写叙述代码点的文本字符串来表示。比如,小写字母 “a” 能够用 U+0061 或文本字符串 "LATIN SMALL LETTER A" 来表示。 代码点能够使用不同的字符编码方案进行编码。

在 Oracle Solaris Unicode 语言环境中,使用的是
UTF-8 形式。UTF-8 是 Unicode 的一种可变长度编码形式,它透明地保留了 ASCII 字符代码值(请參见UTF-8 概述)。 代码点就是一个字符在Unicode中相应的编码。

String 的长度是它包含的字符个数 。但Text 对象的长度是其UTF -8 编码的字节数. 相同。 indexOf () 方泣返回一个char 类型的编码单元的索引。find () 方格是字节偏移量.请看样例:

@Test

public void string() throws UnsupportedEncodingException {

String s = "\u0041\u00DF\u6771\uD801\uDC00";

assertThat(s.length(), is(5));

assertThat(s.getBytes("UTF-8").length, is(10));

assertThat(s.indexOf("\u0041"), is(0));

assertThat(s.indexOf("\u00DF"), is(1));

assertThat(s.indexOf("\u6771"), is(2));

assertThat(s.indexOf("\uD801\uDC00"), is(3));

assertThat(s.charAt(0), is(‘\u0041‘));

assertThat(s.charAt(1), is(‘\u00DF‘));

assertThat(s.charAt(2), is(‘\u6771‘));

assertThat(s.charAt(3), is(‘\uD801‘));

assertThat(s.charAt(4), is(‘\uDC00‘));

assertThat(s.codePointAt(0), is(0x0041));

assertThat(s.codePointAt(1), is(0x00DF));

assertThat(s.codePointAt(2), is(0x6771));

assertThat(s.codePointAt(3), is(0x10400));

}

@Test

public void text() {

Text t = new Text("\u0041\u00DF\u6771\uD801\uDC00");

assertThat(t.getLength(), is(10));
  //10 = 1+2+3+4 是其UTF -8 编码的字节数

assertThat(t.find("\u0041"), is(0));

assertThat(t.find("\u00DF"), is(1));

assertThat(t.find("\u6771"), is(3));

assertThat(t.find("\uD801\uDC00"), is(6));

assertThat(t.charAt(0), is(0x0041));

assertThat(t.charAt(1), is(0x00DF));

assertThat(t.charAt(3), is(0x6771));

assertThat(t.charAt(6), is(0x10400));

}

遍历Text,迭代

迭代使用索引的字节偏移对Text 中的Unicode 字符进行途代是非常复杂的,由于你不能仅仅添加索引。迭代的定义有点模糊(见例4-6 ) 将Text 对象变成java.nio.ByteBuffer然后对缓冲的Text 重复调用bytesToCodePoint() 静态方法.这个方泣提取下一个代码点作为int 然后更新缓冲中的位置。当bytesToCodePoint() 返回- 1 时,检測到字符结束。意思就是说,我们取字符的时候。是一整个一整个字符的取,我们不可以依照索引来取,我们依照代码点整个整个的取。

public class TextIterator {

public static void main(String[] args) {

Text t = new Text("\u0041\u00DF\u6771\uD801\uDC00");

ByteBuffer buf = ByteBuffer.wrap(t.getBytes(), 0, t.getLength());

int cp;

while (buf.hasRemaining() && (cp = Text.bytesToCodePoint(buf)) != -1) {

System.out.println(Integer.toHexString(cp));

}

}

}

输出:

41

df

6771

10400

可改动性

String 和Text 的还有一个差别在于可改动性(像Hadoop 中的全部Writable 实视一样。但NullWritable 除外,后者是单实例对象)。

我们能够通过对它调用set() 函数来重用Text 实例。

示比例如以下:

Text t = new Text("hadoop");

t.set("pig");

assertThat(t.getLength(), is(3));

assertThat(t.getBytes().length, is(3));

转为字符串

Text 不像java. l ang.String 一样有一个能够处理字符串的API ,所以在很多情况下,须要将Text 对象转化为String 对象。这通经常使用toString()方法来完毕。

assertThat(new Text("hadoop ") . toString() , is( "hadoop"));

BytesWritable

BytesWritable 是一个二进制数据数组封装。

它的序列化格式是一个int 字段(4字节) ,指定的是字节数及字节本身。

比如。 一个长度为2 ,值为3 和5 的字节数组序列化为一个4 字节的整数(00000002)加上两个来自数组的字节(03 和05) 。

BytesWritable b = new BytesWritable(new byte[] { 3, 5 });

byte[] bytes = serialize(b);

assertThat(StringUtils.byteToHexString(bytes), is("000000020305"));

BytesWritab1e 是可变的。其值可通过调用set ( )方撞来改变。和Text一样 。从getBytes ( )方法返回的字节数组大小可能并没有反映出存储在BytesWritable 的数据的实际大小.能够通过调用getLength () 方法来确定BytesWritable 的长度。比如:

b.setCapacity(11);

assertThat(b.getLength(), is(2));

assertThat(b.getBytes().length, is(11));

NullWritable

NullWritable 是一种特殊的Writable 类型,由于它的序列化是零长度的。

没有字节被写入流或从流中读出.它被用作占位符.比如,在MapReduce 中,在不需要这个位置的时候,键或值能够被声明为NullWritable,他有效存储了一个不变的空值。NullWritable 也能够非常实用,在打算存储一系列值的时候,作为SequenceFile 的一个键,而不是键/值对。

它是一个不变的单实例,事实上例能够通过调用NullWritable.get() 方法来检索。

今天就到这里。

Charles 2015-12-24晚于P.P


版权说明:

本文由Charles Dong原创,本人支持开源以及免费故意的传播。反对商业化谋利。

CSDN博客:http://blog.csdn.net/mrcharles

个人站:http://blog.xingbod.cn

EMAIL:[email protected]

时间: 2024-10-22 23:45:10

Hadoop Serialization -- hadoop序列化具体解释 (2)【Text,BytesWritable,NullWritable】的相关文章

Python菜鸟的Hadoop实战——Hadoop集群搭建

Hadoop集群的部署 网上很多关于hadoop集群部署的文章, 我这里重新整理下,毕竟,别人的经历,让你按照着完整走下来,总有或多或少的问题. 小技巧分享: 有些初学者喜欢在自己机器上安装虚拟机来部署hadoop,毕竟,很多同学的学习环境都是比较受限的. 我这里则直接选择了阿里云的机器,买了三台ECS作为学习环境.毕竟,最低配一个月才40多块,学习还是要稍微投入点的. 一. 基础环境准备 Windows不熟练,小主只有选择Linux了. 官方提示如下,Linux所需软件包括: JavaTM1.

Hadoop单机Hadoop测试环境搭建

Hadoop单机Hadoop测试环境搭建: 1. 安装jdk,并配置环境变量,配置ssh免密码登录 2. 下载安装包hadoop-2.7.3.tar.gz 3. 配置/etc/hosts 127.0.0.1 YARN001 4. 解压缩hadoop-2.7.3.tar.gtz到/home/zhangzhenghai/cluster目录下 5. 配置etc/hadoop/hadoop-env.sh 配置JAVA_HOME环境变量 export JAVA_HOME=/home/zhangzhengh

Apache Hadoop 和Hadoop生态圈

Apache Hadoop 和Hadoop生态圈 Hadoop是一个由Apache基金会所开发的分布式系统基础架构. 用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力进行高速运算和存储. Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS.HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上:而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数

【Hadoop】Hadoop MR异常处理

1.代码示例 package com.ares.hadoop.mr.flowsort; import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configured; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.LongWritable; import org.apac

【Hadoop】Hadoop MR 自定义排序

1.概念 2.代码示例 FlowSort package com.ares.hadoop.mr.flowsort; import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configured; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.LongWritable; i

Hadoop:hadoop fs、hadoop dfs与hdfs dfs命令的区别

http://blog.csdn.net/pipisorry/article/details/51340838 'Hadoop DFS'和'Hadoop FS'的区别 While exploring HDFS, I came across these two syntaxes for querying HDFS: > hadoop dfs > hadoop fs why we have two different syntaxes for a common purpose 为什么会对同一个功能

一步一步跟我学习hadoop(3)----hadoop命令手册

上节我们学习了hadoop的eclipse插件安装和wordcount程序的运行,本篇对hadoop命令进行一个系统的了解 hadoop的命令通过HADOOP_HOME\bin\hadoop命令触发,我们可以在命令行执行 hadoop --help 来提示用户的命令输入. hadoop命令分为两大类:用户命令和管理命令,以下是hadoop命令的详细解读 本篇博客是摘自官网http://hadoop.apache.org/docs/r1.0.4/cn/commands_manual.html 0概

boost::serialization(2)序列化基类

在派生类中序列化一个基类 假如有一个基类如下: class student_info { public: student_info() {} virtual ~student_info() {} student_info(const std::string& sn, const std::string& snm, const std::string& sg) : name_(sn), number_(snm), grade_(sg) { } virtual void print_i

怎样用boost::serialization去序列化派生模板类

本篇是boost::serialization 用基类指针转存派生类(错误多多,一波三折)的姊妹篇,这里只不过做一个总结. 先来看一个基类 class base_class { public: base_class(int m=0) : base_member_(0) {} virtual ~base_class() {} virtual void print_data() = 0; protected: int base_member_; //other member... }; 它的一个模板