Hadoop学习总结

来源:互联网 发布:冰点软件干嘛的 编辑:程序博客网 时间:2024/06/06 00:56

1.      Apache Hadoop成员组成

Apache Hadoop是一个用java语言实现的软件框架,在由大量计算机组成的集群中运行海量数据的分布式计算,它可以让应用程序支持上千个节点和PB级别的数据。

阿里巴巴集团、雅虎北京全球软件研发中心、中国移动研究院、英特尔研究院、金山软件、百度、腾讯、新浪、搜狐、IBMFacebookAmazonYahoo!、AdobeHuluMicrosoft等公司或者研究院都在使用Hadoop


HadoopJava编写的软件框架,以支持数据密集型分布式应用,其中HDFSMapReduce构成Hadoop core

MapReduce:针对大数据的灵活的并行数据处理框架。MapReduce作为Hadoop的核心是一种处理大型及超大型数据集(TB级别的数据:包括网络点击产生的流数据、日志文件、社交网络等所带来的数据)并生成相关的执行的编程模型。其主要思想是从函数式编程语言借鉴而来的,同时也包含了从矢量编程语言借鉴的特性。

HDFSHadoop分布式文件系统。

ZooKeeper:一个高效的、可扩展的、可靠的分布式协调系统,分布式应用可以使用Zookeeper来存储和协调关键共享状态。

HBase:建立在Hadoop内核之上,提供可靠的,可扩展性的分布式Key-value数据库。

Hive:构建在MapRudece之上的数据仓库软件包,提供数据汇总和特定查询。

Pig:建立在Hadoop内核之上,是一种支持并行计算运行框架的高级数据流语言。Pig Latin语言为编程人员提供了更直观的定制数据流的方法。

SqoopFlume:可改进数据的互操作性和其余部分。Sqoop功能主要是从关系数据库导入数据到Hadoop,并可直接导入到HFDSHive。而Flume设计旨在直接将流数据或日志数据导入HDFS

MahoutMahout提供一些可扩展的机器学习领域经典算法的实现,旨在帮助开发人员更加方便快捷地创建智能应用程序。Mahout包含许多实现,包括集群、分类、推荐过滤、频繁子项挖掘。

AmbariAmbari是最新加入Hadoop的项目, Ambari可帮助系统管理员部署和配置Hadoop,升级集群以及监控服务。还可通过API集成与其他的系统管理工具。

Oozie:负责MapReduce作业调度。

Avro:一个数据序列化的项目。

Cassandra:一个可扩展的多master的,避免单节点失效的数据库。


2.      Mapreduce简介

一个软件架构,是一种处理海量数据的并行编程模式;用于大规模数据集(通常大于1TB)的并行运算。MapReduce实现了MapReduce两个功能:(1)Map把一个函数应用于集合中的所有成员,然后返回一个基于这个处理的结果集;(2)Reduce对结果集进行分类和归纳;Map() Reduce() 两个函数可能会并行运行,即使不是在同一的系统的同一时刻


MapReduce案例分析1:单词计数问题(WordCount)

public static class TokenizerMapper extendsMapper<Object, Text,Text, IntWritable>{//Map   

    private finalstatic IntWritable one = new IntWritable(1);

    private Textword = new Text();

    public voidmap(Object key, Text value, Context context ) throws IOException,InterruptedException {

        StringTokenizer itr = newStringTokenizer(value.toString());

        while (itr.hasMoreTokens()) {

          word.set(itr.nextToken());           

          context.write(word, one); //设置 <key, value>  Map的输出

        }

    }

 }

说明: Map的输出keyvalueReduce的输入keyvalue要一致,见上面红色部分

public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable>{//Reduce

    privateIntWritable result = new IntWritable();

    public void reduce(Text key,Iterable<IntWritable> values, Context context) throws IOException,InterruptedException {

      int sum = 0;

      for(IntWritable val : values) {

        sum +=val.get(); //聚集操作

      }

     result.set(sum);

      context.write(key, result);//设置<key, value> Reduce的输出

    }

  }

说明: Map的输出keyvalueReduce的输入keyvalue要一致,见上面红色部分

public static void main(String[] args) throws Exception {//Job配置

    Configurationconf = new Configuration();

    String[]otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();

    if(otherArgs.length != 2) {

     System.err.println("Usage: wordcount <in> <out>");

     System.exit(2);

    }

    Job job = newJob(conf, "word count");    //job name

   job.setJarByClass(WordCount.class);

   job.setMapperClass(TokenizerMapper.class);

   job.setCombinerClass(IntSumReducer.class);

   job.setReducerClass(IntSumReducer.class);

   job.setOutputKeyClass(Text.class);

   job.setOutputValueClass(IntWritable.class);

   FileInputFormat.addInputPath(job, new Path(otherArgs[0]));    //file input

   FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));  //file output

   System.exit(job.waitForCompletion(true) ? 0 : 1);

}




MapReduce程序分析(1)每个split对应一个map线程;

(2)Map的输出到Reduce的输入过程(shuffle过程):第一阶段:在map端完成(内存→排序→写入磁盘→复制→分区→排序→分区合并→合并后分区→复制);第二阶段:在reduce端完成(映射到reduce端分区→合并→排序)

MapReduce隔离的进程(按照key值通信)

Hadoop限制进程的通信,因为每个任务所处理的单个记录与其它记录无关MapReduce中,Mapper任务是将记录分开,Mapper的输出会作为Reducer任务的输入,Reducer再将不同的Mapper结果合并。

