hadoop高级编程.pdf
Boris Lublinsky [美] Kevin T. Smith 著 Aly Yakubovich 穆玉伟 靳晓辉 译 Join the discussion Wrox Programmer to ProgrammerTM A Wiley Brand 如果你已经准备好要充分实施大规模可扩展性数据分析 工作,那么需要知道如何利用Hadoop技术。这本Hadoop 高级编程构建与实现大数据解决方案可以帮助你做到 这一点本书关注用于构建先进的、基于Hadoop的企业级应 用的架构和方案,并为实现现实的解决方案提供深入的、代 码级的讲解。本书还会带你领略数据设计以及数据设计如何 影响实现。本书解释了MapReduce的工作原理,并展示了如 何在MapReduce中重新定制特定的业务问题。在整本书中, 你将会发现深入的Java代码示例,这些代码示例可以直接使 用,它们均源自于已经成功地构建和部署的应用程序。 主要内容 ◆ 探索MapReduce架构、它的主要组件和MapReduce编程 模型 ◆ 讨论如何创建可靠的MapReduce应用,包括测试和调试, 以及如何使用内置的MapReduce设施 ◆ 解释如何扩展Oozie以及如何用它来集成基于Hadoop的实现 和其他企业级应用 ◆ 描述如何构建基于Hadoop的实时应用以及如何使用实时 Hadoop查询 ◆ 展示如何扩展Hadoop的安全能力,包括加密、认证、授 权、单点登录SSO和审计 ◆ 涵盖在亚马逊Web服务AWS云上运行Hadoop的不同方案 作者简介 Boris Lublinsky是诺基亚的首席架构师,出版了70多篇 作品,包括Applied SOA Service-Oriented Architecture and Design Strategies。 Kevin T. Smith是Novetta Solutions公司AMS部门的技术 解决方案总监,他为客户构建高度安全的、面向数据的解 决方案。 Aly Yakubovich是Hortonworks的一名系统架构师, 而且是对象管理组织OMG关于SOA治理和模型驱动架构 的特别兴趣小组SIG的一名成员。 源代码下载及技术支持 在企业中实现Hadoop解决方案 定价59.80元 Programmer Forums Join our Programmer to Programmer forums to ask and answer programming questions about this book, join discussions on the hottest topics in the industry, and connect with fellow programmers from around the world. Code Downloads Take advantage of free code samples from this book, as well as code samples from hundreds of other books, all ready to use. Read More Find articles, ebooks, sample chapters and tables of contents for hundreds of books, and more reference resources on programming topics that matter to you. HadoopHadoop 高级编程 高级编程 构建与实现大数据解决方案 HadoopHadoop高级编程高级编程 构建与实现大数据解决方案 Professional Hadoop Solutions Hadoop 高级编程 构建与实现大数据解决方案 [美] Boris Lublinsky Kevin T. Smith Aly Yakubovich 著 穆玉伟 靳晓辉 译 北 京 Boris Lublinsky, Kevin T. Smith, Aly Yakubovich Professional Hadoop Solutions EISBN978- 1- 118- 61193- 7 Copyright 2013 by John Wiley FileSystem fs FileSystem.getconf; 另外一个重要的 HDFS 对象是 Path,它代表文件系统中文件或目录的名字。可以使用 一个代表 HDFS 上文件/目录位置的字符串来创建 Path 对象。将 FileSystem 和 Path 对象进 行组合,可以实现对 HDFS 文件和目录的多种编程操作。代码清单 2- 2 展示了一个例子。 代码清单 2- 2操作 HDFS 对象 Path filePath new Pathfile name; iffs.existsfilePath //do something iffs.isFilefilePath //do something Boolean result fs.createNewFilefilePath; Boolean result fs.deletefilePath; FSDataStream in fs.openfilePath; FSDataOutputStream out fs.createfilePath; 代码清单2- 2中的最后两行展示了如何基于文件路径创建FSDataStream和 FSDataOutputStream对象。 这两个对象是Java I/O包中DataStream和DataOutputStream的 子类,这意味着它们支持标准的I/O操作。 第 2 章 Hadoop 数据存储 21 注意 除 DataStream 之外,FSDataStream 还实现了 Seekable 和 PositionedReadable 接口,从而实现了用于查找和从给定位置读取的方法。 至此,应用程序能够以和读/写本地数据系统相同的方式从 HDFS 读取数据,或者将数 据写入到 HDFS。 写租约 当以写入方式打开文件时,打开文件的客户端会被授予一个独占的写租约,用于保护 该文件。这意味着在该客户端完成操作之前,没有其他客户端能够写入该文件。为确保没 有“不辞而别的”客户端占用租约,租约会周期性地过期。租约的使用有效地确保了两个 应用程序不会同时写入一个给定文件与数据库中的写锁相似。 租约的生命周期由软限制和硬限制来限定。在软限制期间,写入者可以独占地访问文 件。如果软限制过期且客户端没能关闭文件或者更新租约通过向 NameNode 发送心跳, 那么其他客户端可以抢占该租约。如果硬限制一小时过期且客户端没能更新租约,那么 HDFS 认为客户端已经退出,并自动替写入者关闭文件,然后恢复租约。 写入者的租约并不会阻止其他客户端读取该文件。一个文件可能有多个并发的读取者。 2.1.3 Hadoop 特定的文件类型 除 “普通” 文件之外, HDFS 还引入了一些特定的文件类型例如 SequenceFile、 MapFile、 SetFile、ArrayFile 和 BloomMapFile,它们提供更加丰富的功能,且通常会简化数据处理。 SequenceFile 提供了用于二进制键/值对的持久化数据结构。这里,键和值的不同实例 必须代表相同的 Java 类,但大小可以不同。类似于其他 Hadoop 文件,SequenceFile 只能 追加。 当使用普通文本或二进制文件保存键/值对MapReduce使用的典型数据结构时, 数据 存储并不知道键和值的布局,它必须在通用存储之上的读取器中实现。 SequenceFile的使用 提供了一种存储机制,原生支持键/值结构,从而使用了这种数据布局的实现更加简单。 SequenceFile有三种可用格式无压缩、记录压缩和块压缩。前两种以基于记录的格式 保存如图 2- 2 所示,而第三种使用基于块的格式如图 2- 3 所示。 记录 记录 记录 记录 同步 同步 头部 图 2- 2 基于记录的 SequenceFile 格式 块 块 块 同步 同步 同步 头部 图 2- 3 基于块的 SequenceFile 格式 Hadoop 高级编程 构建与实现大数据解决方案 22 对于序列文件来说,特定格式的选取决定了该文件在硬盘驱动器上的大小。块压缩的 文件通常最小,而无压缩的最大。 在图 2- 2 和图 2- 3 中,头部header包含了有关 SequenceFile 的一般信息,如表 2- 1 所示。 表 2- 1 SequenceFile 头部header 字 段 描 述 Version 一个 4 字节的数组,包含三个字符SEQ和一个序列文件版本号4 或 6。当前 使用的是版本 6。为向后兼容而支持版本 4 Key Class 键的类名,使用读取器提供的键的类名对其进行验证 Value Class 值的类名,使用读取器提供的值的类名对其进行验证 Compression 键/值压缩标志 Block Compression 块压缩标志 Compression Codec CompressionCodec 类。该类仅在键/值或块压缩标志为 true 时使用。否则,该值 被忽略 Metadata 元数据可选是一个键/值对列表,可以用于向文件添加用户特定的信息 Sync sync 标识符 注意 同步sync是个专门的标记, 用于在 SequenceFile 内部更快速地进行查找。 sync 标记在 MapReduce 实现中还有一个特殊作用数据分割只能在 sync 边界处进行。 如表 2- 2 所示,记录中包含了键和值的实际数据,以及它们的长度。 表 2- 2 记录布局 字 段 描 述 Record Length 记录的长度字节 Key Length 键的长度字节 Key 字节数组,包含记录的键 Value 字节数组,包含记录的值 在基于块的情况下, header 和 sync 服务于相同的目的与基于记录的 SequenceFile 格式 的情况相同。实际的数据包含在块中,如表 2- 3 所示。 第 2 章 Hadoop 数据存储 23 表 2- 3 块布局 字 段 描 述 Keys Lengths Length 在这种情况下,某个给定块中的所有键都保存在一起。该字段指定压缩后的 键- 长度大小以字节为单位 Keys Lengths 字节数组,包含压缩的键- 长度块 Keys Length 压缩后的键大小以字节为单位 Keys 字节数组,包含块中压缩后的键 Values Lengths Length 在这种情况下,某个给定块中的所有值都保存在一起。该字段指定压缩后的 值- 长度大小以字节为单位 Values Lengths 字节数组,包含压缩的值- 长度块 Values Length 压缩后的值大小以字节为单位 Values 字节数组,包含块中压缩后的值 所有格式均使用相同的 header,其中包含着可以由读取器识别的信息。header参见 表 2- 1包含了键和值的类名被读取器用来实例化这些类、版本号以及压缩信息。如果启 用了压缩,则 header 中会增加 Compression Codec class name 字段。 SequenceFile 的元数据是一系列键/值文本对, 可以包含关于 SequenceFile 的额外信息, 文件读取器/写入器会使用这些信息。 无压缩格式和记录压缩格式的写操作的实现非常类似。每一次对 append方法的调用 都会向 SequenceFile 添加一条记录,其中包含整条记录的长度键的长度加值的长度、键 的长度以及键和值的原始数据。压缩和无压缩版本之间的不同在于是否使用特定的编解码 器对原始数据进行了压缩。 块压缩格式可以达到更高的压缩率。直到达到某个阈值块大小后,数据才会被写入, 此时所有的键将被一起压缩。值以及键和值长度的列表也会被压缩。 Hadoop 提 供 了 用 于 SequenceFiles 的 特 殊 读 取 器 SequenceFile.Reader 和 写 入 器 SequenceFile.Writer。代码清单2- 3展示了使用SequenceFile.Writer的一小段代码。 代码清单 2- 3使用 SequenceFile.Writer Configuration conf new Configuration; FileSystem fs FileSystem.getconf; Path path new Path“fileName“; SequenceFile.Writer sequenceWriter new SequenceFile.Writerfs, conf, path, Key.class,value.class,fs.getConf.getInt“io.file.buffer.size“, 4096,fs.getDefaultReplication, 1073741824, null,new Metadata; .. sequenceWriter.appendbytesWritable, bytesWritable; ....... IOUtils.closeStreamsequenceWriter; Hadoop 高级编程 构建与实现大数据解决方案 24 一 个 最 简 化 的 SequenceFile 写 入 器 构 造 函 数 SequenceFile.WriterFileSystem fs, Configuration conf, Path name, Class keyClass, Class valClass需要文件系统的类型、Hadoop 配置、路径文件位置以及键和值的类定义。前面示例中使用的构造函数支持指定额外的 文件参数,包括以下几个 int bufferSize如果不定义,则使用默认的缓冲区大小4096。 short replication使用默认复制。 long blockSize使用值 10737418241024MB。 Progressable progress使用 None。 SequenceFile.Metadata metadata使用一个空的元数据类。 写入器一旦创建完成,就可以用来向文件添加键/记录对了。 SequenceFile 的局限之一是无法基于键值进行查找。其他 Hadoop 文件类型MapFile、 SetFile、ArrayFile 和 BloomMapFile通过在 SequenceFile 之上增加基于键的索引克服了这 个限制。 如图 2- 4 所示,MapFile 实际上并非文件,而是目录,其中包含两个文件一个数据 序列文件,包含 map 中所有的键和值;一个较小的索引文件,包含一部分键。可以通 过按顺序添加条目来创建 MapFile。MapFile 通常利用其索引来高效地搜索和检索文件的 内容。 索引文件 头部 键偏移 键 值 键 值 键 值 键 值 键 值 序列文件 键偏移 图 2- 4 MapFile 索引文件中包含键和一个LongWritable对象,LongWritable对象保存了该键对应记录的 起始字节位置。索引文件并不包含所有键,而是只包含其中一部分。我们可以使用写入器 的setIndexInterval方法设置indexInterval。索引会被完全读入内存,因此对于很大的map, 有必要设置索引跳跃值,使得索引文件足够小,以致能够完全加载到内存中。 类似于 SequenceFile, Hadoop 提供了用于 map 文件的特殊读取器MapFile.Reader和写 入器MapFile.Writer。 SetFile和ArrayFile是用于实现特定键/值类型的MapFile变体。SetFile是一种MapFile, 用于代表没有值的键集合值由NullWritable实例来代表。ArrayFile处理键/值对,其键为连 续的long长整型值。它维护一个内部计数器,并在每次进行追加调用时增加。该计数器的 值会被用作键。 第 2 章 Hadoop 数据存储 25 这两种文件类型对于保存键而非值很有用。 布隆过滤器 布隆过滤器是一种空间利用率高的、概率性的数据结构,用于测试一个元素是否是某 个集合的成员。测试的结果是该元素确定不在集合中,或者可能在集合中。 布隆过滤器的基础数据结构是比特向量。误报的可能性取决于元素集合的大小和比特 向量的大小。 尽管会有误报, 但布隆过滤器在表示集合时相比于其他数据结构例如自平衡二叉搜索 树、单词查找树、哈希表或者简单的数组或链表有着很强的空间优势。大多数数据结构 至少要保存数据条目本身, 这需要的存储空间可能是从少量比特位对于小整数到任意数 量的比特位,例如对于字符串单词查找树是个特例,因为前缀相同的元素之间可以共享 存储。 布隆过滤器的这种优势一部分源于其紧凑性继承自数组,还有一部分源于其概率性 本质。 最后,BloomMapFile 通过添加动态的布隆过滤器参见补充内容“布隆过滤器”扩展 了 MapFile 实现,为键提供快速的成员资格测试。 它还提供了键搜索操作的一个快速版本, 尤其适用于稀疏的 MapFile。写入器的 append操作会更新 DynamicBloomFilter,当写入器 关闭时,DynamicBloomFilter 会被序列化。当创建读取器时,该过滤器会被加载到内存中。 读取器的 get操作首先利用过滤器检查键的成员资格,如果键不存在的话,它立即返回空 值,不再进行任何进一步的 I/O 操作。 数据压缩 在 HDFS 文件中存储数据时,一个需要考虑的重要因素就是数据压缩将数据处理 中的计算负载从 I/O 转化为 CPU。一些出版物提供了对在 MapReduce 实现中使用压缩时, 计算与 I/O 之间相互权衡的系统评估,其结果显示数据压缩的益处取决于数据处理作业的 类型。对于大量读操作I/O 是瓶颈应用例如,文本数据处理,压缩会节省 35~60的 性能开销。另一方面,对于计算密集型应用CPU 是瓶颈,数据压缩带来的性能提升微不 足道。 但这并不意味着数据压缩对此类应用没有益处。 Hadoop 集群的资源均是共享的, 其结 果是,一个应用 I/O 负载的降低将会提升使用该 I/O 的其他应用的性能。 这意味着总是需要数据压缩吗答案是“否” 。例如,如果正在使用文本文件或自定 义二进制输入文件,那么可能就不需要数据压缩,因为压缩后的文件不能被分割在第 3 章 中会学到更多。另一方面,对于 SequenceFile 及其衍生的文件类型,压缩总是需要的。最 后,压缩用于 shuffle 和 sort 的中间文件总是有意义的在第 3 章中会学到更多。 记住数据压缩的结果在很大程度上取决于待压缩数据的类型和压缩算法。 Hadoop 高级编程 构建与实现大数据解决方案 26 2.1.4 HDFS 联盟和高可用性 当前 HDFS 实现的主要局限在于单个 NameNode。由于所有文件元数据都保存在内存 中, 因此NameNode的内存量决定了Hadoop集群上可用文件的数量。 为克服单个NameNode 内存的限制并能够水平地扩展名称服务, Hadoop 0.23引入了HDFS联盟HDFS Federation, 它基于多个独立的 NameNode/名称空间。 以下是 HDFS 联盟的主要优势 名称空间可扩展性HDFS 集群存储可以水平扩展,但名称空间不能。通过向集 群添加更多的 NameNode 来扩展名称空间,大规模部署或者使用大量小文件的部 署会因此受益。 性能文件系统操作的吞吐量受到单个 NameNode 的限制。向集群添加更多的 NameNode 可以扩展文件系统读/写操作的吞吐量。 隔离在多用户环境下,单个 NameNode 无法支持隔离。实验性应用可能会让 NameNode 过载,并拖慢关键的生产应用。使用多个 NameNode,可以将不同类别 的应用和用户隔离到不同的名称空间。 如图 2- 5 所示,HDFS 联盟的实现基于多个独立 NameNode 的汇集,它们之间不需要 进行协调。所有 NameNode 均将 DataNode 作为公共存储,用于保存块。每个 DataNode 都 会向集群中的所有 NameNode 注册。DataNode 周期性地发送心跳和块报告,并处理来自 NameNode 的命令。 池 1 池 k 池 n 块池 公共存储 图 2- 5 HDFS 联盟 NameNode 架构 名称空间在块的集合块池上操作。尽管块池只能用于特定的名称空间,但实 际的数据可以被分配到集群中的任意 DataNode 上。每个块池都是独立管理的,这允许名 称空间在不必与其他名称空间协调的情况下为新块生成块 ID。 某个 NameNode 的失效不会 第 2 章 Hadoop 数据存储 27 影响 DataNode 服务集群中的其他 NameNode。 名称空间和其块池一起被称为名称空间卷。这是一个自包含的管理单元。当删除 NameNode/名称空间时,DataNode 上相对应的块池也会被删除。在集群升级时,每个名称 空间卷会被作为一个单元来升级。 HDFS 联盟的配置是向后兼容的,且允许已有的单一 NameNode 配置能够在不进行任 何改动的情况下正常工作。新配置的设计使得集群中所有节点的配置相同,不必根据其中 节点的类型来部署不同的配置。 尽管 HDFS 联盟解决了 HDFS 扩展性的问题, 但它并不解决 NameNode 可靠性问题事 实上,它使事情变得更糟这种情况下单个 NameNode 失效的概率更高。图 2- 6 展示了 一种新的 HDFS 高可用性架构,包含两台配置为 NameNode 的独立机器,且在任意时间点 只有其中一台处于活动状态。活动的 NameNode 负责响应集群中的所有客户端操作,而另 外一台备机仅作为从属,维护着足够的状态信息,并在需要时提供快速的故障转移。为 保持两个节点的状态同步,该实现要求两个节点均可以访问共享存储设备上的某个目录。 共享存储 NameNode NameNode DataNode DataNode DataNode 图 2- 6 HDFS 故障转移架构 当活动节点进行任何名称空间修改时,它会向一个位于共享目录下的日志文件持久性 地写入一条修改记录。备用节点持续观察该目录的变化,并且将改动应用于自身的名称空 间。当发生故障转移时,备机在确保已经读取了所有改动之后,将自身切换为活动状态。 为支持快速的故障转移,备用节点也需要了解集群中块位置的最新信息。这可以通过 配置 DataNode 来实现,让其同时向两台 NameNode 发送块位置信息和心跳。 目前, 仅支持手工故障转移。 Hortonworks公司提交到1.1版主干和分支中的核心Hadoop 的补丁消除了该局限。该解决方案基于Hortonworks故障转移控制器,它会自动选择一个活 动的NameNode。 Hadoop 高级编程 构建与实现大数据解决方案 28 HDFS为存储大量数据提供了非常强大和灵活的支持。一些特殊文件类型类似于 SequenceFile非常适合支持MapReduce实现。 MapFile及其衍生类型Set、 Array和BloomMap 在快速数据访问方面表现良好。 不过,HDFS只支持一组有限的访问模式写、删除和追加。尽管从技术上讲,可以 将更新实现为覆盖,但这种粒度的实现覆盖仅能工作在文件级别上在大多数情况下都成 本过高。此外,HDFS的设计专门针对支持大量顺序读取,这意味着随机访问数据会造成 显著的性能开销。而且最后,HDFS并不适用于较小的文件。尽管从技术上讲,HDFS支持 这些文件,但它们的存在会造成NameNode内存的显著开销,因而降低了Hadoop集群内存 容量的上限。 为了克服诸多局限,Hadoop 以 HBase 的形式引入了一个更加灵活的数据存储和访问 模型。 2.2 HBase HBase 是一个分布式的、版本化的、面向列的、多维度的存储系统,在设计上具备高 性能和高可用性。为能够成功地使用 HBase,首先必须了解其实现方法和工作原理。 2.2.1 HBase 架构 HBase是Google BigTable架构的开源实现。类似于传统的关系型数据库管理系统 RDBMS,HBase中的数据以表的形式组织。然而与RDBMS不同的是,HBase支持非常松 散的结构定义,且不提供任何连接、查询语言或SQL。 注意 尽管 HBase 不支持实时连接和查询,但可以通过 MapReduce 很轻松地实现批量连接 和/或查询。事实上,更高层的系统例如 Pig 和 Hive可以很好地支持这些功能,它们使用 一种受限的 SQL 方言来执行这些操作。我们会在本章的后面学到关于 Pig 和 Hive 的更多 知识。 HBase 的主要关注点是大稀疏表上的创建、读取、更新和删除CRUD操作。目前, HBase 不支持事务但提供有限的锁支持和一些原子化操作和二级索引一些社区项目正试 图实现该功能,但它不是 HBase 实现的核心组成部分。其结果是,大多数基于 HBase 的 实现正在使用高度非规范化的数据。 类似于 HDFS,HBase 实现了主/从HMaster/域服务器架构,如图 2- 7 所示。 HBase 利用 HDFS 作为其持久化数据存储。这允许 HBase 利用 HDFS 提供的所有高级 特性,包括校验、复制和故障转移。HBase 数据管理由分布式的域服务器实现,域服务器 由 HBase 主控服务器HMaster进行管理。 第 2 章 Hadoop 数据存储 29 获取 数据位置 客户端 获取主节点 获取数据 观察 观察 memstore memstore 域服务器 域服务器 图 2- 7 HBase 高层架构 域服务器的实现包含以下主要组件 memstore 是 HBase 的内存数据缓存实现,它通过直接从内存提供尽可能多的数据 来提升 HBase 的整体性能。 memstore 保存以键/值格式存储的数据在内存中的修改。 预写日志WAL记录了对数据的所有改动。这在主存储发生意外时很重要。如果服 务器崩溃,那么可以高效地重播该日志,将服务器恢复到崩溃之前的状态。这也意 味着如果向 WAL 写入记录失败,那么整个操作都会被认为是失败的。 注意 HBase 的优化技巧之一就是禁止写入 WAL。这体现了性能和可靠性之间的折衷。当域 服务器在写操作完成之前发生失效时,禁止写入 WAL 将导致无法进行恢复。应该谨慎地 使用这种优化方案,除非数据丢失可接受,或者写操作可以基于其他数据源进行“重播” 的情况。 HFile 是 HBase 专用的 HDFS 文件格式。域服务器中的 HFile 实现负责从 HDFS 读 取 HFile,以及将 HFile 写入到 HDFS。 Zookeeper Zookeeper是一个多副本的同步服务,具备最终一致性。它具有鲁棒性,因为持久化的 数据分布在多个节点上该节点集合称为ensemble,并且连接到其中任意一个节点即一台 特定的 “服务器” 的客户端可以在该服务器失效时进行迁移。 只要绝大多数节点正在工作, Zookeeper节点组成的ensemble就是存活的。 Zookeeper 的主节点由 ensemble 中的所有节点通过协商来动态选取。 如果主节点失效, Hadoop 高级编程 构建与实现大数据解决方案 30 余下的节点就会选择一个新的主节点。主节点可以授权写操作。这保证了写操作按照顺序 进行即写操作是线性的。每次客户端向 ensemble 进行写操作时,大多数节点会保存该信 息。这意味着每次写操作都会使服务器与主节点同步。 Zookeeper应用的一个典型示例就是分布式内存计算, 其中某些数据会在客户端节点之 间共享,而且出于同步的考虑,必须以一种非常小心的方式来访问/更新数据。Zookeeper 提供了用于构建自定义同步原语的库,以及运行分布式服务器的能力,而这避免了在使用 中心化的消息存储库时会遇到的单点故障问题。 分布式的 HBase 实例依赖于处在运行状态的 Zookeeper 集群关于该服务的描述, 请参 见补充内容“Zookeeper”。所有参与的节点和客户端必须能够访问正在运行的 Zookeeper 实例。默认情况下,HBase 管理一个 Zookeeper“集群”HBase 将 Zookeeper 进程的启 动和停止作为启动/停止自身进程的一部分。 由于 HBase 主控节点可能会被重新分配, 因此 客户端启动时会向 Zookeeper 查询 HBase 主控节点和- Root- 表的当前位置。 如图 2- 8 所示,HBase 使用一种自动分片和分发方案来应对大量数据与 HDFS 基于块 的设计和快速数据访问相比。 逻辑表 已分区的表 域服务器 域服务器 域服务器 域服务器 客户端 图 2- 8 表分片和分发 为保存任意长度的表,HBase 将表划分成域,每个域包含已排序的根据主键、范围连 续的行。这里,术语“连续的”并不意味着一个域中会包含给定间隔内的所有键。而是意 味着保证会将某个间隔内的所有键划分到相同的域,而在键空间中可能有任意数量的空洞。 域的分裂方式不取决于键空间,而取决于数据的大小。某个特定表的数据分区大小可 以在表创建时配置。这些域会“随机地”散落在域服务器上单个域服务器可以保存某个特 定表的任意数目的域。它们可能也会被来回移动,以达到负载均衡和故障转移的目的。 当向表中插入新记录时, HBase确定该记录应该去到哪个域服务器基于键值并在其上 插入它。如果域的大小超出了预先定义的值,那么它会自动分裂。域分裂是一个相当昂贵 的操作。为了避免这样的操作,可以在表创建过程中进行预先分裂,或者在任意时间点手 第 2 章 Hadoop 数据存储 31 动进行本章后面会介绍更多相关内容。 当读取/更新一条记录或一组记录时,HBase 确定哪些域应该包含该数据,并将客户 端重定向到适当的域。从这个角度来说,域服务器实现了实际的读/更新操作。 如图 2- 9 所示,HBase 利用一个专门的表.META.将特定的键/表对解析到特定的域服 务器。此表包含一个可用的域服务器列表,以及一个用户表描述符的列表。每个描述符为 包含在特定域中的特定表指定了键的范围。 指向 指向 用户表描述符 包含 用户表描述符 域- 键范围 域- 键范围 域- 键范围 域- 键范围 图 2- 9 HBase 中的域服务器解析 另外一个专门的HBase表- ROOT- 包含一个.META.表描述符的列表, 用于发现.META. 表。- ROOT- 表的位置保存在 Zookeeper 中。 如图 2- 10 所示,HBase 表是一个稀疏的、分布式的、持久化的多维度有序映射表。第 一个映射级别是键/行值。 如前所述, 行键总是有序的, 这是表分片以及高效读取和扫描按 顺序读取键/值对的基础。 键 列族 2 列族 1 保存在 图 2- 10 行、列族和列 Hadoop 高级编程 构建与实现大数据解决方案 32 注意 需要知道的一个事实是 HBase 对字节数组进行操作。HBase 数据的所有组件键、 列族名和列名均被其当成未解析的字节数组。这意味着所有内部值的比较以及相应的 排序,均以字典顺序进行。记住这点非常重要,尤其是在行键设计时要避免出现意料之外 的情况。一个典型的例子是整数键的使用。如果没有将它们左补齐为相同的长度,那么作 为 HBase 排序的结果,举例来说,键 11 将出现在键 5 之前。 HBase所使用的第二个映射级别基于列族列族最初由用于快速分析查询的柱状数据 库引入。在这种情况下,数据不像传统RDBMS那样一行接一行地保存,而是以列族的形 式保存。HBase使用列族进行基于访问模式和大小的数据分割。 列族在 HBase 实现中扮演着特殊的角色。它们定义了保存和访问 HBase 数据的方式。 每个列族都保存在一个单独的 HFILE 中。在表设计的过程中,这是一个需要记住的重要考 虑因素。建议为每种数据访问类型创建一个列族即应该把通常一起读/写的数据放入相 同的列族。 在表创建的过程中会定义一系列列族尽管以后可以修改。不同的列族也可以使用不 同的压缩机制,这可能是一个重要因素,例如当为元数据和数据分别创建了单独的列族时 一种常见的设计模式。在这种情况下,元数据通常相对较小,且不需要压缩;而数据可 能会非常大,且压缩通常能够提升 HBase 吞吐量。 鉴于这样的存储组织,HBase 实现了合并读。对于一个给定的行,它读取所有的列族 文件,并在将结果发回给客户端之前将它们进行合并。其结果是,如果总是一起处理整行 的话,那么一个单独的列族通常提供最佳的性能。 最后一个映射级别基于列。HBase 将列作为键/值对的动态映射。这意味着列不在表创 建时定义,而是在写/更新操作过程中动态建立。其结果是,HBase 表中的每个行/列族可以 包含任意多的列。列中包含实际的值。 从技术上讲,HBase还支持一种映射级别每个列值的版本。HBase不区分写入和更 新操作更新其实就是新版本的写入。 默认情况下, HBase为给定列的值保存最近三个版 本同时自动删除更老的版本。版本的深度可以在建表时控制。版本的默认实现是数据插 入的时间戳,但可以很容易地使用定制化的版本值将其改写。 以下是 HBase 支持的 4 种主要数据操作 Get获取为特定行或多行返回列族/列/版本的值。可以将其进一步细化为应 用于特定的列族/列/版本。重要的是要意识到,如果 Get 被细化为针对单独的列族, 那么 HBase 就不必实现合并读了。 Put存入如果键不存在的话,向表添加新的一行或多行;如果键存