北京
这是一本大数据工程师和Hadoop工程师的必备书。
近年来,由于移动互联网的高速发展和智能移动设备的普及,数据累积的速率已超过以往任何时候,这个世界已经进入了大数据时代。如何高效地存储、处理这些海量、多种类、高速流动的数据已成为亟待解决的问题。
Hadoop最早来源于全球云计算技术的领导者谷歌在2003年至2006年间发表的三篇论文。得益于学术界和工业界的大力支持,Hadoop 目前已成为最为成熟的大数据处理技术。Hadoop利用了“分而治之”的朴素思想为大数据处理提供了一整套新的解决方案,如分布式文件系统HDFS、分布式计算框架 MapReduce、NoSQL 数据库 HBase、数据仓库工具 Hive 等。Hadoop打破了传统数据处理技术的瓶颈,如样本容量、样本种类,让大数据真正成为了生产力。Hadoop目前已广泛应用于各行各业,行业巨头也纷纷推出自己的基于Hadoop的解决方案。今天,Hadoop已经在电信业、能源业等有了一定的用户基础,传统数据分析架构也逐渐在向Hadoop进行过渡。
大数据和大数据处理技术在相互促进,大数据刺激了大数据处理技术的发展,而大数据处理技术又加速了大数据应用落地。大数据催生了一批新的产业,并产生了对Hadoop工程师的庞大迫切需求,而目前有关Hadoop的书籍和在线材料仍然太少,这更进一步加大了人才缺口。
本书章节安排合理,结构清晰,内容由浅入深,循序渐进。作者是我的学生,作为一个奋战在大数据第一线的工程师,经验非常丰富,能够更加理解并贴近开发者和读者的需求。全书涵盖了HDFS、MapReduce、Hive、Sqoop等内容,尤其宝贵的是包含了大量动手实例和一个完备的Hadoop项目实例。我相信本书对于希望学习Hadoop的读者来说,是一个不错的选择。
北京软件行业协会执行会长
北京航空航天大学软件学院教授、院长
2014年12月13日,北京
为什么要写这本书
2013年被称为“大数据元年”,标志着世界正式进入了大数据时代,而就在这一年,我加入了清华大学苏州汽车研究院大数据处理中心,从事 Hadoop 的开发、运维和数据挖掘等方面的工作。从出现之日起,Hadoop 就深刻地改变了人们处理数据的方式。作为一款开源软件, Hadoop 能让所有人享受到大数据红利,让所有人在大数据时代站在了同一起跑线上。Hadoop很好地诠释了什么是“大道至简,衍化至繁”,Hadoop来源于非常朴素的思想,但是却衍生出大量的组件,让初学者难以上手。
我在学习和工作的过程中,走过很多弯路也做过很多无用功,尽管这是学习新技术的必由之路,但却浪费了大量的时间。我将自己学习和工作的心得记录下来,为了帮助更多像我当年一样的Hadoop学习者,我决定写一本书,一本自己开始Hadoop职业生涯的时候也想读到的书。
本书特点
本书结构针对学习曲线进行了优化,本书由浅至深,从理论基础到项目实战。
本书最大的特点是面向实践。基础篇介绍了 Hadoop 及相关组件,包含了大量动手实例,而应用篇则包含了一个具有代表性的基于 Hadoop 的项目完整实例,该实例脱胎于生产环境的真实项目,在通过基础篇的学习后,读者将在应用篇得到巩固和升华,并对 Hadoop 有一个更加清晰和完整的认识,这也符合实践出真知的规律。
本书介绍了Hadoop主要组件,如HDFS、MapReduce、Hive、Sqoop等,还介绍了Hadoop生产环境下的调优和运维、机器学习算法等高级主题。
读者对象
全书内容由浅入深,既适合初学者入门,也适合有一定基础的技术人员进一步提高技术水平,本书特别适合循序渐进地学习。本书的读者对象包括:
1.准备学习Hadoop的开发人员;
2.准备学习Hadoop的数据分析师;
3.希望将Hadoop运用到实际项目中的开发人员和管理人员;
4.计算机相关专业的高年级本科生和研究生;
5.具有一定的Hadoop使用经验,并想进一步提高的使用者。
如何阅读本书
本书在章节的安排上旨在引导读者以最快的速度上手Hadoop,而省去其他不必要的学习过程。如果你是一个有经验的 Hadoop 工程师或者是项目经理,也可以直接进入应用篇,关注项目的设计和实现,如果不是,还是建议你循序渐进地阅读本书方能获得最好的学习效果。
本书一共分为基础篇、应用篇和结束篇3个部分,一共18章。
基础篇从第1章至第7章,其中第1章为绪论,第2章为环境准备,第3章至第6章主要介绍HDFS、MapReduce、Hive、Sqoop的原理和使用,在此之上,第7章介绍Hadoop的性能调优和运维。读者将从基础篇获得Hadoop工程师的理论基础。
应用篇从第8章至17章,主要内容为一个基于Hadoop的在线图书销售商业智能系统的设计和实现,包含了系统需求说明、总体设计和完整的实现。应用篇会运用基础篇的知识,巩固并升华基础篇的学习效果。此外,应用篇的项目架构可以进行一些改动并推而广之,有一定的参考价值。读者将从应用篇获得Hadoop工程师的项目经验。
结束篇为第18章,将对全书进行总结,并对技术发展做了展望。
勘误和支持
写书就像是跳水,高高跳起跃入水中,但在浮出水面之前,运动员却无法知道评委的给分,而我期待读者的评价。由于作者水平有限,编写时间仓促,书中难免会出现一些错误,恳请读者批评指正。读者可以将对本书的反馈和疑问发到ddna_1022@163.com,我将尽力为读者提供满意的回复。
致谢
感谢电子科技大学的赵勇教授、北京航空航天大学的孙伟教授和邵兵副教授,从您们身上我学到了严谨的学术精神和做人的道理。
感谢清华大学苏州汽车研究院大数据处理中心的林辉主任,您的锐意进取精神一直深留我心。
感谢周俊琨、肖宇、赵虎、李为、黄普、朱游强、熊荣、江彦平,没有你们的帮助和努力,本书不可能完成。
感谢我的父母和外婆这些年来在生活上对我无微不至的关怀和无时无刻的支持,你们辛苦了;感谢吴静宜和她的家人对我的支持;感谢范若云哥哥,是你改变了我。
感谢人民邮电出版社的杨海玲编辑在本书出版过程中给予我的指导和一如既往的信任,感谢庞燕博士为审阅本书付出的辛勤劳动。
感谢所有在我求学路上帮助过我的人。
范东来
2014年12月于成都
本书的第一部分相当于工具的使用手册,将会介绍Hadoop的核心组件:HDFS、MapReduce、Hive、Sqoop,并在此基础上,进一步学习Hadoop性能调优和运维。通过这部分的学习,读者将获得Hadoop工程师的理论基础。
本章作为绪论,目的是在学习Hadoop之前,让读者理清相关概念以及这些概念之间的联系。
Hadoop从问世之日起,就和云计算有着千丝万缕的联系。本节将在介绍Hadoop的同时,介绍Hadoop和云计算之间的关系,为后面的学习打下基础。
如果你是一名创业者或者是一名项目经理,那么最好准备一份“电梯演讲”。所谓电梯演讲,是对自己产品的简单介绍,通常都是1~2分钟(电梯从1层~30层的时间),以便如果你恰巧和投资人挤上同一部电梯的时候,能够说服他投资你的项目或者产品。
在做Hadoop的电梯演讲之前,先来恶补一下Hadoop的有关知识。来看看Hadoop的发布者Apache软件基金会(ASF)对Hadoop的定义:Hadoop软件库是一个框架,允许在集群中使用简单的编程模型对大规模数据集进行分布式计算。它被设计为可以从单一服务器扩展到数以千计的本地计算和存储的节点,并且 Hadoop 会在应用层面监测和处理错误,而不依靠硬件的高可用性,所以Hadoop能够在一个每个节点都有可能出错的集群之上提供一个高可用服务。
从上面的定义可以看出Hadoop的如下几个特点。
1.Hadoop是一个框架
很多初学者在学习Hadoop的时候,对Hadoop的本质并不十分了解,Hadoop其实是由一系列的软件库组成的框架。这些软件库也可称作功能模块,它们各自负责了 Hadoop 的一部分功能,其中最主要的是 Common、HDFS 和 MapReduce。HDFS 负责数据的存储,MapReduce负责数据的计算,Common则提供远程过程调用RPC、序列化机制等。
而从字面来说:Hadoop没有任何实际的意义。Hadoop这个名字不是缩写,它是一个虚构的名字。Hadoop 的创建者 Doug Cutting这样解释Hadoop这一名称的来历:“这个名字是我的孩子给一头吃饱了的棕黄色大象取的。我的命名标准是简短,容易发音和拼写,没有太多含义,并且不会被用于别处。小孩子是这方面的高手。”所以我们看到这头欢快的大象也随着 Hadoop 的流行而逐渐深入人心(如图1-1所示)。
2.Hadoop适合处理大规模数据
这是Hadoop一个非常重要的特点和优点,Hadoop海量数据的吞吐能力十分可观,并且能够实现分布式存储和分布式计算,扩展能力十分优秀。在2008年的时候,Hadoop打破297s的世界纪录,成为最快的TB级数据排序系统,仅用时209 s。
3.Hadoop被部署在一个集群上
承载Hadoop的物理实体,是一个物理的集群。所谓集群,是一组通过网络互联的计算机,集群里的每一台计算机称作一个节点。Hadoop被部署在集群之上,对外提供服务。当节点数量足够多的时候,故障将成为一种常态而不是异常现象,Hadoop在设计之初就将故障的发生作为常态进行考虑,数据的灾备以及应用的容错对于用户来说都是透明的,用户得到的只是一个提供高可用服务的集群。
了解了上面三点,我们就可以开始准备电梯演讲了。麦肯锡对电梯演讲的要求是“凡事要归纳为三点”,因为人们一般只能记得住一二三而记不住四五六,基于此,我们的Hadoop电梯演讲为“Hadoop是一个提供分布式存储和计算的软件框架,它具有无共享、高可用、弹性可扩展的特点,非常适合处理海量数据”,一共46个字。
一般来说,狭义的Hadoop仅代表了Common、HDFS和MapReduce模块。但是开源世界的创造力是无穷的,围绕 Hadoop 有越来越多的软件蓬勃出现,方兴未艾,构成了一个生机勃勃的Hadoop生态圈。在特定场景下,Hadoop有时也指代Hadoop生态圈。
图1-2所示是一个Hadoop生态圈的架构图。
● Hadoop Common是Hadoop体系最底层的一个模块,为Hadoop各子项目提供各种工具,如系统配置工具Configuration、远程过程调用RPC、序列化机制和日志操作等,是其他模块的基础。
● HDFS(Hadoop Distributed File System,Hadoop分布式文件系统),是Hadoop 的基石。HDFS是一个具有高度容错性的文件系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。
● MapReduce是一种编程模型,利用函数式编程的思想,将对数据集处理的过程分为Map和 Reduce 两个阶段。MapReduce 的这种编程模型非常适合进行分布式计算。Hadoop提供了MapReduce的计算框架,实现了这种编程模型,用户可以通过Java、C++、Python、PHP等多种语言进行编程。
● HBase 来源于谷歌的BigTable 论文,HBase 是一个分布式的、面向列的开源数据库。采用了BigTable的数据模型——键/值存储。HBase擅长大规模数据的随机、实时读写访问。
● 在所有分布式系统中,都需要考虑一致性的问题,ZooKeeper 作为一个分布式的服务框架,基于Fast Paxos算法,解决了分布式系统中一致性的问题。ZooKeeper 提供了配置维护、名字服务、分布式同步、组服务等。
● Hive 最早是由Facebook 开发并使用,是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,提供简单的 SQL 查询功能,并将 SQL 语句转换为MapReduce 作业运行。其优点是学习成本低,对于常见的数据分析需求不必开发专门的MapReduce作业,十分适合大规模数据统计分析。Hive对于Hadoop来说是非常重要的模块,大大降低了Hadoop的使用门槛。
● Pig 和 Hive 类似,也是对大型数据集进行分析和评估的工具,不过与 Hive 提供 SQL接口不同的是,它提供了一种高层的、面向领域的抽象语言:Pig Latin,Pig 也可以将Pig Latin 脚本转化为MapReduce 作业。与SQL 相比,Pig Latin 更加灵活,但学习成本稍高。
● Impala 由 Cloudera 公司开发,可以对存储在 HDFS、HBase 的数据提供直接查询互动的 SQL。除了像 Hive 使用相同的统一存储平台,Impala 也使用相同的元数据、SQL语法、ODBC驱动程序和用户界面(Hue Beeswax)。Impala 还提供了一个熟悉的面向批量或实时查询的统一平台。Impala 的特点是对于中等数据量的查询非常迅速,其性能大幅领先于Hive。从图1-2可以看出,Impala并没有基于MapReduce的计算框架,这也是Impala可以大幅领先Hive的原因。
● Mahout是一个机器学习和数据挖掘库,它利用MapReduce编程模型实现了k-means、Native Bayes、Collaborative Filtering等经典的机器学习算法,并使其具有良好的可扩展性。
● Flume 是 Cloudera 提供的一个高可用、高可靠、分布式的海量日志采集、聚合和传输系统,Flume 支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种数据接受方(可定制)的能力。
● Sqoop是SQL to Hadoop的缩写,主要作用在于在结构化的数据存储(数据库)与Hadoop之间进行数据双向交换。也就是说,Sqoop可以将关系型数据库(如MySQL、Oracle等)的数据导入Hadoop的HDFS、Hive中,也可以将HDFS、Hive的数据导出到关系型数据库中。Sqoop充分利用了Hadoop的优点,整个导出导入都是由MapReduce计算框架实现并行化,非常高效。
说Hadoop催生了一个产业毫不过分,目前围绕Hadoop做二次开发的公司非常多,其中最著名的当属Cloudera和Hortonworks(两家公司的LOGO如图1-3所示)。这两家公司技术实力都非常雄厚,其中Cloudera开发的CDH (Cloudera’s Distribution for Hadoop)已成为生产环境下装机量最大Hadoop发行版。CDH的特点在于稳定,并有许多重要的补丁、向后移植和更新。而Hontonworks最著名的则是DAG (有向无环图)计算框架Tez。在一些应用场景中,为了利用 MapReduce 解决问题,需将问题分解为若干个有依赖关系的作业,形如一个有向无环图,目前Hadoop的计算框架并不支持有向无环图的计算,而Tez的出现很好地解决了这一问题。
云计算自从诞生之日起,短短几年就在各个行业产生了巨大的影响,而 Hadoop 作为云计算时代最耀眼的明星,又和云计算的提出者Google有着千丝万缕的联系。
自从2006 年8 月9 日Google 首席执行官埃里克·施密特在搜索引擎大会(SES San Jose 2006)率先提出了“云计算”这个名词后,其概念众说纷纭,相关领域的各方机构和专家分别从不同的角度对云计算进行了定义,有的从应用场景划分,有的从资源角度划分。本书选取美国国家标准技术研究院(NIST)对云计算的定义:“云计算是一种可以通过网络方便地接入共享资源池,按需获取计算资源(这些资源包括网络、服务器、存储、应用、服务等)的服务模型。共享资源池中的资源可以通过较少的管理代价和简单业务交互过程而快速部署和发布。”
从上面这个定义可以归纳出云计算的5个特点。
(1)按需提供服务:以服务的形式为用户提供应用程序、数据存储、基础设施等资源,并可以根据用户需求自动分配资源,而不需要系统管理员干预。例如亚马逊弹性计算云(Amazon Elastic Compute Cloud,Amazon EC2),用户可以通过填写Web 表单将自己所需要的配置,比如CPU核数、内存大小提交给亚马逊,从而动态地获得计算能力。
(2)宽带网络访问:用户可以利用各种终端设备(如 PC 机、笔记本电脑、智能手机等)随时随地通过互联网访问云计算服务。
(3)资源池化:资源以共享资源池的方式统一管理。利用虚拟化技术,将资源分享给不同用户,资源的放置、管理与分配策略对用户透明。
(4)高可伸缩性:服务的规模可快速伸缩,以自动适应业务负载的动态变化。用户使用的资源同业务的需求相一致,避免了因为服务器性能过载或冗余而导致的服务质量下降或资源浪费。
(5)可量化的服务:云计算中心都可以通过监控软件监控用户的使用情况,并根据资源的使用情况对服务计费。
另外云计算还有些比较明显且重要的特点。
(1)大规模:承载云计算的集群一般都具有超大的规模,Google的有“信息核电站”之称的云计算中心具有100多万台服务器,Amazon、IBM、微软和雅虎等公司的“云”均具有几十万台服务器的规模,从这点上来看,云将赋予用户前所未有的计算能力。
(2)服务极其廉价:“云”的特殊容错机制使得可以采用极其廉价的节点来构建云;“云”的自动化管理使数据中心管理成本大幅降低;“云”的公用性和通用性使资源的利用率大幅提升;“云”设施可以建在电力丰富的地区,从而大幅降低能源成本。因此“云”具有极高的性价比。Google 中国区前总裁李开复称,Google 每年投入约 16 亿美元构建云计算数据中心,所获得的能力相当于使用传统技术投入 640 亿美元,构建云计算数据中心投入的成本是使用传统技术投入的成本的 1/40。根据微软公布的数据,使用微软的云计算平台 Windows Azure的解决方案部署自己的Web应用,将会节约三分之一的成本,而成本还将会随着使用时间的延长而进一步降低。这正是为什么不仅CTO关注云计算,连CEO和CFO也对云计算高度关注的原因。
之所以称之为“云”,是因为云计算在某些地方和现实中的云非常符合,云的规模可以动态伸缩,它的边界是模糊的,云在空中飘忽不定,无法也无需确定它的具体位置,但它确实存在于某处。
云计算按照服务类型大致可以分为基础设施即服务(Infrastructure as a Service,IaaS)、平台即服务(Platform as a Service,PaaS)、软件即服务(Software as a Service,SaaS)三类,如图1-4所示。
IaaS作为云计算架构最底层,利用虚拟化技术将硬件设备等基础资源封装成服务供用户使用,用户相当于在使用裸机,既可以让它运行Windows,也可以让它运行Linux,既可以做Web服务器,也可以做数据库服务器(架构如图1-5所示),但是用户必须考虑如何才能让多个节点(虚拟机)协同工作起来。IaaS最大的优势在于它允许用户动态申请或释放节点,按使用量和使用时间计费。典型的虚拟化产品包括 VMware vShpere、微软的 Hyper-V、开源的 KVM、开源的Xen。Amazon EC2/S3 利用的是Xen这种技术。
而PasS对资源进行更进一步的抽象,它提供了用户应用程序的应用环境,典型的如Google App Engine 等。Google App Engine 使用户可以在Google提供的基础架构上运行、开发、托管自己的应用程序。Google App Engine 应用程序易于构建和维护,并可根据用户的访问量和数据存储需要的增长轻松扩展。使用Google App Engine,不再需要维护服务器,只需上传你的应用程序即可。PaaS自身负责资源的动态扩容、容错灾备,用户的应用程序不需过多考虑节点间的配合问题,但与此同时,用户的自主权降低,必须使用特定的编程环境并遵照特定的编程模型,这有点像在高性能集群计算机里进行 MPI 编程,只适合于解决某些特定的计算问题。例如, Google App Engine 只允许使用Python 和Java 语言,基于Django 的Web 应用框架,使用Google App Engine SDK来开发在线应用服务。
SaaS的针对性更强,它将某些特定应用软件功能封装成服务,如Salesforce公司提供的在线客户关系管理(Client Relationship Management,CRM)服务。SaaS 既不像IaaS 一样提供计算或存储资源类型的服务,也不像PaaS一样提供运行用户自定义应用程序的环境,它只提供某些专门用途的服务供应用调用。
通过以上三个层面对云计算的介绍,我们可以看出云计算是集成服务的,图1-6比较清晰地表明云计算和服务集成之间的关系。
需要指出的是,随着云计算的深化发展,不同云计算解决方案之间相互渗透融合,同一种产品往往横跨两种以上的类型。例如,Amazon Web Service 是以IaaS 为基础的,但新提供的弹性MapReduce服务模仿了Google的MapReduce、SimpleDB模仿了Google的BigTable,这两者属于PaaS的范畴,而它新提供的电子商务服务FPS和DevPay以及网站访问统计服务Alexa Web服务则属于SaaS的范畴。
云计算技术作为 IT 产业界的一场技术革命,已经成为 IT 行业未来发展的方向。各国政府也纷纷将云计算服务视为国家软件产业发展的新机遇。需求驱动、技术驱动和政策驱动三大驱动力给云计算的发展提供了极大助力,在国外,由于云计算技术发展较早,有较强的技术基础和运营经验,商业模式也较为清晰,尤其是在美国,Google、Amazon、IBM、微软和Yahoo 等都是云计算技术的先行者和佼佼者,众多成功公司还包括 Facebook、VMware、Salesforce等。而国内虽然政策力度非常大,资金投入也比较多,但由于起步较晚,技术和商业模式学习欧美,采用复制并本地化的发展路线,因此云计算技术仍属于初级阶段。国内云计算技术走在前列的有华为公司、阿里巴巴集团、百度等,主要以互联网企业巨头和系统集成提供商为主。
在谷歌提出“云计算”的概念之前,谷歌工程师在全球顶级计算机会议 OSDI 和 SOSP上连续发表 3 篇论文:SOSP 2003 会议上的 The Google File System、OSDI 2004 会议上的MapReduce: Simpliyed Data Processing on Large Clusters 和OSDI 2006 会议上的Bigtable: A Distributed Storage System for Structured Data,在工业界和学术界引起了不小的震动,随后世界上顶级的开源团队(Yahoo、ASF)接手将其实现,可以说 Hadoop 是云计算的产物,是云计算技术的一种实现。
Hadoop做为一个分布式的软件框架,拥有云计算PaaS层的所有特点,是云计算的重要组成部分。Hadoop 的分布式文件系统 HDFS 抽象了所有硬件资源,使其对于用户来说是透明的,它提供了数据的冗余,自动灾备,也可以动态地增加、减少节点;Hadoop 也提供 Java、C++、Python 等应用程序的运行环境,但如果想基于 Hadoop 做应用开发,则必须参照 MapReduce 的编程模型,用户完全不需要考虑各个节点相互之间的配合。另外Hadoop还提供自己独特的数据库服务HBase以及数据仓库服务Hive。Hadoop也可以搭建在 IaaS 环境下,《纽约时报》使用 Hadoop 做文字处理就是基于 Amazon EC2,当然这不是必须的。
Hadoop可以看做云计算技术的一种实现,而云计算的概念则更广阔,并不拘泥于某种技术。Hadoop作为云计算领域的一颗明星,目前已经得到越来越广泛的应用。
在人们对云计算这个词汇耳熟能详之后,大数据这个词汇又在最短时间内进入大众视野。云计算对于普通人来说就像云一样,一直没有机会能够真正感受到,而大数据则更加实际,是确确实实能够改变人们生活的事物。Hadoop从某个方面来说,与大数据结合得更加紧密,它就是为大数据而生的。
“大数据”(big data),一个看似通俗直白、简单朴实的名词,却无疑成为了时下IT 界最炙手可热的名词,在全球引领了新一轮数据技术革命的浪潮。通过 2012 年的蓄势待发,2013 年被称为世界大数据元年,标志着世界正式步入了大数据时代。
现在来看看我们如何被数据包围着。在现实的生活中,一分钟或许微不足道,或许连200字也打不了,但是数据的产生却是一刻也不停歇的。来看看一分钟到底会有多少数据产生:YouTube 用户上传 48 小时的新视频,电子邮件用户发送 204 166 677 条信息,Google收到超过 2 000 000 个搜索查询,Facebook 用户分享 684 478 条内容,消费者在网购上花费272 070 美元,Twitter 用户发送超过 100 000 条微博,苹果公司收到大约 47 000 个应用下载请求,Facebook 上的品牌和企业收到 34 722 个“赞”,Tumblr 博客用户发布 27 778 个新帖子,Instagram 用户分享 36 000 张新照片,Flicker 用户添加 3 125 张新照片,Foursquare 用户执行 2 083 次签到,571 个新网站诞生,WordPress 用户发布 347 篇新博文,移动互联网获得217个新用户。
数据还在增长着,没有慢下来的迹象,并且随着移动智能设备的普及,一些新兴的与位置有关的大数据也越来越呈迸发的趋势。
那么大数据究竟是什么?我们来看看权威机构对大数据给出的定义。国际顶级权威咨询机构麦肯锡说:“大数据指的是所涉及的数据集规模已经超过了传统数据库软件获取、存储、管理和分析的能力。这是一个被故意设计成主观性的定义,并且是一个关于多大的数据集才能被认为是大数据的可变定义,即并不定义大于一个特定数字的TB才叫大数据。因为随着技术的不断发展,符合大数据标准的数据集容量也会增长;并且定义随不同行业也有变化,这依赖于在一个特定行业通常使用何种软件和数据集有多大。因此,大数据在今天不同行业中的范围可以从几十TB到几PB。”
从上面的定义我们可以看出以下几点。
(1)多大的数据才算大数据,这并没有一个明确的界定,且不同行业有不同的标准。
(2)大数据不仅仅只是大,它还包含了数据集规模已经超过了传统数据库软件获取、存储、分析和管理能力这一层意思。
(3)大数据不一定永远是大数据,大数据的标准是可变的,在 20 年前 1 GB 的数据也可以叫大数据,可见,随着计算机软硬件技术的发展,符合大数据标准的数据集容量也会增长。
IBM说:“可以用三个特征相结合来定义大数据:数量(Volume)、种类(Variety)和速度(Velocity),或者就是简单的3V,即庞大容量、极快速度和种类丰富的数据。”[1]
(1)数据量(Volume):如今存储的数量正在急剧增长,毫无疑问我们正深陷在数据之中。我们存储所有事物——环境数据、财务数据、医疗数据、监控数据等。有关数据量的对话已从TB级别转向 PB级别,并且不可避免地转向ZB级别。现在经常听到一些企业使用存储集群来保存数PB的数据。随着可供企业使用的数据量不断增长,可处理、理解和分析的数据比例却不断下降。
(2)数据的多样性(Variety):与大数据现象有关的数据量为尝试处理它的数据中心带来了新的挑战:它多样的种类,随着传感器、智能设备以及社交协作技术的激增,企业中的数据也变得更加复杂,因为它不仅包含传统的关系型数据,还包含来自网页、互联网日志文件(包括点击流数据),搜索索引、社交媒体论坛、电子邮件、文档、主动和被动的传感器数据等原始、半结构化和非结构化数据。简言之,种类表示所有数据类型。
(3)数据的速度(Velocity):就像我们收集和存储的数据量和种类发生了变化一样,生成和需要处理数据的速度也在变化。不要将速度的概念限定为与数据存储库相关的增长速率,应动态地将此定义应用到数据——数据流动的速度。有效处理大数据要求在数据变化的过程中对它的数量和种类执行分析,而不只是在它静止后执行分析。
最近,IBM在以上3V的基础上归纳总结了第4个V——Veracity(真实和准确)。“只有真实而准确的数据才能让对数据的管控和治理真正有意义。随着社交数据、企业内容、交易与应用数据等新数据源的兴起,传统数据源的局限性被打破,企业愈发需要有效的信息治理以确保其真实性及安全性。”
接下来让我们来剖析下大数据突出的特征:多样性。图1-7显示了几种不同结构类型数据的增长趋势,从图中可以看到,未来数据增长的80%~90%将来自于不是结构化的数据类型(半结构化数据、准结构化数据或非结构化数据)。
结构化数据:包括预定义的数据类型、格式和结构的数据,例如,事务性数据和联机分析处理,如表1-1所示。
半结构化数据:具有可识别的模式并可以解析的文本数据文件,例如自描述和具有定义模式的XML数据文件,如代码清单1-1所示。
代码清单1-1 半结构化数据示例
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!-- Do not modify this file directly. Instead, copy entries that you -->
<!-- wish to modify from this file into mapred-site.xml and change them -->
<!-- there. If mapred-site.xml does not already exist, create it. -->
<configuration>
<property>
<name>hadoop.job.history.location</name>
<value></value>
</property>
<property>
<name>hadoop.job.history.user.location</name>
<value></value>
</property>
</configuration>
准结构化数据:具有不规则数据格式的文本数据,使用工具可以使之格式化,例如包含不一致的数据值和格式的网站点击数据,如 http://zh.wikipedia.org/wiki/Wikipedia:%E9%A6%96%E9%A1%B5。
非结构化数据是没有固定结构的数据,通常保存为不同的类型文件,如文本文档、PDF文档、图像和视频。
虽然上面显示了 4 种不同的、互相分离的数据类型,但实际上,有时这些数据类型是可以被混合在一起的。例如,一个传统的关系型数据库管理系统保存着一个软件支持呼叫中心的通话日志。这里有典型的结构化数据,比如日期/时间戳、机器类型、问题类型、操作系统,这些都是在线支持人员通过图形用户界面上的下拉式菜单输入的。另外,还有非结构化数据或半结构化数据,比如自由形式的通话日志信息,这些可能来自包含问题的电子邮件,或者技术问题和解决方案的实际通话描述,最重要的信息通常是藏在这里的。另外一种可能是与结构化数据有关的实际通话的语音日志或者音频文字实录。即使是现在,大多数分析人员还无法分析这种通话日志历史数据库中的最普通和高度结构化的数据。因为挖掘文本信息是一项工作强度很大的工作,并且无法实现简单的自动化。人们通常最熟悉结构化数据的分析,然而,半结构化数据(XML)、准结构化数据(网站地址字符串)和非结构化数据带来了不同的挑战,需要使用不同的技术来分析。
先来看一则《纽约时报》报道的新闻。一位愤怒的父亲跑到美国Targe t超市投诉他近期收到超市寄给他大量婴儿用品广告,而他的女儿还只不过是个高中生,但一周以后这位愤怒的父亲再次光临并向超市道歉,因为Target发来的婴儿用品促销广告并不是误发,他的女儿的确怀孕了。《纽约时报》的这则故事让很多人第一次感受到了变革,这次变革和人类经历过的若干次变革最大的不同在于:它发生得悄无声息,但它确确实实改变了我们的生活。各行各业的先知先觉者已经从与数据共舞中初尝到了甜头,而越来越多的后来者和新进者都希望借助云计算和大数据这波浪潮去撬动原有市场格局或开辟新的商业领域。这也难怪麦肯锡称大数据将会是传统四大生产要素之后的第五大生产要素。
1.你能一直赢吗?
风靡全球的网络游戏《英雄联盟》拥有数以千万计的用户群体。每天深夜,当大多数玩家已经奋战一天,呼呼大睡的时候,数据服务器正紧张地劳作着。世界各地的运营商会把当日的数据发送到位于美洲的数据中心。随即一个巨大的数据分析引擎转动起来,需要执行上千个数据分析的任务。当日所有的比赛都会被分析,数据分析师会发现,某一个英雄单位太强或太弱,在接下来的2~3周内,会推出一个新补丁,及时调整所有的平衡性问题,并加入一个新单位。整个游戏被保持在一个快速更新,并且良好平衡的状态。正是靠着大数据的魔力,《英雄联盟》才能成为这个时代最受欢迎的游戏之一。
2.你喜欢这个吗?
产品推荐是Amazon的发明,它为Amazon等电子商务公司赢得了近1/3的新增商品交易。产品推荐的一个重要方面是基于客户交易行为分析的交叉销售。根据客户信息、客户交易历史、客户购买过程的行为轨迹等客户行为数据,以及同一商品其他访问或成交客户的客户行为数据,进行客户行为的相似性分析,为客户推荐产品,包括浏览这一产品的客户还浏览了哪些产品、购买这一产品的客户还购买了哪些产品、预测客户还喜欢哪些产品等。对于领先的B2C网站如京东、亚马逊等,这些数据是海量存在的。
产品推荐的另一个重要方面是基于客户社交行为分析的社区营销。通过分析客户在微博、微信、社区里的兴趣、关注、爱好和观点等数据,投其所好,为客户推荐他本人喜欢的、或者是他的圈子流行的、或推荐给他朋友的相关产品。
通过对客户行为数据的分析,产品推荐将更加精准、个性化。传统企业既可以依赖大型电子商务公司和社区网络的产品推荐系统提升销售量,也可以依靠企业内部的客户交易数据、公司自有的电子商务网站等直销渠道、企业社区等进行客户行为数据的采集和分析,实现企业直销渠道的产品推荐。
基于大数据应用的行业实例数不胜数,并且都为各个行业带来可观的效益,甚至可以改变游戏规则。对于未来,我们会发现在电影中出现的预测犯罪、智慧城市等情景都会由于大数据处理技术的进步一一实现,这完全不是遥远的梦想。
首先,简单概括一下云计算和大数据之间的关系。在很大程度上它们是相辅相成的,最大的不同在于:云计算是你在做的事情,而大数据是你所拥有的东西。以云计算为基础的信息存储、分享和挖掘手段为知识生产提供了工具,而通过对大数据分析、预测会使得决策更加精准,两者相得益彰。从另一个角度讲,云计算是一种IT理念、技术架构和标准,而云计算不可避免地会产生大量的数据。大数据技术与云计算的发展密切相关,大型的云计算应用不可或缺的就是数据中心的建设,所以大数据技术是云计算技术的延伸。
作为云计算PaaS层技术的代表,Hadoop可以以一种可靠、高效、可扩展的方式存储、管理“大数据”,如图 1-8 所示。Hadoop 及其生态圈为管理、挖掘大数据提供了一整套相对成熟可靠的解决方案。从功能上说,Hadoop可以称作一个“大数据管理平台”。下面我们先对Hadoop的核心组件做一个简单的介绍,让读者对Hadoop有个初步的认识。
1.海量数据的摇篮——HDFS
作为Hadoop分布式文件系统,HDFS处于Hadoop生态圈的最下层,存储着所有的数据,支持着Hadoop 的所有服务。它的理论基础源于Google 的The Google File System 这篇论文,它是GFS的开源实现。
HDFS的设计理念是以流式数据访问模式,存储超大文件,运行于廉价硬件集群之上。
2.处理海量数据的利器——MapReduce
MapReduce是一种编程模型,Hadoop根据Google的MapReduce论文将其实现,作为Hadoop的分布式计算模型,是Hadoop的核心。基于这个框架,分布式并行程序的编写变得异常简单。综合了HDFS的分布式存储和MapReduce的分布式计算,Hadoop在处理海量数据时,性能横向扩展变得非常容易。
3.列式存储的代表——HBase
HBase是对Google的BigTable的开源实现,但又和BigTable存在许多不同之处。HBase是一个基于 HDFS 的面向列的分布式数据库,擅长实时地随机读/写超大规模数据集。它也是 Hadoop 非常重要的组件,但由于它主要做实时数据处理,而本书主要关注的是离线数据分析,所以本书不会过多介绍 HBase,有兴趣的读者可以参阅 HBase: The Definitive Guide。
简言之,由于 Hadoop 可以基于分布式存储进行分布式计算,横向扩展能力非常优秀,所以Hadoop非常适合并且能够胜任存储、管理、挖掘“大数据”的任务。
1.Storm
Hadoop 主要擅长进行批处理,而现实生活中,有很多数据是属于流式数据,即计算的输入并不是一个文件,而是源源不断的数据流,如网上实时交易所产生的数据。流式数据有以下特点。
(1)数据实时到达,需要实时处理。
(2)数据是流式源源不断的,大小可能无穷无尽。
(3)系统无法控制将要处理的新到达数据元素的顺序,无论这些数据元素是在同一个数据流中还是跨多个数据流。
(4)一旦数据流中的某个数据经过处理,要么被丢弃或者无状态。
Storm也是一个比较成熟的分布式的流计算平台,擅长做流处理(stream processing)或者复杂事件处理(complex event processing,CEP),Storm有以下几个关键特性。
(1)适用场景广泛。
(2)良好的伸缩性。
(3)保证数据无丢失。
(4)异常健壮。
(5)良好的容错性。
(6)支持多语言编程。
值得一提的是,Storm采用的计算模型并不是MapReduce,并且MapReduce也已经被证明不适合做流处理。
2.Apache Spark
Apache Spark 是一种与Hadoop 相似的开源集群计算环境,在性能和迭代计算上很有优势,现在是 Apache 孵化的顶级项目。Spark 由加州大学伯克利分校 AMP 实验室(Algorithms, Machines, and People Lab)开发,可用来构建大型的、低延迟的数据分析应用程序。Spark 启用了内存分布式数据集,除了能够提供交互式查询外,它还可以优化迭代工作负载。Spark 是由Scala语言实现的,它将Scala作为其应用程序框架,而Scala的语言特点也铸就了大部分Spark的成功。与Hadoop不同,Spark和Scala能够紧密集成,其中的Scala可以像操作本地集合对象一样轻松地操作分布式数据集。
Spark最大的优点在于对于一定数据量的查询可以达到毫秒级,在某些迭代计算的场景(如机器学习算法)中可以领先Hadoop数十倍到一百倍,并且它支持DAG(有向无环图)计算。
3.Hadoop 2.0
本书 Hadoop 如没做特别说明,都指第一代 Hadoop,Hadoop 2.0 从架构上来说与 Hadoop完全不同,结构更加合理,并解决了第一代Hadoop中主节点(master node)的单点性能故障和性能瓶颈的问题。但与第一代Hadoop最大的不同在于,它引入了一个资源管理平台的组件——YARN,通过这个组件,我们可以共用底层存储(HDFS),计算框架采取可插拔式的配置,支持Storm、Spark等其他开源计算框架,也就是说,同样的集群既可以做批处理也可以做流处理甚至是 MPI(大规模并行计算),如图1-9所示。
和云计算、大数据的概念相比,数据挖掘和商业智能的概念早已被学术界和工业界所接受,但由于大数据的出现,又为它们注入了新的活力,“大数据时代的商业智能”的概念不断被业界所提及,那么它们究竟是什么呢?
先来看一个例子,Google 的 Flu Trends(流感趋势)使用特殊的搜索项作为流感活动的指示器。它发现了搜索流感相关信息的人数与实际具有流感症状的人数之间的紧密联系。当与流感相关的所有搜索聚集在一起时,一个模式就出现了。使用聚集的搜索数据,Google的 Flu Trends 可以比传统的系统早两周对流感活动做出评估。这个例子说明了数据挖掘如何把大型数据集转化为知识。现在,我们可以对数据挖掘做一个简短的定义,数据挖掘就是“数据→知识”。
带着这个概念,我们来一步一步分析数据挖掘的本质。数据挖掘可以看作是信息技术自然而然进化的结果。数据库和数据管理产业的一些关键功能不断发展,大量数据库系统提供的查询和事务处理已经司空见惯,高级数据分析自然成为下一步。
20世纪60年代,数据库和信息技术已经系统地从原始文件处理演变成复杂的功能强大的数据库系统。
20世纪70年代,数据库从层次型数据库、网状数据库发展到关系型数据库,用户可以通过查询语言灵活方便地访问数据。
20世纪80年代中后期,数据库技术转向高级数据库系统、支持高级数据分析的数据仓库和数据挖掘,基于Web的数据库。
硬件的飞速发展,导致了功能强大和价格可以接受的计算机、数据收集设备和存储介质大量涌现。这些软件和硬件的进步大大推动了数据库和信息产业的发展,也导致了数据库管理系统分成了两个发展方向:OLTP(联机事务处理)和OLAP(联机分析处理)。图1-10展示了数据库管理系统的发展过程。
而OLAP的出现也导致数据仓库这种数据存储结构的出现。数据仓库是一种多个异构数据源在单个站点以统一的模式组织的存储,以支持管理决策。大量的数据累积在数据库和数据仓库中,数据丰富但数据分析工具缺乏,这种情况被描述为“数据丰富但信息贫乏”。快速增长的“大数据”,没有强有力的工具,理解它们已经远远超出了人的能力。结果,收集了大量数据的数据库和数据仓库变成了“数据坟墓”——几乎不再访问的数据档案(如历史订单)。这样,重要的决策常常不是基于数据库和数据仓库中含有丰富信息的数据,而是基于决策者的直觉。尽管在开发专家系统和知识库系统方面已经做出很大的努力,但是这种系统通常依赖用户或领域专家人工地将知识输入知识库。不幸的是,这一过程常常有偏差和错误,并且费用高、耗费时间。数据和信息之间的鸿沟越来越宽,这就要求必须系统地开发数据挖掘工具,将数据坟墓转换为“数据金块”。
作为一个多学科交叉的领域,数据挖掘可以用多种方式定义,例如“从数据中挖掘知识”、“知识挖掘”等。许多人把数据挖掘视为另一个流行术语——数据中的知识发现(Knowledge Discovery in Database,KDD)的同义词,而另一些人只是把数据挖掘视为知识发现过程的一个基本步骤。知识发现的过程如图1-11所示,由以下步骤的迭代序列组成[2]。
(1)数据清理:消除噪声和删除不一致数据。
(2)数据集成:多种数据源可以组合在一起。
(3)数据选择:从数据库中提取与分析任务相关的数据。
(4)数据变换:通过汇总或聚集操作,把数据变换和统一成适合挖掘的形式。
(5)数据挖掘:基本步骤,使用智能方法提取数据模式。
(6)模式评估:根据某种兴趣度量,识别代表知识的真正有趣模式。
(7)知识表示:使用可视化和知识表示技术,向用户提供挖掘的知识。
目前信息产业界的一个流行趋势是将数据清理和数据集成作为数据预处理步骤执行,结果数据存放在数据仓库中。步骤 1~步骤 4 都是在为数据挖掘准备数据。数据挖掘步骤可与用户或知识库交互,将有趣的模式提供给用户,或作为新的知识存放在知识库中。
因为数据仓库对于数据挖掘和本书都是一个比较关键的概念,在这里我们详细地来分析一下数据仓库的概念。
按照数据仓库系统构造方面的领衔设计师 William H.Inmon 的说法,数据仓库是一个面向主题的、集成的、时变的、非易失的数据集合,支持管理者的决策过程。这个简短而又全面的定义指出了数据仓库的主要特征,4个关键字:面向主题的、集成的、时变的、非易失的。
● 面向主题的(subject-oriented):数据仓库围绕一些重要的主题,如顾客、供应商、产品和销售组织。数据仓库关注决策者的数据建模和分析,而不是单位的日常操作和事务处理。因此数据仓库通常排除对于决策无用的数据,提供特定主题的简明视图。
● 集成的(integrated):通常,构造数据仓库是将多个异构数据源,如关系型数据库、一般文件和联机事务处理记录集成在一起。使用数据清理和数据集成技术,确保命名约定、编码结构、属性度量等的一致性。
● 时变的(time-variant):数据仓库从历史的角度(例如,过去5~10 年)提供信息。数据仓库中的关键结构都隐式地或显式地包含时间元素。
● 非易失的(nonvolatile):数据仓库总是物理地分离存放数据,这些数据源于操作环境下的应用数据。由于这种分离,数据仓库不需要事务处理、恢复和并发控制机制。数据的易失性在于操作型系统是一次访问和处理一个记录,可以对操作环境中的数据进行更新。但是数据仓库中的数据呈现出非常不同的特性,数据仓库中的数据通常是一次载入和访问的,但在数据仓库环境中并不进行一般意义上的数据更新。通常,它只需要两种数据访问操作:数据的初始化装入和数据访问。
操作数据库系统的主要任务是执行联机事务和查询处理。这种系统称作联机事务处理(OLTP)系统。它们涵盖了单位的大部分日常操作,如购物、库存、工资等,也被称作业务系统。另一方面,数据仓库系统在数据分析和决策方面为用户提供服务,这种系统称作联机分析处理(OLAP)系统。
OLTP和OLAP的主要区别有以下几个方面。
● 用户和系统的面向性:OLTP 是面向客户的,用于办事员、客户和信息技术专业人员的事务和查询处理。OLAP是面向市场的,用于知识工人(包括经理、主管和分析人员)的数据分析。
● 数据内容:OLTP 系统管理当前数据。通常,这种数据太琐碎,很难用于决策。OLAP系统管理大量历史数据,提供汇总和聚集机制,并在不同的粒度层上存储和管理信息。这些特点使得数据更容易用于有根据的决策。
● 视图:OLTP 系统主要关注一个企业或部门内部的当前数据,而不涉及历史数据或不同单位的数据。相比之下,由于单位的演变,OLAP 系统常常跨越数据库模式的多个版本。OLAP 系统还要处理来自不同单位的信息,以及由多个数据库集成的信息。由于数据量巨大,OLAP系统的数据通常也存放在多个存储介质上。
● 访问模式:OLTP 系统主要由短的原子事务组成。这种系统需要并发控制和恢复机制。然而,对OLAP系统的访问大部分是只读操作(由于大部门数据仓库存放历史数据,而不是最新数据),尽管这其中的许多操作可能是复杂的查询。
OLTP和OLAP的其他区别包括数据库大小、操作的频繁程度以及性能度量等。
既然操作数据库存放了大量数据,读者可能奇怪,为什么不直接在这种数据库上进行联机分析处理(OLAP),而是另外花费时间和资源去构造分离的数据仓库?。分离的主要原因是有助于提高两个系统的性能。操作数据库是为已知的任务和负载设计的,例如使用的主键索引、检索特定的记录、优化定制的查询。另一方面,数据仓库的查询通常是复杂的,涉及大量数据在汇总级的计算,可能需要特殊的基于多维视图的数据组织、存取方法和实现方法。在操作数据库上处理OLAP查询,可能会大大降低操作任务的性能。
此外,操作数据库支持多事务的并发处理,需要并发控制和恢复机制(例如,加锁和记日志),以确保一致性和事务的鲁棒性。通常,OLAP查询只需要对汇总和聚集数据记录进行只读访问。如果将并发控制和恢复机制用于这种OLAP操作,就会危害并行事务的运行,从而大大降低OLTP系统的吞吐量。
最后,数据仓库与操作数据库分离是由于这两种系统中数据的结构、内容和用法都不同。决策支持需要历史数据,而操作数据库一般不维护历史数据。在这种情况下,操作数据库中的数据尽管很丰富,但对于决策是远非完整的。决策支持需要整合来自异构源的数据(例如,聚集和汇总),产生高质量的、纯净的和集成的数据。相比之下,操作数据库只维护详细的原始数据(如事务),这些数据在进行分析之前需要整理。由于两种系统提供大不相同的功能,需要不同类型的数据,因此需要维护分离的数据库。
哪里有数据,哪里就有数据挖掘应用,这句话用来形容商业智能再合适不过了。数据仓库解决了存储问题,而OLAP技术提供了挖掘手段,企业自然而然会想到将数据利用起来,而商业智能就是最好的途径。
商业智能(Business Intelligence,BI)是一个统称,指的是用于支持制定业务决策的技能、流程、技术、应用和实践。商业智能对当前数据或历史数据进行分析,在理想情况下辅助决策者制定未来的业务决策。商业智能通常被理解为将企业中现有的数据转化为知识,帮助企业做出明智的业务经营决策的工具。商业智能是对商业信息的搜集、管理和分析过程,目的是使企业的各级决策者获得知识或洞察力(insight),促使他们做出对企业更有利的决策。从技术层面上讲,商业智能不是什么新技术,它只是数据仓库、OLAP等技术的综合运用[3]。
大多数的数据仓库是为了挖掘某种商业价值而创建的,但是商业智能和数据仓库之间的区别在于商业智能是定位于生成可向业务用户交付的产品,而数据仓库的目标只是着眼于对数据进行结构化的存储和组织,所以对于数据仓库,还需要 OLAP 技术,才能完成数据仓库到商业智能的转换过程。对于数据仓库来说,可以只关注数据本身,不需要专门考虑业务,而商业智能则更主要的是基于数据仓库的数据从业务的角度进行分析。如图1-12所示,商业智能主要使用到数据仓库技术和 OLAP 技术。商业智能系统通过对数据仓库的数据进行数据选择、抽取、加载后,使用数据挖掘方法提取知识,再用BI报表将知识呈现给决策者供其参考。
一款优秀的商业智能系统应该满足以下4个特性:准确、及时、价值高和可操作。准确性的意义是数据是可信的,及时性意味着数据可定期获取、价值高表示对商业用户有用,可操作性是指信息可以用于业务决策过程。
据预测,到 2020年,全球需要存储的数据量将达到 35万亿 GB,是 2009年数据存储量的 44 倍。根据 IDC 的研究,2010 年底全球的数据量已经达到 120 万 PB(或 1.2 ZB)。这些数据如果使用光盘存储,摞起来可以从地球到月球一个来回。对于商业智能而言,这里孕育着巨大的市场机会,庞大的数据就是一个信息金矿,但是海量数据也带给传统商业智能前所未有的压力。
数据是企业的重要资产。由于数据挖掘等商业智能技术的应用,让不少企业从大量的历史数据中剥茧抽丝,发现很多有价值的信息,大大改善了管理人员决策的科学性。不过,长期以来,商业智能的应用一直局限于结构化数据,其核心组件数据仓库最为擅长的也是结构化数据的存储与管理。
在大数据时代,一批新的数据挖掘技术正在涌现,有望改变我们分析处理海量数据的方式,使得我们更快、更经济地获得所需的结果。大数据技术就是要打破传统商业智能领域的局限,它在处理数据量上有了质的提高,传统商业智能限于技术瓶颈很大程度上是对抽样数据进行分析,而大数据技术的引入使得商业智能可以基于全量数据,这样让结果更加准确可信。大数据技术不但能处理结构化数据,而且还能分析和处理各种半结构化和非结构化数据,甚至从某种程度上,更擅长处理非结构化数据,比如 Hadoop。而在现实生活中,这样的数据更为普遍,增长得也更为迅速。比如,社交媒体中的各种交互活动、购物网站用户点击行为、图片、电子邮件等。可以说,正是此类数据的爆炸性增长催生了大数据相关技术的出现和完善。
而对于Hadoop来说,首先HDFS解决了海量数据存储的问题,Hive负责结构化数据的分析,而半结构化、非结构化数据的分析和数据清洗可根据需要编写MapReduce作业完成,整个过程都是对基于分布式存储的数据进行分布式计算,扩展性将比传统商业智能系统大大提升。另外Hadoop生态圈的Sqoop、Flume等实现了传统商业智能的一些功能模块,如日志收集、数据抽取等。可以说Hadoop及Hadoop生态圈为大数据的商业智能系统提供了一套完整、高效的解决方案。在本书的后半部分,将基于 Hadoop 设计和实现一个商业智能系统,在实现这个商业系统的过程中,读者可以发现我们无论采取大数据技术还是传统数据挖掘技术,遵循的方法论其实是一致的,希望读者可以从这个项目中举一反三,融会贯通。
本章将介绍Hadoop的第一个重要组成部分——HDFS。
HDFS 的设计理念源于非常朴素的思想:当数据集的大小超过单台计算机的存储能力时,就有必要将其进行分区(partition)并存储到若干台单独的计算机上,而管理网络中跨多台计算机存储的文件系统称为分布式文件系统(distribute filesystem)。该系统架构于网络之上,势必会引入网络编程的复杂性,因此分布式文件系统比普通文件系统更为复杂,例如,使文件系统能够容忍节点故障且不丢失任何数据,就是一个极大的挑战。通过本章的介绍,我们可以发现HDFS很好地完成了这个挑战。
准确地说,Hadoop 有一个抽象的文件系统概念,HDFS 只是其中的一个实现。Hadoop 文件系统接口由 Java 抽象类 org.apache.hadoop.fs.FileSystem 类定义,该类同时还继承了org.apache.hadoop.conf并且实现了Java的java.io.Closeable接口。表3-1所示是抽象类FileSystem的几个具体实现。
Hadoop 提供了许多文件系统的接口,用户可以选取合适的 URI 方案来实现对特定的文件系统的交互。例如,如果想访问本地文件系统,执行以下shell命令即可:
hadoop dfs -ls file:/// (最后一个/表示本地文件系统的根目录)
执行完成后,屏幕会打印出以下信息:
dr-xr-xr-x - root root 12288 2014-04-07 09:33 /lib64
drwxr-xr-x - root root 4096 2014-02-22 06:15 /media
drwxr-xr-x - root root 0 2014-05-02 10:03 /net
drwxr-xr-x - root root 4096 2011-09-23 07:50 /srv
drwx------ - root root 16384 2014-01-27 05:55 /lost+found
drwx------ - root root 4096 2014-01-27 06:20 /.dbus
dr-xr-xr-x - root root 4096 2014-04-07 09:34 /bin
-rw-r--r-- 1 root root 0 2014-05-02 17:01 /.autofsck
drwxr-xr-x - root root 4096 2014-01-27 05:56 /usr
dr-xr-xr-x - root root 12288 2014-04-07 09:34 /sbin
…
如果想访问HDFS文件系统,执行以下命令即可:
hadoop dfs -ls hdfs:///
执行完成后,屏幕会打印出以下信息:
Found 3 items
drwxr-xr-x - hadoop supergroup 0 2014-04-02 11:52 /home
drwxr-xr-x - hadoop supergroup 0 2014-04-06 12:13 /tmp
drwxr-xr-x - hadoop supergroup 0 2014-04-06 12:10 /user
作为Hadoop生态圈的基础,HDFS非常适合运行在廉价硬件集群之上,以流式数据访问模式来存储超大文件。简单的一句话,已经勾勒出HDFS的特点。
(1)适合存储超大文件:存储在HDFS的文件大多在GB甚至TB级别,目前阿里巴巴的集群存储的数据已经达到了60 PB。
(2)运行于廉价硬件之上:HDFS 在设计的时候,就已经认为在集群规模足够大的时候,节点故障并不是小概率事件,而可以认为是一种常态。例如,一个节点故障的概率如果是千分之一,那么当集群规模是1000台时,正常情况每天都会有节点故障。当节点发生故障时,HDFS能够继续运行并且不让用户察觉到明显的中断,所以HDFS并不需要运行在高可靠且昂贵的服务器上,普通的PC Server 即可。
(3)流式数据访问:HDFS 认为,一次写入,多次读取是最高效的访问模式。HDFS 存储的数据集作为 Hadoop 的分析对象,在数据集生成后,会长时间在此数据集上进行各种分析。每次分析都将涉及该数据集的大部分数据甚至全部数据,因此读取整个数据集的时间延迟比读取第一条记录的时间延迟更重要。
除了上面3点,HDFS也有一些短板。
(1)实时的数据访问弱:如果应用要求数据访问的时间在秒或是毫秒级别,那么HDFS是做不到的。由于HDFS针对高数据吞吐量做了优化,因而牺牲了读取数据的速度,对于响应时间是秒或是毫秒的数据访问,可以考虑使用HBase。
(2)大量的小文件:当Hadoop启动时,NameNode会将所有元数据读到内存,以此构建目录树。一般来说,一个HDFS上的文件、目录和数据块的存储信息大约在150字节左右,那么可以推算出,如果NameNode的内存为16 GB的话,大概只能存放480 万个文件,对于一个超大规模的集群,这个数字很快就可以达到。
(3)多用户写入,任意修改文件:HDFS 中的文件只能有一个写入者,并且写数据操作总是在文件末。它不支持多个写入者,也不支持在数据写入后,在文件的任意位置进行修改。事实上,如果不将hdfs-site.xml中的dfs.support.append设置为true的话,HDFS也不支持对文件进行追加操作。
在前面我们已经大致了解了HDFS的架构,下面将会详细地介绍架构中的每一部分。一个完整的 HDFS 运行在一些节点之上,这些节点运行着不同类型的守护进程,如 NameNode、DataNode、SecondaryNameNode,不同类型的节点相互配合,相互协作,在集群中扮演了不同的角色,一起构成了HDFS。
如图3-1所示,一个典型的HDFS集群中,有一个NameNode,一个SecondaryNode和至少一个DataNode,而HDFS客户端数量并没有限制。所有的数据均存放在运行DataNode进程的节点的块(block)里。
1.块
每个磁盘都有默认的数据块大小,这是磁盘进行数据读/写的最小单位,而文件系统也有文件块的概念,如ext3、ext2等。文件系统的块大小只能是磁盘块大小的整数倍,磁盘块大小一般是512 字节,文件系统块大小一般为几千字节,如ext3 的文件块大小为4 096 字节,Windows的文件块大小为 4 096 字节。用户在使用文件系统对文件进行读取或写入时,完全不需要知道块的细节,这些对于用户都是透明的。
HDFS同样也有块(block)的概念,但是HDFS的块比一般文件系统的块大得多,默认为64 MB,并且可以随着实际需要而变化,配置项为hdfs-site.xml 文件中的dfs.block.size 项。与单一文件系统相似,HDFS上的文件也被划分为块大小的多个分块,它是HDFS存储处理的最小单元。
某个文件data.txt,大小为 150 MB,如果此时HDFS 的块大小没有经过配置,默认为64 MB,那么该文件实际在HDFS中存储的情况如图3-2所示。
圆形为保存该文件的第一个块,大小为64 MB,方形为保存文件的第二个块,大小为64 MB,五边形为保存文件的第三个块,大小为22 MB,这里特别指出的,与其他文件系统不同的是, HDFS 小于一个块大小的文件不会占据整个块的空间,所以第三块的大小为 22 MB 而不是 64 MB。
HDFS 中的块如此之大的原因是为了最小化寻址开销。如果块设置的足够大,从磁盘传输数据的时间可以明显大于定位这个块开始位置所需的时间。这样,传输一个由多个块组成的文件的时间取决于磁盘传输的效率。得益于磁盘传输速率的提升,块的大小可以被设为 128 MB甚至更大。
在hdfs-site.xml文件中,还有一项配置为dfs.relication,该项配置为每个HDFS的块在Hadoop集群中保存的份数,值越高,冗余性越好,占用存储也越多,默认为 3,即有 2 份冗余,如果设置为2,那么该文件在HDFS中存储的情况如图3-3所示。
使用块的好处是非常明显的。
(1)可以保存比存储节点单一磁盘大的文件:块的设计实际上就是对文件进行分片,分片可以保存在集群的任意节点,从而使文件存储跨越了磁盘甚至机器的限制,如data.txt文件被切分为3个块,并存放在3个DataNode之中。
(2)简化存储子系统:将存储子系统控制单元设置为块,可简化存储管理,并且也实现了元数据和数据的分开管理和存储。
(3)容错性高:这是块非常重要的一点,如果将dfs.relication设置为2,如图3-2,那么任意一个块损坏,都不会影响数据的完整性,用户在读取文件时,并不会察觉到异常。之后集群会将损坏的块的副本从其他候选节点复制到集群中能正常工作的节点,从而使副本数回到配置的水平。
2.NameNode和SecondaryNameNode
NameNode也被称为名字节点,是HDFS的主从(master/slave)架构的主角色的扮演者。NameNode是HDFS的大脑,它维护着整个文件系统的目录树,以及目录树里所有的文件和目录,这些信息以两种文件存储在本地文件中:一种是命名空间镜像(File System Image,FSImage,也称为文件系统镜像),即 HDFS 元数据的完整快照,每次 NameNode 启动的时候,默认会加载最新的命名空间镜像,另一种是命名空间镜像的编辑日志(Edit Log)。
SecondaryNameNode,也被称为第二名字节点,是用于定期合并命名空间镜像和命名空间镜像的编辑日志的辅助守护进程。每个HDFS集群都有一个SecondaryNameNode,在生产环境下,一般SecondaryNameNode也会单独运行在一台服务器上。
FSImage 文件其实是文件系统元数据的一个永久性检查点,但并非每一个写操作都会更新这个文件,因为FSImage是一个大型文件,如果频繁地执行写操作,会使系统运行极为缓慢。解决方案是NameNode只将改动内容预写日志(WAL),即写入命名空间镜像的编辑日志(Edit Log)。随着时间的推移,Edit Log 会变得越来越大,那么一旦发生故障,将会花费非常多的时间来回滚操作,所以就像传统的关系型数据库一样,需要定期地合并FSImage 和Edit Log 日志。如果由NameNode来做合并的操作,那么NameNode在为集群提供服务时可能无法提供足够的资源,为了彻底解决这一问题,SecondaryNameNode应运而生。NameNode和SecondaryNameNode交互如图3-4所示。
(1)SecondaryNameNode 引导NameNode滚动更新Edit Log 文件,并开始将新的内容写入Edit Log.new。
(2)SecondaryNameNode 将 NameNode 的 FSImage 和 Edit Log 文件复制到本地的检查点目录。
(3)SecondaryNameNode 载入FSImage文件,回放Edit Log,将其合并到FSImage,将新的FSImage文件压缩后写入磁盘。
(4)SecondaryNameNode 将新的 FSImage 文件送回 NameNode,NameNode 在接受新的FSImage后,直接加载和应用该文件。
(5)NameNode将Edit Log.new 更名为Edit Log。
默认情况下,该过程每小时发生一次,或者当NameNode的Edit Log文件达到默认的64 MB也会被触发。
从名称上来看,初学者会以为当 NameNode 出现故障时,SecondaryNameNode 会自动成为新的 NameNode,也就是 NameNode 的“热备”。通过上面的介绍,我们清楚地认识到这是错误的。
3.DataNode
DataNode 被称为数据节点,它是 HDFS 的主从架构的从角色的扮演者,它在 NameNode的指导下完成I/O任务。如前文所述,存放在HDFS的文件都是由HDFS的块组成,所有的块都存放于DataNode节点。实际上,对于DataNode所在的节点来说,块就是一个普通的文件,我们可以去 DataNode 存放块的目录下观察(默认是$(dfs.data.dir)/current),块的文件名为blk_blkID。
DataNode 会不断地向 NameNode 报告。初始化时,每个 DataNode 将当前存储的块告知NameNode,在集群正常工作时,DataNode 仍然会不断地更新 NameNode,为之提供本地修改的相关信息,同时接受来自NameNode的指令,创建、移动或者删除本地磁盘上的数据块。
4.HDFS客户端
HDFS客户端是指用户和HDFS交互的手段,HDFS提供了非常多的客户端,包括命令行接口、Java API、Thrift 接口、C语言库、用户空间文件系统,本章将在3.3 节详细介绍如何与HDFS进行交互。
本节将回答本章开头的问题:如何使文件系统能够容忍节点故障且不丢失任何数据,也就是HDFS的容错机制。
1.心跳机制
在NameNode和DataNode之间维持心跳检测,当由于网络故障之类的原因,导致DataNode发出的心跳包没有被NameNode正常收到的时候,NameNode就不会将任何新的I/O操作派发给那个DataNode,该DataNode上的数据被认为是无效的,因此NameNode会检测是否有文件块的副本数目小于设置值,如果小于就自动开始复制新的副本并分发到其他DataNode节点。
2.检测文件块的完整性
HDFS会记录每个新创建文件的所有块的校验和。当以后检索这些文件时或者从某个节点获取块时,会首先确认校验和是否一致,如果不一致,会从其他DataNode节点上获取该块的副本。
3.集群的负载均衡
由于节点的失效或者增加,可能导致数据分布不均匀,当某个 DataNode 节点的空闲空间大于一个临界值的时候,HDFS会自动从其他DataNode迁移数据过来。
4.NameNode 上的FSImage和Edit Log 文件
NameNode 上的FSImage 和Edit Log 文件是HDFS 的核心数据结构,如果这些文件损坏了, HDFS 将失效。因而,NameNode由Secondary NameNode定期备份FSImage和Edit Log 文件, NameNode在Hadoop中确实存在单点故障的可能,当NameNode出现机器故障,手工干预是必须的。
5.文件的删除
删除并不是马上从 NameNode 移出命名空间,而是存放在/trash 目录随时可恢复,直到超过设置时间才被正式移除。设置的时间由hdfs-site.xml文件的配置项fs.trash.interval决定,单位为秒。
我们知道在HDFS中,NameNode作为集群的大脑,保存着整个文件系统的元数据,而真正数据是存储在 DataNode 的块中。本节将介绍 HDFS 如何读取和写入文件,组成同一文件的块在HDFS的分布情况如何影响HDFS读取和写入速度。
HDFS 会将文件切片成块并存储至各个 DataNode 中,文件数据块在 HDFS 的布局情况由NameNode和hdfs-site.xml中的配置dfs.replication共同决定。dfs.replication表示该文件在HDFS中的副本数,默认为3,即有两份冗余。
图3-5为dfs.replication为1的分布情况,即没有冗余。图3-6为dfs.replication为2的分布情况,即有一份冗余。
NameNode如何选择在哪个DataNode存储副本?这里需要在可靠性、写入速度和读取速度之间进行一些取舍。如果将所有副本都存储在一个节点之中,那么写入的带宽损失将是最小的,因为复制管道都是在单一节点上运行。但这样无论副本数设为多少,HDFS都不提供真实的冗余,因为该文件的所有数据都在一个节点之上,那么如果该节点发生故障的话,该文件的数据将会丢失。如果将副本放在不同数据中心,可以最大限度地提高冗余,但是对带宽的损耗非常大。即使在同一数据中心,也有不同的副本分布策略。其实,在发布的Hadoop 0.17.0 版本中改变了数据布局策略来辅助保持数据块在集群内分布相对均匀。从0.21.0版本开始,可即时选择块的分布策略。
Hadoop的默认布局是在HDFS客户端节点上放第一个副本,但是由于HDFS客户端有可能运行于集群之外,就随机选择一个节点,不过 Hadoop 会尽量避免选择那些存储太满或者太忙的节点。第二个副本放在与第一个不同且随机另外选择的机架中的节点上。第三个副本与第二个副本放在相同的机架,且随机选择另外一个节点。其他副本(如果dfs.replication大于3)放在集群随机选择的节点上,Hadoop也会尽量避免在相同的机架上放太多副本。当NameNode按照上面的策略选定副本存储的位置后,就会根据集群网络拓扑图创建一个管道。假设dfs.replication = 3,则如图3-7 所示。
这样的方法不仅提供了很好的数据冗余性(如果可能,块存储在两个机架中)并实现很好的负载均衡,包括写入带宽(写入操作只需要遍历一个交换机)、读取性能(可以从两个机架中进行选择读取)和集群中块的均匀分布。
HDFS 客户端可以通过多种不同的方式(如命令行、Java API 等)对HDFS 进行读写操作,这些操作都遵循同样的流程。HDFS客户端需要使用到Hadoop库函数,函数库封装了大部分与NameNode和DataNode通信相关的细节,同时也考虑了分布式文件系统在诸多场景的错误处理机制。
假设在HDFS中存储了一个文件/user/test.txt,HDFS客户端要读取该文件,Hadoop客户端程序库是必不可少的。如图3-8所示,HDFS客户端首先要访问NameNode,并告诉它所要读取的文件,在这之前,HDFS 会对客户的身份信息进行验证。验证的方式有两种:一种是通过信任的客户端,由其指定用户名;第二种方式是通过诸如Kerberos等强制验证机制来完成。接下来还需要检查文件的所有者及其设定的访问权限。当文件确实存在,且该用户对其有访问权限,这时 NameNode 会告诉 HDFS 客户端这个文件的第一个数据块的标号以及保存有该数据块的DataNode列表。这个列表是DataNode与HDFS客户端间的距离进行了排序。有了数据块标号和 DataNode 的主机名,HDFS 客户端便可以直接访问最合适的 DataNode,读取所需要的数据块。这个过程会一直重复直到该文件的所有数据块读取完成或HDFS客户端主动关闭了文件流。特殊的情况,如果该HDFS客户端是集群中的DataNode时,该节点将从本地DataNode中读取数据。
HDFS的数据写操作相对复杂些,以HDFS客户端向HDFS创建一个新文件为例,如图3-9所示。
首先HDFS客户端通过HDFS相关API发送请求,打开一个要写入的文件,如果该用户有写入文件的权限,那么这一请求将被送达 NameNode,并建立该文件的元数据。但此时新建立的文件元数据并未和任何数据块相关联,这时HDFS客户端会收到“打开文件成功”的响应,接着就可以写入数据了。当客户端将数据写入流时,数据会被自动拆分成数据包,并将数据包保存在内存队列中。客户端有一个独立的线程,它从队列中读取数据包,并向NameNode请求一组DataNode列表,以便写入下一个数据块的多个副本。接着,HDFS客户端将直接连接到列表中的第一个DataNode,而该DataNode又连接到第二个DataNode,第二个又连接到第三个,如此就建立了数据块的复制管道。复制管道中的每一个DataNode都会确认所收到的数据包已经成功写入磁盘。HDFS客户端应用程序维护着一个列表,记录着哪些数据包尚未收到确认信息。每收到一个响应,客户端便知道数据已经成功地写入管道中的一个DataNode。当数据块被写入列表中的DataNode中时,HDFS客户端将重新向NameNode申请下一组DataNode。最终,客户端将剩余数据包写入全部磁盘,关闭数据管道并通知NameNode文件写操作已经完成。
如果写入的时候,复制管道中的某一个DataNode无法将数据写入磁盘(如DataNode死机)。发生这种错误时,管道会立即关闭,已发送的但尚未收到确认的数据包会被退回到队列中,以确保管道中错误节点的下游节点可以得到数据包。而剩下的健康的DataNode中,正在写入的数据块会被分配新的 blk_id。这样,当发生故障的数据节点恢复后,冗余的数据块就会因为不属于任何文件而被自动丢弃,由剩余DataNode节点组成的新复制管道会重新开放,写入操作得以继续,写操作将继续直至文件关闭。NameNode 如果发现文件的某个数据块正在通过复制管道进行复制,就会异步地创建一个新的复制块,这样,即便 HDFS 的多个 DataNode 发生错误, HDFS 客户端仍然可以从数据块的副本中恢复数据,前提是满足最少数目要求的数据副本(dfs.replication.min)已经被正确写入(dfs.replication.min配置默认为1)。
Hadoop用户都希望HDFS在读写数据时,数据不会有任何丢失或者损坏。但是在实际情况中,如果系统需要处理的数据量超过 HDFS 能够处理的极限,那么数据被损坏的概率还是很高的。
检测数据是否损坏的常用措施是,在数据第一次引入系统时计算校验和,并在数据通过一个不可靠的通道进行数据传输时再次计算校验和,如果发现两次校验和不一样,那么可以判定,数据已经损坏。校验和技术并不能修复数据,只能检测出数据是否已经损坏[5]。
HDFS 也是采用校验和技术判断数据是否损坏,HDFS 会对写入的所有数据计算校验和,并在读取数据的时验证校验和,它针对由core-site.xml文件的io.bytes.per.checksum配置项指定字节的数据计算校验和,默认为512字节。
DataNode 负责验证收到的数据的校验和,并且如果该校验和正确,则保存收到的数据。DataNode 在收到客户端的数据或复制其他 DataNode 的数据时执行这个操作。正在写数据的HDFS客户端将数据及其校验和发送到由一系列DataNode组成的复制管道,如图3-9所示,管道中最后一个DataNode负责验证校验和。如果DataNode检测到错误,HDFS客户端便会收到一个校验和异常,可以在应用程序中捕获该异常,并做相应的处理,例如重新尝试写入。
HDFS客户端从DataNode读取数据时,也会验证校验和,将它们与DataNode中存储的校验和进行比较。每个DataNode均保存有一个用于验证的校验和日志,所以它知道每个数据块的最后一次验证时间。客户端成功验证一个数据块后,会告诉这个 DataNode,DataNode 由此更新日志。
不只是客户端在读取数据和写入数据时会验证校验和,每个 DataNode 也会在一个后台线程运行一个DataBlockScanner,定期验证存储在这个DataNode上的所有数据块。
由于HDFS存储着每个数据块的副本,因此当一个数据块损坏时,HDFS可以通过复制完好的该数据块副本来修复损坏的数据块,进而得到一个新的、完好无损的副本。大致的步骤是, HDFS客户端在读取数据块时,如果检测到错误,则向NameNode报告已损坏的数据块以及尝试读取该数据块的DataNode,最后才抛出ChecksumException异常。NameNode将这个已损坏的数据块标记为已损坏。之后,它安排这个数据块的一个副本复制到另一个DataNode,如此一来,数据块的副本数又回到了配置的水平。最后,已损坏的数据块副本便会被删除。
HDFS提供给HDFS客户端访问的方式多种多样,用户可以根据不同的情况选择不同的方式。
Hadoop自带一组命令行工具,而其中有关HDFS的命令是其工具集的一个子集。命令行工具虽然是最基础的文件操作方式,但却是最常用的。作为一名合格的 Hadoop 开发人员和运维人员,熟练掌握是非常有必要的。
执行hadoop dfs命令可以显示基本的使用信息。
[hadoop@master bin]$ hadoop dfs
Usage: java FsShell
[-ls <path>]
[-lsr <path>]
[-df [<path>]]
[-du <path>]
[-dus <path>]
[-count[-q] <path>]
[-mv <src> <dst>]
[-cp <src> <dst>]
[-rm [-skipTrash] <path>]
[-rmr [-skipTrash] <path>]
[-expunge]
[-put <localsrc> ... <dst>]
[-copyFromLocal <localsrc> ... <dst>]
[-moveFromLocal <localsrc> ... <dst>]
[-get [-ignoreCrc] [-crc] <src> <localdst>]
[-getmerge <src> <localdst> [addnl]]
[-cat <src>]
[-text <src>]
[-copyToLocal [-ignoreCrc] [-crc] <src> <localdst>]
[-moveToLocal [-crc] <src> <localdst>]
[-mkdir <path>]
[-setrep [-R] [-w] <rep> <path/file>]
[-touchz <path>]
[-test -[ezd] <path>]
[-stat [format] <path>]
[-tail [-f] <file>]
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
[-chown [-R] [OWNER][:[GROUP]] PATH...]
[-chgrp [-R] GROUP PATH...]
[-help [cmd]]
表3-2列出了hadoop命令行接口,并用例子说明各自的功能。
本地访问HDFS最主要的方式是HDFS提供的Java应用程序接口,其他的访问方式都建立在这些应用程序接口之上。为了访问HDFS,HDFS客户端必须拥有一份HDFS的配置文件,也就是hdfs-site.xml文件,以获取NameNode的相关信息,每个应用程序也必须能访问Hadoop程序库JAR文件,也就是在$HADOOP_HOME、$HADOOP_HOME/lib下面的jar文件。
Hadoop 是由 Java 编写的,所以通过 Java API 可以调用所有的 HDFS 的交互操作接口,最常用的是 FileSystem 类,它也是命令行 hadoop fs 的实现,其他接口在这一节也会有介绍。
1.java.net.URL
先来看一个例子,如代码清单3-1所示。
代码清单3-1 java.net.URL 示例
package com.hdfsclient;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.hadoop.fs.FsUrlStreamHandlerFactory;
import org.apache.hadoop.io.IOUtils;
public class MyCat {
static{
URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
}
public static void main(String[] args) throws MalformedURLException, IOException{
InputStream in = null;
try {
in = new URL(args[0]).openStream();
IOUtils.copyBytes(in, System.out, 4096,false);
} finally{
IOUtils.closeStream(in);
}
}
}
编译代码清单3-1所示的代码,导出为xx.jar文件,执行命令:
hadoop jar xx.jar hdfs://master:9000/user/hadoop/test
执行完成后,屏幕上输出HDFS的文件/user/hadoop/test的文件内容。
该程序是从HDFS读取文件的最简单的方式,即用java.net.URL对象打开数据流。代码中,静态代码块的作用是让Java 程序识别Hadoop 的HDFS url。
2.org.apache.hadoop.fs.FileSystem
虽然上面的方式是最简单的方式,但是在实际开发中,访问 HDFS 最常用的类还是FileSystem类。
(1)读取文件
读取文件的示例如代码清单3-2所示。
代码清单3-2 读取文件示例
package com.hdfsclient;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
public class FileSystemCat {
public static void main(String[] args) throws IOException {
String uri = "hdfs://master:9000/user/hadoop/test";
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(uri), conf);
InputStream in = null;
try {
in = fs.open(new Path(uri));
IOUtils.copyBytes(in, System.out, 4096,false);
} finally{
IOUtils.closeStream(in);
}
}
}
编译代码3-2,导出为xx.jar文件,上传至集群任意一节点,执行命令:
hadoop jar xx.jar com.hdfsclient.FileSystemCat
执行完成后控制台会输出文件内容。
FileSystem类的实例获取是通过工厂方法:
public static FileSystem get(URI uri,Configuration conf) throws IOException
其中 Configuration 对象封装了 HDFS 客户端或者 HDFS 集群的配置,该方法通过给定的URI方案和权限来确定要使用的文件系统。得到FileSystem实例之后,调用open()函数获得文件的输入流:
Public FSDataInputStream open(Path f) throws IOException
方法返回Hadoop独有的FSDataInputStream对象。
(2)写入文件
写入文件的示例如代码清单3-3所示。
代码清单3-3 写入文件示例
package com.hdfsclient;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
public class FileCopyFromLocal {
public static void main(String[] args) throws IOException {
//本地文件路径
String source = "/home/hadoop/test";
String destination = "hdfs://master:9000/user/hadoop/test2";
InputStream in = new BufferedInputStream(new FileInputStream(source));
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(destination),conf);
OutputStream out = fs.create(new Path(destination));
IOUtils.copyBytes(in, out, 4096,true);
}
}
编译代码清单3-3所示的代码,导出为xx.jar文件,上传至集群任意一节点,执行命令:
hadoop jar xx.jar com.hdfsclient.FileCopyFromLocal
FileSystem实例的create()方法返回FSDataOutputStream对象,与FSDataInputStream类不同的是,FSDataOutputStream不允许在文件中定位,这是因为HDFS只允许一个已打开的文件顺序写入,或在现有文件的末尾追加数据。
(3)创建HDFS的目录
创建HDFS目录的示例如代码清单3-4所示。
代码清单3-4 创建HDFS目录示例
package com.hdfsclient;
import java.io.IOException;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class CreateDir {
public static void main(String[] args){
String uri = "hdfs://master:9000/user/test";
Configuration conf = new Configuration();
try {
FileSystem fs = FileSystem.get(URI.create(uri), conf);
Path dfs=new Path("hdfs://master:9000/user/test");
fs.mkdirs(dfs);
} catch (IOException e) {
e.printStackTrace();
}
}
}
编译代码清单3-4所示的代码,导出为xx.jar文件,上传至集群任意一节点,执行命令:
hadoop jar xx.jar com.hdfsclient.CreateDir
运行完成后可以用命令hadoop dfs -ls 验证目录是否创建成功。
(4)删除HDFS上的文件或目录
删除HDFS上的文件或目录的示例如代码清单3-5所示。
代码清单3-5 删除HDFS上的文件或目录示例
package com.hdfsclient;
import java.io.IOException;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class DeleteFile {
public static void main(String[] args){
String uri = "hdfs://master:9000/user/hadoop/test";
Configuration conf = new Configuration();
try {
FileSystem fs = FileSystem.get(URI.create(uri), conf);
Path delef=new Path("hdfs://master:9000/user/hadoop");
boolean isDeleted=fs.delete(delef,false);
//是否递归删除文件夹及文件夹下的文件
//boolean isDeleted=fs.delete(delef,true);
System.out.println(isDeleted);
} catch (IOException e) {
e.printStackTrace();
}
}
}
编译代码清单3-5所示的代码,导出为xx.jar文件,上传至集群任意一节点,执行命令:
hadoop jar xx.jar com.hdfsclient.DeleteFile
如果需要递归删除文件夹,则需将fs.delete(arg0, arg1)方法的第二个参数设为true。
(5)查看文件是否存在
查看文件的示例如代码清单3-6所示。
代码清单3-6 查看文件示例
package com.hdfsclient;
import java.io.IOException;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class CheckFileIsExist {
public static void main(String[] args){
String uri = "hdfs://master:9000/user/hadoop/test";
Configuration conf = new Configuration();
try {
FileSystem fs = FileSystem.get(URI.create(uri), conf);
Path path=new Path(url);
boolean isExists=fs.exists(path);
System.out.println(isExists);
} catch (IOException e) {
e.printStackTrace();
}
}
}
编译代码清单3-6所示的代码,导出为xx.jar文件,上传至集群任意一台节点,执行命令:
hadoop jar xx.jar com.hdfsclient.CheckFileIsExist
(6)列出目录下的文件或目录名称
列出目录下的文件或目录名称的示例如代码清单3-7所示。
代码清单3-7 列出目录下的文件或目录名称示例
package com.hdfsclient;
import java.io.IOException;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class ListFiles {
public static void main(String[] args){
String uri = "hdfs://master:9000/user";
Configuration conf = new Configuration();
try {
FileSystem fs = FileSystem.get(URI.create(uri), conf);
Path path =new Path(uri);
FileStatus stats[]=fs.listStatus(path);
for(int i = 0; i < stats.length; ++i){
System.out.println(stats[i].getPath().toString());
}
fs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
编译代码清单3-7所示的代码,导出为xx.jar文件,上传至集群任意一节点,执行命令:
hadoop jar xx.jar com.hdfsclient.ListFiles
运行后,控制台会打印出/user目录下的目录名称或文件名。
(7)文件存储的位置信息
文件存储的位置信息的示例如代码清单3-8所示。
代码清单3-8 文件存储的位置信息示例
package com.hdfsclient;
import java.io.IOException;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.BlockLocation;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class LocationFile {
public static void main(String[] args){
String uri = "hdfs://master:9000/user/test/test";
Configuration conf = new Configuration();
try {
FileSystem fs = FileSystem.get(URI.create(uri), conf);
Path fpath=new Path(uri);
FileStatus filestatus = fs.getFileStatus(fpath);
BlockLocation[] blkLocations = fs.getFileBlockLocations(filestatus, 0,
filestatus.getLen());
int blockLen = blkLocations.length;
for(int i=0;i<blockLen;i++){
String[] hosts = blkLocations[i].getHosts();
System.out.println("block_"+i+"_location:"+hosts[0]);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
编译代码清单3-8所示的代码,导出为xx.jar文件,上传至集群任意一节点,执行命令:
hadoop jar xx.jar com.hdfsclient.LocationFile
前面提到过,HDFS的存储由DataNode的块完成,执行成功后,控制台会输出:
block_0_location:slave1
block_1_location:slave2
block_2_location:slave3
表示该文件被分为3个数据块存储,存储的位置分别为slave1、slave2、slave3。
3.SequenceFile
SequeceFile 是HDFS API 提供的一种二进制文件支持,这种二进制文件直接将<key, value>对序列化到文件中,所以SequenceFile 是不能直接查看的,可以通过hadoop dfs -text 命令查看,后面跟要查看的SequenceFile的HDFS路径。
(1)写入SequenceFile
写入SequenceFile示例如代码清单3-9所示。
代码清单3-9 写入SequenceFile示例
package com.hdfsclient;
import java.io.IOException;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Text;
public class SequenceFileWriter {
private static final String[] text = {
"两行黄鹂鸣翠柳",
"一行白鹭上青天",
"窗含西岭千秋雪",
"门泊东吴万里船",
};
public static void main(String[] args) {
String uri = "hdfs://master:9000/user/hadoop/testseq";
Configuration conf = new Configuration();
SequenceFile.Writer writer = null;
try {
FileSystem fs = FileSystem.get(URI.create(uri), conf);
Path path =new Path(uri);
IntWritable key = new IntWritable();
Text value = new Text();
writer = SequenceFile.createWriter(fs, conf, path, key.getClass(),
value.getClass());
for (int i = 0;i<100;i++){
key.set(100-i);
value.set(text[i%text.length]);
writer.append(key, value);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeStream(writer);
}
}
}
可以看到 SequenceFile.Writer 的构造方法需要制定键值对的类型。如果是日志文件,那么时间戳作为key,日志内容是value是非常合适的。
编译代码清单3-9所示的代码,导出为xx.jar文件,上传至集群任意一节点,执行命令:
hadoop jar xx.jar com.hdfsclient.SequenceFileWriter
运行完成后,执行命令:
hadoop dfs -text /user/hadoop/testseq
可以看到如下内容:
100 两行黄鹂鸣翠柳
99 一行白鹭上青天
98 窗含西岭千秋雪
97 门泊东吴万里船
……
2 窗含西岭千秋雪
1 门泊东吴万里船
(2)读取SequenceFile
读取SequenceFile示例如代码清单3-10所示。
代码清单3-10 读取SequenceFile示例
package com.hdfsclient;
import java.io.IOException;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.util.ReflectionUtils;
public class SequenceFileReader {
public static void main(String[] args) {
String uri = "hdfs://master:9000/user/hadoop/testseq";
Configuration conf = new Configuration();
SequenceFile.Reader reader = null;
try {
FileSystem fs = FileSystem.get(URI.create(uri), conf);
Path path = new Path(uri);
reader = new SequenceFile.Reader(fs, path, conf);
Writable key = (Writable)
ReflectionUtils.newInstance(reader.getKeyClass(), conf);
Writable value = (Writable)
ReflectionUtils.newInstance(reader.getValueClass(), conf);
long position = reader.getPosition();
while(reader.next(key,value)){
System.out.printf("[%s]\t%s\n",key,value);
position = reader.getPosition();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeStream(reader);
}
}
}
编译代码清单3-10所示的代码,导出为xx.jar文件,上传至集群任意一节点,执行命令:
hadoop jar xx.jar com.hdfsclient.SequenceFileReader
运行完成后,控制台会输出:
[100] 两行黄鹂鸣翠柳
[99] 一行白鹭上青天
[98] 窗含西岭千秋雪
[97] 门泊东吴万里船
……
[2] 窗含西岭千秋雪
[1] 门泊东吴万里船
1.Thrift
HDFS 的底层的接口是通过Java API 提供的,所以非Java 程序访问HDFS 比较麻烦。弥补方法是通过thriftfs 功能模块中的Thrift API 将HDFS 封装成一个Apache Thrift 服务。这样任何与Thrift 绑定的语言都能与HDFS 进行交互。为了使用Thrift API,需要运行提供Thrift 服务的服务器,并以代理的方式访问Hadoop。目前支持远程调用Thrift API 的语言有C++、Perl、PHP、Python和Ruby。
2.FUSE
用户空间文件系统(Filesystem in Universe,FUSE)允许把按照用户空间实现的文件系统整合成一个 Unix文件系统。通过使用 Hadoop的 Fuse-DFS功能模块,任意一个 Hadoop文件系统(如 HDFS)均可作为一个标准文件系统进行挂载。随后便可以使用普通 Unix 命令,如ls、cat等与该文件系统交互,还可以通过任意一种编程语言调用POSIX库来访问文件系统。
其余可以访问HDFS的还有WebD AV、HTTP、ftp接口,不过并不常用。
我们还可以通过NameNode的50070 端口号访问HDFS 的Web UI,HDFS 的Web UI 包含了非常丰富并且实用的信息,如图3-10 所示。通过HDFS 的Web UI 了解集群的状况是一名合格Hadoop开发和运维人员必备的条件。
我们可以直接在浏览器中输入master:9000(即NameNode的主机名:端口号)便可进入Web UI。如图 3-10,点击“Browse the filesystem”可以查看整个HDFS 的目录树,点击“Namenode Logs”可以查看所有的NameNode的日志,这对于排查错误十分有帮助。下面的表格展示了整个 HDFS 大致的信息,如总容量、使用量、剩余量等,其中点击“Live Nodes”选项,可以看到所有DataNode节点的状况,如图3-11所示。
图书在版编目(CIP)数据
Hadoop海量数据处理:技术详解与项目实战/范东来著.--北京:人民邮电出版社,2015.3
ISBN 978-7-115-38099-9
Ⅰ.①H… Ⅱ.①范… Ⅲ.①数据处理软件 Ⅳ.①TP274
中国版本图书馆CIP数据核字(2015)第024369号
内容提要
本书介绍了Hadoop技术的相关知识,并将理论知识与实际项目相结合。全书共分为三个部分:基础篇、应用篇和总结篇。基础篇详细介绍了Hadoop、MapReduce、HDFS、Hive和Sqoop,并深入探讨了 Hadoop 的运维和调优;应用篇则包含了一个具有代表性的完整的基于 Hadoop 的商业智能系统的设计和实现;结束篇对全书进行总结,并对技术发展做了展望。
本书结构针对学习曲线进行了优化,由浅至深,从理论基础到项目实战,适合Hadoop的初学者阅读,也适合作为高等院校相关课程的教学参考书。
◆著 范东来
责任编辑 杨海玲
责任印制 张佳莹 焦志炜
◆人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
网址 http://www.ptpress.com.cn
北京中新伟业印刷有限公司印刷
◆开本:800×1000 1/16
印张:20.75
字数:474千字 2015年3月第1版
印数:1-3000册 2015年3月北京第1次印刷
定价:59.00元
读者服务热线:(010)81055410 印装质量热线:(010)81055316
反盗版热线:(010)81055315