在一个Hadoop集群中,不同的结点仍会相互通信,但是与其它传统的分布式系统通信有所不同,传统的作法是应用设计者要显式地通过socketMPI(Message PassingInterface)缓冲将字节流从一个结点传到另一个结点,但在Hadoop上的通信是隐式的,数据片断都有着key值,通过它Hadoop就知道如何将相关信息位发送到一个目标结点。Hadoop在内部管理数据传输和集群拓扑。

通过限制结点之间的通信,Hadoop使分布式系统更可靠,单个结点崩溃可以通过其它结点重新开始任务的方式正常工作。因为用户级任务不需要结点之间显式地通信,所以不需要用户程序交换信息,同样结点不需要回滚到预定义的检测点去部分地重新开始计算,在Hadoop中其它正常结点继续运行,好似没有错误发生,并将部分重新开始程序这个挑战的方面留到Hadoop层处理。

MapReduce案例分析2:解决“数组中最大数字”问题

问题:求出以下数组当中最大的数{1323341828101675}


MapReduce优点:(1)模型非常方便使用,即使是对于完全没有分布式程序的程序员也是如此;(2)对于大型的计算需求使用MapReduce可以非常轻松的完成;(3)通过MapReduce,应用程序可以在超过1000个节点的大型集群上运行,并且提供经过优化的错误容灾。

MapReduce缺点:(1)将输入数据分隔成固定大小的片段,再由MapReduce平台处理,缺点在于处理延迟与数据片段的长度、初始化处理任务的开销成正比;(2)为了支持流式处理,MapReduce需要被改造成Pipeline的模式,而不是Reduce直接输出;考虑到效率,中间结果最好只保存在内存中。这些改动使得原有的MapReduce框架的复杂度大大增加,不利于系统的维护和扩展;(3)用户被迫使用MapReduce的接口来定义流式作业,这使得用户程序的可伸缩性降低

3.      MapReduce基础

(a)函数式编程概念

MapReduce程序是用来并行计算大规模海量数据的,它需要把工作流划分到大量的机器上去,如果组件(component)之间可以任意的共享数据,那这个模型就没法扩展到大规模集群上去了(数百或数千个节点),因为用来保持节点间数据的同步而产生的通信开销会使得系统在大规模集群上变得不可靠和效率低下。实际上,所有在MapReduce上的数据元素都是不可变的,这就意味着它们不能够被更新。如果在一个Map任务中你改变了一个输入键值对,它并不会反馈到输入文件;节点间的通信只在产生新的输出键值对((key,value)pairs)时发生,Hadoop系统会把这些输出传到下一个执行阶段。

(b) Mapping数据列表(Lists

MapReduce程序的第一步叫做mapping,在这一步会有一些数据元素作为Mapper函数的输入数据,每次一个,Mapper会把每次map得到的结果单独的传到一个输出数据元素里。


Mapping通过对输入数据列表中的每一个元素应用一个函数创建了一个新的输出数据列表。

(c) Reducing数据列表(Lists

Reducing可以让你把数据聚集在一起,reducer函数接收来自输入列表的迭代器,它会把这些数据聚合在一起,然后返回一个输出值。


通过列表迭代器对输入数据进行reducing操作来输出聚合结果,Reducing一般用来生成总结数据,把大规模的数据转变成更小的总结数据。

(d)根据键划分reduce空间


不同颜色代表不同的键,有着相同键的数值都被传到同一个reduce任务里。有着相同键的所有数值会被一起送到一个reducer里。作用在有着不同键关联的数值列表上的reduce操作之间是独立执行的。


4.      细节化的Hadoop MapReduce数据流


(a)   输入文件:文件是MapReduce任务的数据的初始存储地。正常情况下,输入文件一般是存在HDFS里。这些文件的格式可以是任意的;我们可以使用基于行的日志文件,也可以使用二进制格式,多行输入记录或其它一些格式,这些文件会很大数十G或更大

(b)   输入格式:InputFormat类定义了如何分割和读取输入文件,它提供有下面的几个功能:(1)选择作为输入的文件或对象;(2)定义把文件划分到任务的InputSplits;(3)RecordReader读取文件提供了一个工厂方法。Hadoop自带了好几个输入格式,其中有一个抽象类叫FileInputFormat,所有操作文件的InputFormat类都是从它那继承功能和属性。当开启Hadoop作业时,FileInputFormat会得到一个路径参数,这个路径内包含了所需要处理的文件,FileInputFormat会读取这个文件夹内的所有文件(默认不包括子文件夹内的),然后它会把这些文件拆分成一个或多个的InputSplit。你可以通过JobConf对象的setInputFormat()方法来设定应用到你的作业输入文件上的输入格式。

默认的输入格式是TextInputFormat,它把输入文件每一行作为单独的一个记录,但不做解析处理。这对那些没有被格式化的数据或是基于行的记录来说是很有用的,比如日志文件。更有趣的一个输入格式是KeyValueInputFormat,这个格式也是把输入文件每一行作为单独的一个记录。然而不同的是TextInputFormat把整个文件行当做值数据,KeyValueInputFormat则是通过搜寻tab字符来把行拆分为键值对。这在把一个MapReduce的作业输出作为下一个作业的输入时显得特别有用,因为默认输出格式正是按KeyValueInputFormat格式输出数据。最后说说SequenceFileInputFormat,它会读取特殊的特定于Hadoop的二进制文件,这些文件包含了很多能让Hadoopmapper快速读取数据的特性。Sequence文件是块压缩的并提供了对几种数据类型(不仅仅是文本类型)直接的序列化与反序列化操作。Squence文件可以作为MapReduce任务的输出数据,并且用它做一个MapReduce作业到另一个作业的中间数据是很高效的。下表给出了一些标准的输入格式:

输入格式

描述

TextInputFormat

默认格式,读取文件的行

行的字节偏移量

行的内容

KeyValueInputFormat

把行解析为键值对

第一个tab字符前的所有字符

行剩下的内容

SequenceFileInputFormat

Hadoop定义的高性能二进制格式

用户自定义

用户自定义

(c)   输入块(InputSplit:一个输入块描述了构成MapReduce程序中单个map任务的一个单元。把一个MapReduce程序应用到一个数据集上,即是指一个作业,会由几个(也可能几百个)任务组成。Map任务可能会读取整个文件,但一般是读取文件的一部分。默认情况下,FileInputFormat及其子类会以64MB(与HDFSBlock默认大小相同)为基数来拆分文件。你可以在hadoop-site.xml文件内设定mapred.min.split.size参数来控制具体划分大小,或者在具体MapReduce作业的JobConf对象中重写这个参数。通过以块形式处理文件,我们可以让多个map任务并行的操作一个文件。如果文件非常大的话,这个特性可以通过并行处理大幅的提升性能。更重要的是,因为多个块(Block)组成的文件可能会分散在集群内的好几个节点上,这样就可以把任务调度在不同的节点上;因此所有的单个块都是本地处理的,而不是把数据从一个节点传输到另外一个节点

(d)   记录读取器(RecordReader):InputSplit定义了如何切分工作,但是没有描述如何去访问它。RecordReader类则是实际上用来加载数据并把数据转换为适合mapper读取的键值对。RecordReader实例是由输入格式定义的,默认的输入格式TextInputFormat提供了一个LineRecordReader,这个类会把输入文件的每一行作为一个新的值,关联到每一行的键则是该行在文件中的字节偏移量RecordReader会在输入块上重复的调用直到整个输入块被处理完毕,每一次调用RecordReader都会调用Mappermap()方法。

(e)   Mapper:给定一个键值对,map()方法会生成一个或多个键值对,这些键值对会被送到Reducer那。对于整个作业输入部分的每一个map任务(输入块),每一个新的Mapper实例都会在单独的Java进程中被初始化,mapper之间不能进行通信。这就使得每一个map任务的可靠性不受其它map任务的影响,只由本地机器的可靠性来决定。map()方法除了键值对外还会接收额外的两个参数(译注:0.20.×后的版本,接口已变化,由Context对象代替这两个参数

(f)    Partition & Shuffle:当第一个map任务完成后,节点可能还要继续执行更多的map任务,但这时候也开始把map任务的中间输出交换到需要它们的reducer那里去,这个移动map输出到reducer的过程叫做shuffle。每一个reduce节点会分派到中间输出的键集合中的一个不同的子集合,这些子集合(被称为“partitions”)是reduce任务的输入数据。每一个map任务生成的键值对可能会隶属于任意的partition,有着相同键的数值总是在一起被reduce,不管它是来自那个mapper的。因此,所有的map节点必须就把不同的中间数据发往何处达成一致。Partitioner类就是用来决定给定键值对的去向,默认的分类器(partitioner)会计算键的哈希值并基于这个结果来把键赋到相应的partition(hash(key) mod R)

(g)   排序:每一个reduce任务负责归约(reduceing)关联到相同键上的所有数值,每一个节点收到的中间键集合在被送到具体的reducer那里前就已经自动被Hadoop排序过了。

(h)   归约(Reduce):每个reduce任务都会创建一个Reducer实例,这是一个用户自定义代码的实例,负责执行特定作业的第二个重要的阶段。对于每一个已赋予到reducerpartition内的键来说,reducerreduce()方法只会调用一次,它会接收一个键和关联到键的所有值的一个迭代器,迭代器会以一个未定义的顺序返回关联到同一个键的值。reducer也要接收一个OutputCollectorReport对象(或者在0.20.×后版本中的Context对象),它们像在map()方法中那样被使用。

(i)    输出格式:提供给OutputCollector的键值对会被写到输出文件中,写入的方式由输出格式控制。OutputFormat的功能跟前面描述的InputFormat类很像,Hadoop提供的OutputFormat的实例会把文件写在本地磁盘或HDFS上,它们都是继承自公共的FileInputFormat类。每一个reducer会把结果输出写在公共文件夹中一个单独的文件内,这些文件的命名一般是part-nnnnnnnnnn是关联到某个reduce任务的partitionid,输出文件夹通过FileOutputFormat.setOutputPath()来设置。你可以通过具体MapReduce作业的JobConf对象的setOutputFormat()方法来设置具体用到的输出格式。下表给出了已提供的输出格式:

输出格式

描述

TextOutputFormat

默认的输出格式, "key \t value"的方式输出行

SequenceFileOutputFormat

输出二进制文件,适合于读取为子MapReduce作业的输入

NullOutputFormat

忽略收到的数据,即不做输出

Hadoop提供了一些OutputFormat实例用于写入文件,默认的是TextOutputFormat,它会以一行一个键值对的方式把数据写入一个文本文件里。这样后面的MapReduce任务就可以通过KeyValueInputFormat类简单的重新读取所需的输入数据了,而且也适合于人的阅读。还有一个更适合于在MapReduce作业间使用的中间格式,那就是SequenceFileOutputFormat,它可以快速的序列化任意的数据类型到文件中,而SequenceFileInputFormat则会把文件反序列化为相同的类型并提交为下一个Mapper的输入数据,方式和前一个Reducer的生成方式一样。NullOutputFormat不会生成输出文件并丢弃任何通过OutputCollector传递给它的键值对,如果你在要reduce()方法中显式的写你自己的输出文件并且不想Hadoop框架输出额外的空输出文件,那这个类是很有用的。

(j)    RecordWriter:这个和InputFormat中通过RecordReader读取单个记录的实现很相似,OutputFormat类是RecordWriter对象的工厂方法,用来把单个的记录写到文件中,就像是OuputFormat直接写入的一样。

5.  额外的MapReduce功能(Combiner, Counters, DistributedCache)


Combiner前面展示的流水线忽略了一个可以优化MapReduce作业所使用带宽的步骤,这个过程叫Combiner,它在Mapper之后Reducer之前运行。Combiner是可选的,如果这个过程适合于你的作业,Combiner实例会在每一个运行map任务的节点上运行Combiner会接收特定节点上的Mapper实例的输出作为输入,接着Combiner的输出会被发送到Reducer那里,而不是发送Mapper的输出。Combiner是一个迷你reduce”过程,它只处理单台机器生成的数据

Combiner实例:词频统计是一个可以展示Combiner的用处的基础例子,上面的词频统计程序为每一个它看到的词生成了一个(word1)键值对。所以如果在同一个文档内“cat”出现了3次,(”cat”1)键值对会被生成3次,这些键值对会被送到Reducer那里(占用带宽)。通过使用Combiner,这些键值对可以被压缩为一个送往Reducer的键值对(”cat”3)。现在每一个节点针对每一个词只会发送一个值到reducer大大减少了shuffle过程所需要的带宽并加速了作业的执行

Combiner应是Reducer接口的实例,如果你的Reducer由于不可交换或不可组合不能作为Combiner,你仍可以写一个第三方类来作为你的作业的Combiner

CounterCounters是多个由Map/Reduce框架或者应用程序定义的全局计数器,每一个Counter可以使任何一种Enum类型,同一特定Enum类型的Counter可以汇集到一个组,其类型为Counters.Group。应用程序可以定义任意的Counters并且可以通过map或者reduce方法更新,之后框架会汇总全局counters信息。

DistributedCache:可将具体应用相关的、大尺寸的、只读的文件有效地分布放置。DistributedCacheMap/Reduce框架提供的功能,能够缓存应用程序所需的文件,包括文本、档案文件、jar文件等。

1.      MapReduceJob介绍

Hadoop中所有MapReduce程序以Job形式提交给集群运行。一个MapReduce Job被划分为若干个Map TaskReduce Task并行执行。一个Job的提交包括数据和程序(Jar文件)的提交。



7.      分布式文件系统HDFS简介

HDFS(Hadoop Distributed FileSystem)是一个设计用来保存大数据量的分布式文件系统(TB级甚至是PB),并提供快速访问数据的能力,数据通过冗余的方式保存在多台机器上,用来提供对失败的容错性和并行应用的高度可用性。(冗余→可靠性)

HDFS块结构的文件系统,单个文件被分成相同大小的块,这些块被分布到集群中的多台机器上,集群中的单个机器称为数据结点(DataNode)一个文件可由多个块组成,它们并不需要放在同一台机器上,保存每个块的机器是随机选择的,所以访问一个文件需要多台机器协作,但它所支持的文件大小会比单台机器的DFS大的多。如果使用多台机器保存一个文件,那么这些机器中任一一台崩溃都会导致文件不可访问,HDFS通过将每个块复制到多台机器(默认3)的方式来解决这个问题。(冗余机制)

NFSNetwork File System,是最常见的分布式文件系统它提供远程访问单台机器的单个逻辑卷的能力,一个NFS服务器将它本地的对外部客户端可见,客户端可以将这个远程的文件系统直接挂载到它们自己的Linux文件系统上,再以对本地磁盘相同的方式访问它。它最大的优点是它的透明性,客户端并不需要特别关注它们是在操作远程的数据文件,标准的库函数如openclosefread等等,都可通过NFS使用。但作为一个分布式文件系统,它的能力很有限,一个NFS的文件需要存放在一台机器上,这意味着它只能保存一台机器所能保存的数据量,并且不提供处理机器崩溃时的解决方案(比如通过复制文件到其它服务器上),如果大量客户端要操作数据,会使服务器压力很重,并且客户端必需总是将数据读到它们本地机器上才能操作。

HDFS的设计相对其它分布式文系统,比如NFS,具有以下不同特征

(1)HDFS设计用来保持非常大数据量的信息,这需要将数据分布到大量的机器上,它支持比NFS大得多的文件大小。

(2)HDFS的数据保存是可靠的,如果集群中的机器工作不正常,数据仍然是可用的。

(3)HDFS提供快速,可扩展的数据访问能力。

(4)通过简单地添加一些机器,就可以使集群能服务于更多的客户端是可能的。

(5)HDFSHadoop MapReduce能很好地集成,它在可能的情况下,能使数据读取,计算本地化。

相比于传统的分布式文件系统,HDFS做出的一些权衡:

(1)应用程序使用HDFS要使用流的方式读取文件,HDFS对流式读取文件进地了优化,但这时seek到文件的任一位置的操作代价就高了。

(2)数据是一次写入,多次读取;Hadoop不支持写操作和关闭文件后再更新文件(Hadoop 0.19支持追加文件)。

(3)由于文件很大,并且是流式读取,所以系统并不提供本地缓存机制,如果想要读取过的数据,只能再从HDFS文件中读取。

(4)Hadoop假设每个机器都极可能失败,或永远或暂时,集群必须能应付多台机器同时失败的情况,这时集群性能下降应与损失的机器成比例,系统作为一个整体不应变得很慢,也不应该有数据丢失,Hadoop用复制数据的策略来克服这一困难。

大多数基于块的文件系统的块大小为4KB8KB,而HDFS中默认使用块大小为64MB,比前者大出好几个数量级,这使得HDFS减少了对每个文件元数据的存储量。同时它通过将大量数据连续化存放在磁盘上,方便快速地以流式方式读取数据,这种设计的结果使得HDFS倾向于处理大文件。并且HDFS倾向于保存大量超大文件,每个几百M或几G,因为100MB的文件才不过能分成2个块。在你计算机上的文件也许经常被随机访问,即应用程序从一个文件中不同位置读取少量信息,而不是顺序访问,相反,HDFS希望程序从开始顺序读到结尾的方式读。

HDFS是在一些机器中以块的形式保存文件,但这些文件并不是普通文件系统的一部分,在运行Hadoop服务的数据结点上输入ls命令,可以显示普通文件系统的内容,但它不能显示HDFS中的文件,这是因为HDFS在一个不同的命名空间中运行,它与本地文件内容是隔离的HDFS中的文件(更准确性,组成这些文件的块)是保存在数据结点服务管理的一个特定目录下,这些文件没有名字,只有块id,你无法通过Linux文件命令与HDFS中的文件交互。

文件系统可靠保存它的元数据是很重要的,因为文件数据是以一次写入,多次读取的方式访问的,元数据结构(比如:文件名,目录名)会被大量客户端同时修改。所以元数据只由一台机器来处理,它被称为名字结点(NameNode),它保存着文件系统中所有元数据。因为每个文件都有较少的元数据(它只保存文件名,权限,和文件每块的位置),所有的元数据都保存在NameNode的内存中,可以进行快速访问元数据。(一个名字节点和多个数据节点)

要打开一个文件,客户端可从NameNode取得该文件每个块的位置,即是哪些结点保存着这个文件的块,客户端再直接从DataNode中读取每个块,这个过程可以并行读取,NameNode并不直接参与块数据传输,这可使它的压力减至最小。

当然,即使名字结点失败名字结点的信息也必须保存,所以有多个冗余系统可以使名字结构完全崩溃时,也可以保存元数据信息。名字结点崩溃比如数据结点崩溃更严重,当单个数据结点崩溃,整个集群也会正常运行,但名字结点崩溃后,集群将不可访问,除非名字结点手工恢复,幸运的是名字结点参与相比是非常少的,所以它崩溃的可能也远低于任一数据结点。

故障检测:(1)心跳包(检测是否宕机);(2)块报告(安全模式下检测);(3)数据完整性检测(校验和比较):采用CRC32作数据交验。在文件Block写入的时候除了写入数据还会写入交验信息,在读取的时候需要交验后再读入;(4)名字节点(日志文件,镜像文件)

HDFS缺点:低延时访问。HDFS不太适合于那些要求低延时(数十毫秒)访问的应用程序,因为HDFS是设计用于大吞吐量数据的,这是以一定延时为代价的;大量小文件。Namenode把文件系统的元数据放置在内存中,所以文件系统所能容纳的文件数目是由Namenode的内存大小来决定;不支持多用户写,任意文件修改。目前Hadoop只支持单用户写,不支持并发多用户写。

8.      HDFS基础

Hadoop的系统中,会有一台Master,主要负责NameNode的工作以及JobTracker的工作。JobTracker的主要职责就是启动、跟踪和调度各个Slave的任务执行。还会有多台Slave,每一台Slave通常具有DataNode的功能并负责TaskTracker的工作。TaskTracker根据应用要求来结合本地数据执行Map任务以及Reduce任务。


HDFS内部通信都是基于标准的TCP/IP协议NameNode依赖来自每个 DataNode的定期心跳(heartbeat)消息。每条消息都包含一个块报告,NameNode可以根据这个报告验证块映射和其他文件系统元数据。如果 DataNode不能发送心跳消息,NameNode将采取修复措施,重新复制在该节点上丢失的块。

名称节点( NameNode):管理文件系统的命名空间,记录文件系统树及这个树内所有的文件和索引目录,同时也记录每个文件的每个块,所在的数据节点。NameNode记录着每个文件的元数据。每个文件在那个块中,每个数据节点包含哪些块。(不储存原始文件)

数据节点( DataNode ):文件系统的工作者,存储并提供定位块的服务,并定时向名称节点发送块的存储列表。DataNode是文件的工作者,它们存储文件并提供定位块的服务,并且定时向名称节点发送它们的存储块的列表。(储存原始文件)


JobTracker:协调作业的运行;

TaskTracker:运行作业划分后的任务。

提交的MapReduce作业就是一个Job,一个Job对应多个Task,一个Task对应一个或者多个MapReduce线程。


读文件流程:客户端联系NameNode,得到所有数据块信息,以及数据块对应的所有数据服务器的位置信息;尝试从某个数据块对应的一组数据服务器中选出一个,进行连接(选取算法未加入相对位置的考虑);数据被一个包一个包发送回客户端,等到整个数据块的数据都被读取完了,就会断开此链接,尝试连接下一个数据块对应的数据服务器,整个流程,依次如此反复,直到所有想读的都读取完了为止


写文件流程:客户端把数据缓存到本地临时文件夹;临时文件夹数据超过64M,客户端联系NameNode NameNode分配DataNodeDataNode依照客户端的位置被排列成一个有着最近物理距离和最小的序列;与序列的第一个数据服务器建立Socket连接,发送请求头,然后等待回应,依次下传,客户端得到回包,流水线建立成功;正式发送数据,以4K为大小传送

HDFS数据分布:Hadoop集群中,数据被分布在集群中的各个结点,Hadoop Distributed File System (HDFS)将大的数据文件分成块,并将这些块交由集群中的结点处理,其中每个块都可能会复制到不同的若干个机器上,所以一台机器崩溃不会导致数据丢失,监测系统会对结点崩溃做出反应,重新复制数据,即部分存储(partial storage)。即便文件块被复制分布到不同的机器,但它们形成了一个单一的命名空间,所以它们的内容还是全局可访问的。

Hadoop的编程框架中,从概念上来说,数据是面向记录的。每个输入文件都被以行或其它特定的应用逻辑格式分开。集群中的每个结点的每个进程都会处理数据文件的一部分,Hadoop框架再根据分布式文件系统的信息调试进程到数据记录的位置,因为文件以块的形式存在于分布式文件系统中,每个结点上的每个计算进程处理数据的一个子集。哪些数据要被一个结点处理是由数据本身的存放位置决定的。大部分数据是直接从磁盘读入CPU,降低网络带宽的限制,也防止了不必要的网络传输。这种策略是将计算移动到数据,而不是将数据移动到计算。

本地计算:网络带宽为分布计算瓶颈,“本地计算”是最有效的一种节约网络带宽的手段,业界把这形容为“移动计算比移动数据更经济”。


9.      HDFS配置信息

HDFS配置文件是一组XML文件,它们位于Hadoop的配置目录conf下。conf/hadoop-defaults.xml文件中包含了Hadoop中的所有参数默认值,这个文件一般视为是只读的,可以在conf/hadoop-site.xml中设置新的值,以覆盖默认值,这个文件会被复制到集群中所有机器上。

fs.default.name,这是集群名字结点的URI(协议,主机名,端口)。Hadoop系统中的结点都需要知道名字结点的地址才能进行操作,数据结点要与这个名字结点一起注册,才使它们的数据通过名字结点变为可用的,客户端要通过个地址取得文件块的真正存放地址再去取数据。

dfs.data.dir,这是数据结点保存数据的本地文件系统路径,因为它们将运行在不同的机器上,并不需要所有的数据结点都将数据保存到相同前缀的本地路径,集群中的机器不同时也是可行的。但如果这个目录在整个系统都是统一的,那么配置也就简单些,Hadoop默认将dfs.data.dir设为/tmp。这在测试时是没关系的,但在生产环境中,这样做很容易丢失数据,所以必需覆盖。

dfs.name.dir,这是名字结点保存元数据的本地路径,它仅用于名字结点找到它自己的信息,它在数据结点上不存在,它的默认值也是/tmp,所以它也必须覆盖。

另一个上面没有提及的参数dfs.replication,它是文件系统中每个块的默认复制数,在生产环境集群中,一般就用它的默认值3,你可以随意增加复制数,但它会无谓地用去很多空间,如果把它设小,会影响数据的可用性和可靠性。(一个Block会有三份备份,一份放在NameNode指定的DataNode,另一份放在与指定DataNode非同一机架上的DataNode,最后一份放在与指定DataNode同一机架上的DataNode上。DataNode通常以机架形式组织,机架通过一个交换机将所有系统连接起来。通常,机架内部节点之间的传输速度快于机架间节点的传输速度)

(hadoop-site.xml文件配置信息,早期版本配置—0.20.×版本之前)

<configuration>

  <property>

     <name>fs.default.name</name>

     <value>hdfs://your.server.name.com:9000</value>

  </property>

  <property>

     <name>dfs.data.dir</name>

     <value>/home/username/hdfs/data</value>

  </property>

  <property>

     <name>dfs.name.dir</name>

     <value>/home/username/hdfs/name</value>

  </property>

</configuration>

其中your.server.name.comusername是需要修改的,而使用端口9000也是任意选择的。

0.20.×版本之后配置文件

conf/core-site.xml

conf/hdfs-site.xml

mapred-site.xml

<configuration>

<property>

<name>fs.default.name</name>

<value>hdfs://localhost:9000</value>

</property>

</configuration>

<configuration>

<property>

<name>dfs.replication</name>

<value>1</value>

</property>

</configuration>

<configuration>

<property>

<name>mapred.job.tracker</name>

<value>localhost:9001</value>

</property>

</configuration>


10.      HDFS常用命令(格式化、启动、停止)

所有与集群交互的命令是通过一个脚本bin/hadoop来进行的,它可以用Java虚拟机载入Hadoop系统,来执行用户命令。

格式化文件系统:user@namenode:hadoop$bin/hadoop namenode –format,上述操作只应进行一次,当它完成时,就可以启动分布式文件系统了。(0.20.×版本之后: $ bin/hadoop namenode –format)

user@namenode:hadoop$bin/start-dfs.sh,启动主结点的名字结点,同样也启动每个子结点(slave machine)上的数据结点实例。在单机“集群”中,数据结点与名字结点在同一台机器上,在两台或多台机器的真实集群上,这个脚本会ssh到每个子结点,启动数据结点实例。(0.20.×版本之后: $ bin/start-all.sh)

如果想停止集群的HDFS,执行命令:someone@namenode:hadoop$bin/stop-dfs.sh,这个命令必须由启动HDFS,即运行bin/start-dfs.sh的用户执行 (0.20.×版本之后:$bin/stop-all.sh)

检查是否启动成功,可访问以下url: NameNode(http://localhost:50070/)JobTracker(http://localhost:50030/)

将输入文件复制到分布式文件系统$ bin/hadoop fs -put local input

运行 $ bin/hadoop jar path/xx.jarWordCount input output

检查输出 $ bin/hadoop fs -cat output/*

11.      DFS & DFSAdmin模块介绍

dfs模块提供了常用的文件或目录操作,但它们都是对文件系统内部的操作,dfsadmin模块是对整个文件系统操作或查询

得到全局状态:一个简洁的HDFS状态状态报告可以用bin/hadoop dfsadmin –report得到,它返回的是HDFS集群的基本健康信息。

更详细的状态:如果你想知道名字结点元数据状态的更多信息,你可以使用bin/hadoop dfsadmin –metasave filename命令,它将信息保存到filename中,metasave命令枚举在复制的块列表。注意,这个命令的帮助信息是“保存名字结点的主要数据结构”,但它是错误 的,名字结点的状态不能通过个命令保存,它提供的是名字结点对数据块的处理信息。

安全模式:安全模式是HDFS的一种状态,在这种状态下,文件系统以只读方式被挂载,不进行冗余复制,文件不能被创建也不能被删除,这是名字结点启动后自动进入的状态,以便允许所有数据结点与名字结点时间同步,并声明它们所拥有的块,然后名字结点才决定哪些块被复制,等等。名字结点要等到一定比例的数据块可见,这个比例是由配置中的dfs.safemode.threshold参数来决定,在达到这个比例的阈值后,安全模式自动退出,然后HDFS允许正常操作。bin/hadoop dfsadmin–safemode what命令允许用户根据what的值操作安全模式,what的值在下面列出:

(1) enter进入安全模式;(2)leave 强制名字结点退出安全模式;(3)get 返回标名安全模式是ON还是OFF的字符串;(4)wait 等待直到名字结点退出安全模式后自动返回。

修改HDFS成员:当停止结点时,要将它们逐步地与HDFS断开连接,来保证数据不会丢失

升级HDFS版本:当将HDFS版本升级到另一版本时,名字结点和数据结点的格式有可能改变,当你要启动新的Hadoop时,你需要告诉Hadoop去修改HDFS版本,用bin/start-dfs.sh –upgrade命令。它将开始升级HDFS版本,升级进行的信息可以用bin/hadoop dfsadmin–upgradeProgress status命令查询,你可以用bin/hadoop dfsadmin–upgradeProgress details得到更详细的升级信息。如果升级阻塞了,你可以用bin/hadoopdfsadmin –updateProgress force命令,使它继续进行(注意,在使用这个命令时,你最好知道你在干什么)。

HDFS升级后,Hadoop保留了允许你降级到以前HDFS版本的信息,以防你想回到以前的Hadoop版本,如果你要降级就先停止集群,重装旧版的Hadoop,再运行bin/start-dfs.sh –rollback,它就会回到以前版本的HDFS状态。

每次只能保存一个存档拷贝,所以在新版本下运行几天后(当感觉它稳定后),可以用bin/hadoop dfsadmin –finalizeUpgrade命令删除存档拷贝,而rollback命令将不再可用,但这也是进行第二次升级必要的一个工作。

获得帮助:dfs中一样,输入bin/hadoop dfsadmin –help cmd命令会显示这个命令的用法。

12.      MapRedece的容错性

使用Hadoop的一个主要原因就是它的高容错性,就算在由高失败率的节点或网络组成的大集群内运行的作业,Hadoop都可以让作业成功完成。Hadoop实现容错的主要方法就是重新执行任务,单个任务节点TaskTracker会不断的与系统的核心节点JobTracker进行通信,如果一个TaskTracker在一定时间内(默认是1分钟)无法与JobTracker进行通信,那JobTracker会假设这个TaskTracker出问题挂了,JobTracker了解给每个TaskTracker赋予了那些mapreduce任务。
  如果作业仍然在mapping阶段,其它的TaskTracker会被要求重新执行所有的由前一个失败的TaskTracker所执行的map任务。如果作业在reduce阶段,则其它的TaskTracker会被要求重新执行所有的由前一个失败的TaskTracker所执行的reduce任务。
Reduce任务一旦完成会把数据写到HDFS。因此,如果一个TaskTracker已经完成赋予它的3reduce任务中的2个,那只有第三个任务会被重新执行。Map任务则更复杂一点:即使一个节点已经完成了10map任务,reducer仍可能无法获取这些map任务的所有的输出。如果此时节点挂了,那它的mapper输出就不可访问了。所以已经完成的map任务也必须被重新执行以使它们的输出结果对剩下的reducing机器可用,所有的这些都是由Hadoop平台自动操作完成的。(Map&Reduce任务重新执行原则)
这个容错性强调需要程序的执行没有副作用影响,如果MapperReducer有自身的标识并和外部有通信,那重新执行一个任务可能需要其它节点去和新的mapreduce任务实例进行通信,并且重启的任务可能需要重建它们的中间状态。这个过程是很复杂的并且容易出错。MapReduce通过去除任务标识或任务间的通信而大大简化了这个问题。单个任务只能看到它自己的输入和输出,这样就使得错误与重启过程变成清晰可靠。

13.      推测性的执行(Speculative execution

Hadoop系统有一个问题,它把任务分派到很多个节点,其中很有可能有一些慢的节点会限制剩下程序的执行速度。举个例子,如果有个节点内有一个比较慢的磁盘控制器,那它读取输入数据的速度可能只有所有其它节点的速度的10%。所以当99map任务都已经完成了,系统仍在等待最后那个比较耗时的map任务完成。
   
通过强迫任务独立运行于其它的任务,使得单个任务之间不会知道它们的输入数据来自哪里。任务相信Hadoop平台会派送合适的输入到它们那里。因此,对于相同的输入数据,我们可以并行多次处理以利用不同机器的负载能力。因为作业中大多数的任务都已经完成了,Hadoop平台会在几个空闲的节点上调度执行剩余任务的拷贝,这个过程叫做推测性的执行。当任务完成时,它会向JobTracker通告。任何一个首先完成的拷贝任务将成为权威拷贝,如果其他拷贝任务还在推测性的执行中,Hadoop会告诉TaskTracker去终止这些任务并丢弃它们的输出,接着Reducer会从首先完成的Mapper那里获取输入数据。
   
推测性的执行默认是启用的,你可以通过设置JobConf中的mapred.map.tasks.speculative.executionmapred.reduce.tasks.speculative.executionfalse来禁用mapperreducer的推测性的执行。

14.      Hadoop中的I/O操作

Hadoop中常规JAVA数据类型是不能表示HDFS的数据类型的,例如HDFS中的字符串不是String类而是Text类,这些数据类型都必须实现一个Writable接口WritableHadoop的核心(MapReduce程序使用它来序列化键/值对) void write(DataOutput out) throws IOExceptionvoid readFields(DataInput in) throwsIOException

分别实现对数据的序列化和反序列化。(Writable接口的子类)

Java 代码

1. public classMyWritable implements Writable {//自定义Writable

2. // Some data

3. private intcounter;

4. private longtimestamp;

5.

6. public voidwrite(DataOutput out) throws IOException {

7.out.writeInt(counter);

8.out.writeLong(timestamp);

9. }

10.

11. public voidreadFields(DataInput in) throws IOException {

12. counter =in.readInt();

13. timestamp =in.readLong();

14. }

15.

16. publicstatic MyWritable read(DataInput in) throws IOException {

17. MyWritable w= new MyWritable();

18.w.readFields(in);

19. return w;

20. }

21. }

其中的writereadFields分别实现了把对象序列化和反序列化的功能,是Writable接口定义的两个方法。


15.      MapReduce实现单元最短路径算法

用邻接表的方式来存储图,在文件中,每一行数据代表图的一个节点,行的格式可以采用一下格式:

ID

Edges

其中ID代表节点的标识,Edges代表从某一节点出发的所有的边(对于有向图)Edges又可以用从该节点出发的边的另一端的节点表示。例如图1.1就可以用邻接表表示。


在求解图的问题时,图中的节点和边往往含有更多的信息,在用标色法求解单元最短路径时,节点的信息还包括节点的颜色、节点到源点的距离等,边的信息包括边的权值等。对以上邻接表作稍微改进便可满足要求。更新后的图文件的每一行的格式如下:

ID

distance

color

Edgesweight

1.1所示有向图加上权值信息后如图1.2所示,用邻接表表示如下


MAX表示无穷大,颜色域,0:白色(初始状态)1:灰色(仍需处理)2:黑色(无需处理)

 

算法流程:

MAP: <k1,v1> →list(<k2,v2>)//k1:节点的ID;v1:该节点的距离、边、边的权值、颜色

begin

If( color(k1) = = gray )   //如果k1还需处理,即k1的颜色为灰色

{ "kiÎ {p | (k1 ,ki) Î k1.Edges } //对所有k1指向的节点

If ( distance(k1) + weight(k1 ,ki) <  distance(ki))

{

Set distance(distance(ki)) = distance(k1) + weight(k1 ,ki);

Set color(ki) = gray;

emit (ki, v1)

}

Set color(k1) = black;

}

emit (k1, v1)

end

 

Reduce <k2,list(v2)>→<k3,v3>

Begin

Set color(k2) = white;

Set distance(k2) = MAX;

         " viÎ list(v2)

                   If(vi.color is hevier than k2)

{

         Set color(k2) =vi.color;

}

If {vi.distance < distance(k2) )

{

                   Set  distance(k2) =  vi.distance;

           If(vi.color == gray)

                            Setcolor(k2) = gray;

}

   SetEdges(k2) = vi.edges if vi.edges != null;

emit (k2, v) 

End

 

Int I = 0;

While (++i)

{

 inputPath = mapredJob(i).outPath

 outPath= createPath()

 DomapredJob(i);

 If(canEnd)

{

         Break;

}

}

运行示例:一个job的输出作为另一个job的输入,多个job组成job chain(链式MapReduce) 来完成最短路径的求解,当某一个job的输出中所有的节点中没有灰色节点时停止迭代,算法停止。






0 0