hadoop面试题整理

hadoop面试题整理

ID:11922693

大小:54.08 KB

页数:6页

时间:2018-07-14

上传者:U-4187
hadoop面试题整理_第1页
hadoop面试题整理_第2页
hadoop面试题整理_第3页
hadoop面试题整理_第4页
hadoop面试题整理_第5页
资源描述:

《hadoop面试题整理》由会员上传分享,免费在线阅读,更多相关内容在行业资料-天天文库

1请列出你所知道的hadoop调度器并简要说明其工作方法随着MapReduce的流行,其开源实现Hadoop也变得越来越受推崇。在Hadoop系统中,有一个组件非常重要,那就是调度器,它的作用是将系统中空闲的资源按一定策略分配给作业。在Hadoop中,调度器是一个可插拔的模块,用户可以根据自己的实际应用要求设计调度器。Hadoop中常见的调度器有三种,分别为:(注:本文介绍的Hadoop调度器不够系统化,如果想了解更系统化的Hadoop调度器,可阅读我的最新书籍《Hadoop技术内幕:深入解析MapReduce架构设计与实现原理》(购买说明)第10章“Hadoop多用户作业调度器分析”,分析了当前比较流行的FIFO、Capacity个Fair三种调度器的配置方法、实现机制和优缺点对比,当然,也介绍了其他类型的几种调度器。)(1)默认的调度器FIFOHadoop中默认的调度器,它先按照作业的优先级高低,再按照到达时间的先后选择被执行的作业。(2)计算能力调度器CapacityScheduler支持多个队列,每个队列可配置一定的资源量,每个队列采用FIFO调度策略,为了防止同一个用户的作业独占队列中的资源,该调度器会对同一用户提交的作业所占资源量进行限定。调度时,首先按以下策略选择一个合适队列:计算每个队列中正在运行的任务数与其应该分得的计算资源之间的比值,选择一个该比值最小的队列;然后按以下策略选择该队列中一个作业:按照作业优先级和提交时间顺序选择,同时考虑用户资源量限制和内存限制。(3)公平调度器FairScheduler同计算能力调度器类似,支持多队列多用户,每个队列中的资源量可以配置,同一队列中的作业公平共享队列中所有资源,具体算法参见我的博文《Hadoop公平调度器算法解析》实际上,Hadoop的调度器远不止以上三种,最近,出现了很多针对新型应用的Hadoop调度器。2HDFS的存储机制1.HDFS开创性地设计出一套文件存储方式,即对文件分割后分别存放;2.HDFS将要存储的大文件进行分割,分割后存放在既定的存储块(Block)中,并通过预先设定的优化处理,模式对存储的数据进行预处理,从而解决了大文件储存与计算的需求;3.一个HDFS集群包括两大部分,即NameNode与DataNode。一般来说,一个集群中会有一个NameNode和多个DataNode共同工作;4.NameNode是集群的主服务器,主要是用于对HDFS中所有的文件及内容数据进行维护,并不断读取记录集群中DataNode主机情况与工作状态,并通过读取与写入镜像日志文件的方式进行存储;5.DataNode在HDFS集群中担任任务具体执行角色,是集群的工作节点。文件被分成若干个相同大小的数据块,分别存储在若干个DataNode上,DataNode会定期向集群内NameNode发送自己的运行状态与存储内容,并根据NameNode发送的指令进行工作;6.NameNode负责接受客户端发送过来的信息,然后将文件存储位置信息发送给提交请求的客户端,由客户端直接与DataNode进行联系,从而进行部分文件的运算与操作。7.Block是HDFS的基本存储单元,默认大小是64M;8.HDFS还可以对已经存储的Block进行多副本备份,将每个Block至少复制到3个相互独立的硬件上,这样可以快速恢复损坏的数据;9.用户可以使用既定的API接口对HDFS中的文件进行操作;10.当客户端的读取操作发生错误的时候,客户端会向NameNode报告错误,并请求NameNode排除错误的DataNode后后重新根据距离排序,从而获得一个新的DataNode的读取路径。如果所有的DataNode都报告读取失败,那么整个任务就读取失败;11.对于写出操作过程中出现的问题,FSDataOutputStream并不会立即关闭。客户端向NameNode报告错误信息,并直接向提供备份的DataNode中写入数据。备份DataNode被升级为首选DataNode,并在其余2个DataNode中备份复制数据。NameNode对错误的DataNode进行标记以便后续对其进行处理3MapReduce中的两表join几种方案1.概述在传统数据库(如:MYSQL)中,JOIN操作是非常常见且非常耗时的。而在HADOOP中进行JOIN操作,同样常见且耗时,由于Hadoop的独特设计思想,当进行JOIN操作时,有一些特殊的技巧。本文首先介绍了Hadoop上通常的JOIN实现方法,然后给出了几种针对不同输入数据集的优化方法。2.常见的join方法介绍假设要进行join的数据分别来自File1和File2.2.1reducesidejoinreducesidejoin是一种最简单的join方式,其主要思想如下:在map阶段,map函数同时读取两个文件File1和File2,为了区分两种来源的key/value数据对,对每条数据打一个标签(tag),比如:tag=0表示来自文件File1,tag=2表示来自文件File2。即:map阶段的主要任务是对不同文件中的数据打标签。在reduce阶段,reduce函数获取key相同的来自File1和File2文件的valuelist,然后对于同一个key,对File1和File2中的数据进行join(笛卡尔乘积)。即:reduce阶段进行实际的连接操作。REF:hadoopjoin之reducesidejoinhttp://blog.csdn.net/huashetianzu/article/details/78192442.2mapsidejoin之所以存在reducesidejoin,是因为在map阶段不能获取所有需要的join字段,即:同一个key对应的字段可能位于不同map中。Reducesidejoin是非常低效的,因为shuffle阶段要进行大量的数据传输。Mapsidejoin是针对以下场景进行的优化:两个待连接表中,有一个表非常大,而另一个表非常小,以至于小表可以直接存放到内存中。这样,我们可以将小表复制多份,让每个maptask内存中存在一份(比如存放到hashtable中),然后只扫描大表:对于大表中的每一条记录key/value,在hashtable中查找是否有相同的key的记录,如果有,则连接后输出即可。为了支持文件的复制,Hadoop提供了一个类DistributedCache,使用该类的方法如下:(1)用户使用静态方法DistributedCache.addCacheFile()指定要复制的文件,它的参数是文件的URI(如果是HDFS上的文件,可以这样:hdfs://namenode:9000/home/XXX/file,其中9000是自己配置的NameNode端口号)。JobTracker在作业启动之前会获取这个URI列表,并将相应的文件拷贝到各个TaskTracker的本地磁盘上。(2)用户使用DistributedCache.getLocalCacheFiles()方法获取文件目录,并使用标准的文件读写API读取相应的文件。REF:hadoopjoin之mapsidejoinhttp://blog.csdn.net/huashetianzu/article/details/78216742.3SemiJoinSemiJoin,也叫半连接,是从分布式数据库中借鉴过来的方法。它的产生动机是:对于reducesidejoin,跨机器的数据传输量非常大,这成了join操作的一个瓶颈,如果能够在map端过滤掉不会参加join操作的数据,则可以大大节省网络IO。实现方法很简单:选取一个小表,假设是File1,将其参与join的key抽取出来,保存到文件File3中,File3文件一般很小,可以放到内存中。在map阶段,使用DistributedCache将File3复制到各个TaskTracker上,然后将File2中不在File3中的key对应的记录过滤掉,剩下的reduce阶段的工作与reducesidejoin相同。更多关于半连接的介绍,可参考:半连接介绍:http://wenku.baidu.com/view/ae7442db7f1922791688e877.htmlREF:hadoopjoin之semijoinhttp://blog.csdn.net/huashetianzu/article/details/78233262.4reducesidejoin+BloomFilter在某些情况下,SemiJoin抽取出来的小表的key集合在内存中仍然存放不下,这时候可以使用BloomFiler以节省空间。BloomFilter最常见的作用是:判断某个元素是否在一个集合里面。它最重要的两个方法是:add()和contains()。最大的特点是不会存在falsenegative,即:如果contains()返回false,则该元素一定不在集合中,但会存在一定的falsepositive,即:如果contains()返回true,则该元素一定可能在集合中。因而可将小表中的key保存到BloomFilter中,在map阶段过滤大表,可能有一些不在小表中的记录没有过滤掉(但是在小表中的记录一定不会过滤掉),这没关系,只不过增加了少量的网络IO而已。更多关于BloomFilter的介绍,可参考:http://blog.csdn.net/jiaomeng/article/details/14955003.二次排序在Hadoop中,默认情况下是按照key进行排序,如果要按照value进行排序怎么办?即:对于同一个key,reduce函数接收到的valuelist是按照value排序的。这种应用需求在join操作中很常见,比如,希望相同的key中,小表对应的value排在前面。有两种方法进行二次排序,分别为:bufferandinmemorysort和value-to-keyconversion。对于bufferandinmemorysort,主要思想是:在reduce()函数中,将某个key对应的所有value保存下来,然后进行排序。这种方法最大的缺点是:可能会造成outofmemory。对于value-to-keyconversion,主要思想是:将key和部分value拼接成一个组合key(实现WritableComparable接口或者调用setSortComparatorClass函数),这样reduce获取的结果便是先按key排序,后按value排序的结果,需要注意的是,用户需要自己实现Paritioner,以便只按照key进行数据划分。Hadoop显式的支持二次排序,在Configuration类中有个setGroupingComparatorClass()方法,可用于设置排序group的key值,具体参考:http://www.cnblogs.com/xuxm2007/archive/2011/09/03/2165805.html4.后记最近一直在找工作,由于简历上写了熟悉Hadoop,所以几乎每个面试官都会问一些Hadoop相关的东西,而Hadoop上Join的实现就成了一道必问的问题,而极个别公司还会涉及到DistributedCache原理以及怎样利用DistributedCache进行Join操作。为了更好地应对这些面试官,特整理此文章。4介绍一下hbase过滤器一、过滤器(Filter)基础API中的查询操作在面对大量数据的时候是非常苍白的,这里Hbase提供了高级的查询方法:Filter。Filter可以根据簇、列、版本等更多的条件来对数据进行过滤,基于Hbase本身提供的三维有序(主键有序、列有序、版本有序),这些Filter可以高效的完成查询过滤的任务。带有Filter条件的RPC查询请求会把Filter分发到各个RegionServer,是一个服务器端(Server-side)的过滤器,这样也可以降低网络传输的压力。要完成一个过滤的操作,至少需要两个参数。一个是抽象的操作符,Hbase提供了枚举类型的变量来表示这些抽象的操作符:LESS/LESS_OR_EQUAL/EQUAL/NOT_EUQAL等;另外一个就是具体的比较器(Comparator),代表具体的比较逻辑,如果可以提高字节级的比较、字符串级的比较等。有了这两个参数,我们就可以清晰的定义筛选的条件,过滤数据。Java代码收藏代码CompareFilter(CompareOpcompareOp,WritableByteArrayComparablevalueComparator) CompareFilter是高层的抽象类,下面我们将看到它的实现类和实现类代表的各种过滤条件。这里实现类实际上代表的是参数中的过滤器过滤的内容,可以使主键、簇名、列值等,这就是由CompareFilter决定了。行过滤器(RowFilter)行过滤器的比较对象是行主键Java代码收藏代码Scanscan=newScan();Filterfilter1=newRowFilter(CompareFIlter.CompareOp.LESS_OR_EUQAL,newBinaryComparator(Bytes.toBytes("hello")));scan.setFilter(filter1);scan.close();例中的Filter会将所有的小于等于“Hello”的主键过滤出来。簇过滤器(FamilyFilter)簇过滤器过滤的是簇的名字。列过滤器(QualifierFilter)列过滤器过滤的是列的名字。值过滤器(ValueFilter)值过滤器过滤的是扫描对象的值。单值过滤器(SingleColumnValueFilter)单值过滤器是以特定列的值为过滤内容,与值过滤器不同的是,这里是特定的列,而值过滤器比较的是行内的所有列。所有在使用单值过滤器的时候要指定比较的列的坐标。Java代码收藏代码SingleColumnValueFilter(byte[]family,byte[]qualifier,CompareOpcompareOp,WritableByteArrayComparablecomparator)对于找不到该列的行,可以有特殊的处理Java代码收藏代码voidsetFilterIfMissing(booleanfilterIfMissing)默认缺省行将被包含进过滤的结果集中。前缀过滤器(PrefixFilter)前缀过滤器将会过滤掉不匹配的记录,过滤的对象是主键的值。Java代码收藏代码PrefixFilter(byte[]prefix)页过滤器(PageFilter)页过滤器可以根据主键有序返回固定数量的记录,这需要客户端在遍历的时候记住页开始的地方,配合scan的startkey一起使用。Java代码收藏代码PageFilter(intsize)键过滤器(KeyOnlyFilter)键过滤器可以简单的设置过滤的结果集中只包含键而忽略值,这里有一个选项可以把结果集的值保存为值的长度。FirstKeyOnlyFilter在键过滤器的基础上,根据列有序,只包含第一个满足的键。ColumnPrefixFilter这里过滤的对象是列的值。TimestampsFilterJava代码收藏代码TimestampsFilter(Listtimes)这里参数是一个集合,只有包含在集合中的版本才会包含在结果集中。包装类过滤器,此类过滤器要通过包装其他的过滤器才有意义,是其他过滤器的一种加强。SkipFilterJava代码收藏代码SkipFilter(Filterfilter)过滤器集合(FilterList)Hbase的过滤器设计遵照于设计模式中的组合模式,以上的所有过滤器都可以叠加起来共同作用于一次查询。二、计数器(Counter)Hbase提供一个计数器工具可以方便快速的进行计数的操作,而免去了加锁等保证原子性的操作。但是实质上,计数器还是列,有自己的簇和列名。值得注意的是,维护计数器的值最好是用Hbase提供的API,直接操作更新很容易引起数据的混乱。计数器的增量可以是正数负数,正数代表加,负数代表减。Java代码收藏代码longicrementColumnValue(byte[]row,byte[]famuly,byte[]qualifier,longamount)Resultincrement(Incrementincrement)三、协处理器(Coprocessor)协处理器的思想是把处理的复杂代码分发到各个RegionServer,使大部分的计算可以在服务器端,或者扫描的时候完成,提高处理的效率。形式上比较类似RDBMS中的存储过程,不同的是,存储过程的原理是在服务器端进行预处理等优化,而协处理器仅仅只是服务器处理,这里又有点类似于Map-Reduce中的Map阶段。协处理器(Coprocesssor)有两种,一种是观察者(Obsever)另外一种是Endpoint(LZ跪了,实在不知道翻译成啥)。每个协处理器都有一个优先级,优先级分为USER/SYSTEM,优先级决定处理器的执行顺序,SYSTEM级别的处理器永远先于USER。每个处理器都有自己的执行环境(CoprocessorEnvironment),这个环境包含当前集群和请求的状态等信息,是处理中重要的一部分,以构造函数参数的形式被传入到处理器。另外就是CoprocessorHost,这是Hbase管理协处理器的类,用来维护所有的处理器和其环境。协处理器的加载有两种方式,一种是通过配置文件,在配置文件中指定加载路径、类名等,通过这种方式加载的处理器都是SYSTEM级别的,会作用于所有的请求,所有的表;另一种方式是通过在创建表的时候在表中指定,这种方式既可以创建全局的SYSTEM级别的处理器,也可以创建USER级别的处理器,USER级别的处理器是针对表的。Java代码收藏代码Pathpath=newPaht("test.jar");HTableDescriptorhtd=newHTableDescriptor("test");htd.addFamily(newHColumnDescriptor("family1"));htd.setValue("Coprocessor$1",path.toString+"|"+className+"|"+Coprocessor.Priority.USER);HBaseAdminadmin=newHBaseAdmin(conf);admin.createTable(htd);这里setValue方法有两个参数,第一个参数是协处理器的名字,$后面跟的是影响执行顺序的序号;第二个参数是||。Observer这是第一种处理器,观察者,观察者有三种,分别用来监听RegionServerObserver、MasterServerObserver、WALObserver。RegionServer监听的是RegionServer上的操作,如在RegionServer上的Get、Put等。操作被赋予生命周期:Pendingopen--open--Pendingclose监听器是可以监听生命周期中的各个阶段,并对其做出处理。每一个监听的方法都有一个上下文参数(Context),通过Context参数可以直接的操作请求的声明周期。Java代码收藏代码voidbypass();voidcomplete();MasterObserver监听的是MasterServer上的操作,有点类似RDBMS中的DDL的操作如表操作、列操作等。具体的操作和RegionServer比较类似。 Endpoint这是第二种处理器,Endpoint相当于被分发到各个RegionServer上的存储过程,可以在客户端远程调用的方法。Endpoint的存在使我们可以进行一些服务器端的计算,如服务器聚集、求和等运算,弥补了查询API的不足。服务器端计算的优势是显而易见的,它可以降低网络传输的数据量,合理利用服务器资源。从功能上可以看出Endpoint是一个基于RPC调用的模块,所以在实现自己的Endpoint时候需要定义我们自己的通信协议。在Hbase中,通信协议被抽象为CoprocessorProtocol接口,要实现我们的协议,我们要创建协议接口继承自CoprocessorProtocol接口,然后再实现我们的协议类。Java代码收藏代码publicinterfaceMyProtocolextendsCoprocessorProtocol{publicintwork();}协议类本身也是处理器,所以还要继承BaseEndpointCoprocessor类。Java代码收藏代码publicclassMyEndpointextendsBaseEndpointCoprocessorimplementsMyProtocol{publicintwork(){Sytem.out.println("hello");}}在抽象的父类BaseEndpointCoprocessor中还提供了一些有用的方法,如我们可以拿到对应的环境类。Java代码收藏代码RegionCoprocessorEnvironmentgetEnvironment()配置好Endpoint重启集群环境以后,我们的实现类会被分发到各个RegionServer,通过HTable实例的方法我们可以调用到Endpoint。Java代码收藏代码MapcoprocessorExec(Classprotocol,byte[]startKey,byte[]endKey,Batch.Callcallable);startKey和endKey用于确定哪些RegionServer将执行Endpoint,Batch中的内部类将决定协议中方法的调用。四、HTablePool连接池在Hbase中,创建一个代表表的HTable实例是一个耗时且很占资源的操作,类似操作数据库,我们也需要建立我们自己的连接池,于是有了代表连接池的抽象类:HTable。Java代码收藏代码HTablePool(Configuarationconf,intmaxSize)HTablePool(Configuarationconf,intmaxSize,HTableInterfaceFactoryfactory)创建HTable需要配置文件的实例,连接池的最大连接数也在构造方法中设置。另外,如果想要自己控制HTable被创建的过程,则需要实现自己的工厂方法。在连接池中,最大连接数(maxSize)的含义是,连接池管理的最大的连接数,当所需要的连接数超过最大值时,会临时的创建连接来满足需求,但是这些连接在使用完毕之后会被直接释放且丢弃而不会进入连接池被管理,所以最大连接数代表的是连接池中最大被管理的连接数,而不是使用连接池最大可使用的连接数。Java代码收藏代码HTableInterfacegetTable(StringtableName)HTableInterfacegetTable(byte[]tableName)voidputTable(HTableInterfacetable)需要注意的是,使用完连接以后需要手动的调用putTable方法将连接放回池中。5hbase性能调优我们经常看到一些文章吹嘘某产品如何如何快,如何如何强,而自己测试时却不如描述的一些数据。其实原因可能在于你还不是真正理解其内部结构,对于其性能调优方法不够了解。本文转自TaoBao的KenWu同学的博客,是目前看到比较完整的HBase调优文章。原文链接:HBase性能调优因官方BookPerformanceTuning部分章节没有按配置项进行索引,不能达到快速查阅的效果。所以我以配置项驱动,重新整理了原文,并补充一些自己的理解,如有错误,欢迎指正。配置优化zookeeper.session.timeout默认值:3分钟(180000ms)说明:RegionServer与Zookeeper间的连接超时时间。当超时时间到后,ReigonServer会被Zookeeper从RS集群清单中移除,HMaster收到移除通知后,会对这台server负责的regions重新balance,让其他存活的RegionServer接管.调优:这个timeout决定了RegionServer是否能够及时的failover。设置成1分钟或更低,可以减少因等待超时而被延长的failover时间。不过需要注意的是,对于一些Online应用,RegionServer从宕机到恢复时间本身就很短的(网络闪断,crash等故障,运维可快速介入),如果调低timeout时间,反而会得不偿失。因为当ReigonServer被正式从RS集群中移除时,HMaster就开始做balance了(让其他RS根据故障机器记录的WAL日志进行恢复)。当故障的RS在人工介入恢复后,这个balance动作是毫无意义的,反而会使负载不均匀,给RS带来更多负担。特别是那些固定分配regions的场景。hbase.regionserver.handler.count默认值:10说明:RegionServer的请求处理IO线程数。调优:这个参数的调优与内存息息相关。较少的IO线程,适用于处理单次请求内存消耗较高的BigPUT场景(大容量单次PUT或设置了较大cache的scan,均属于BigPUT)或ReigonServer的内存比较紧张的场景。较多的IO线程,适用于单次请求内存消耗低,TPS要求非常高的场景。设置该值的时候,以监控内存为主要参考。这里需要注意的是如果server的region数量很少,大量的请求都落在一个region上,因快速充满memstore触发flush导致的读写锁会影响全局TPS,不是IO线程数越高越好。压测时,开启EnablingRPC-levellogging,可以同时监控每次请求的内存消耗和GC的状况,最后通过多次压测结果来合理调节IO线程数。这里是一个案例HadoopandHBaseOptimizationforReadIntensiveSearchApplications,作者在SSD的机器上设置IO线程数为100,仅供参考。hbase.hregion.max.filesize默认值:256M说明:在当前ReigonServer上单个Reigon的最大存储空间,单个Region超过该值时,这个Region会被自动split成更小的region。调优:小region对split和compaction友好,因为拆分region或compact小region里的storefile速度很快,内存占用低。缺点是split和compaction会很频繁。特别是数量较多的小region不停地split,compaction,会导致集群响应时间波动很大,region数量太多不仅给管理上带来麻烦,甚至会引发一些Hbase的bug。一般512以下的都算小region。大region,则不太适合经常split和compaction,因为做一次compact和split会产生较长时间的停顿,对应用的读写性能冲击非常大。此外,大region意味着较大的storefile,compaction时对内存也是一个挑战。当然,大region也有其用武之地。如果你的应用场景中,某个时间点的访问量较低,那么在此时做compact和split,既能顺利完成split和compaction,又能保证绝大多数时间平稳的读写性能。既然split和compaction如此影响性能,有没有办法去掉?compaction是无法避免的,split倒是可以从自动调整为手动。只要通过将这个参数值调大到某个很难达到的值,比如100G,就可以间接禁用自动split(RegionServer不会对未到达100G的region做split)。再配合RegionSplitter这个工具,在需要split时,手动split。手动split在灵活性和稳定性上比起自动split要高很多,相反,管理成本增加不多,比较推荐online实时系统使用。内存方面,小region在设置memstore的大小值上比较灵活,大region则过大过小都不行,过大会导致flush时app的IOwait增高,过小则因storefile过多影响读性能。hbase.regionserver.global.memstore.upperLimit/lowerLimit默认值:0.4/0.35upperlimit说明:hbase.hregion.memstore.flush.size这个参数的作用是当单个memstore达到指定值时,flush该memstore。但是,一台ReigonServer可能有成百上千个memstore,每个memstore也许未达到flush.size,jvm的heap就不够用了。该参数就是为了限制memstores占用的总内存。当ReigonServer内所有的memstore所占用的内存总和达到heap的40%时,HBase会强制block所有的更新并flush这些memstore以释放所有memstore占用的内存。lowerLimit说明:同upperLimit,只不过当全局memstore的内存达到35%时,它不会flush所有的memstore,它会找一些内存占用较大的memstore,做个别flush,当然更新还是会被block。lowerLimit算是一个在全局flush导致性能暴跌前的补救措施。为什么说是性能暴跌?可以想象一下,如果memstore需要在一段较长的时间内做全量flush,且这段时间内无法接受任何读写请求,对HBase集群的性能影响是很大的。调优:这是一个Heap内存保护参数,默认值已经能适用大多数场景。它的调整一般是为了配合某些专属优化,比如读密集型应用,将读缓存开大,降低该值,腾出更多内存给其他模块使用。这个参数会给使用者带来什么影响?比如,10G内存,100个region,每个memstore64M,假设每个region只有一个memstore,那么当100个memstore平均占用到50%左右时,就会达到lowerLimit的限制。假设此时,其他memstore同样有很多的写请求进来。在那些大的region未flush完,就可能又超过了upperlimit,则所有region都会被block,开始触发全局flush。不过,除了你的内存非常小或你的应用场景里大多数都是读,我觉得不需要去调这个参数。hfile.block.cache.size默认值:0.2说明:storefile的读缓存占用Heap的大小百分比,0.2表示20%。该值直接影响数据读的性能。调优:当然是越大越好,如果读比写少,开到0.4-0.5也没问题。如果读写较均衡,0.3左右。如果写比读多,果断默认吧。设置这个值的时候,你同时要参考hbase.regionserver.global.memstore.upperLimit,该值是memstore占heap的最大百分比,两个参数一个影响读,一个影响写。如果两值加起来超过80-90%,会有OOM的风险,谨慎设置。hbase.hstore.blockingStoreFiles默认值:7说明:在compaction时,如果一个Store(CoulmnFamily)内有超过7个storefile需要合并,则block所有的写请求,进行flush,限制storefile数量增长过快。 调优:block写请求会影响当前region的性能,将值设为单个region可以支撑的最大storefile数量会是个不错的选择,即允许comapction时,memstore继续生成storefile。最大storefile数量可通过regionsize/memstoresize来计算。如果你将regionsize设为无限大,那么你需要预估一个region可能产生的最大storefile数。hbase.hregion.memstore.block.multiplier默认值:2说明:当一个region里的memstore超过单个memstore.size两倍的大小时,block该region的所有请求,进行flush,释放内存。虽然我们设置了memstore的总大小,比如64M,但想象一下,在最后63.9M的时候,我Put了一个100M的数据,此时memstore的大小会瞬间暴涨到超过预期的memstore.size。这个参数的作用是当memstore的大小增至超过memstore.size时,block所有请求,遏制风险进一步扩大。调优:这个参数的默认值还是比较靠谱的。如果你预估你的正常应用场景(不包括异常)不会出现突发写或写的量可控,那么保持默认值即可。如果正常情况下,你的写请求量就会经常暴长到正常的几倍,那么你应该调大这个倍数并调整其他参数值,比如hfile.block.cache.size和hbase.regionserver.global.memstore.upperLimit/lowerLimit,以预留更多内存,防止HBaseserverOOM。其他启用LZO压缩LZO对比Hbase默认的GZip,前者性能较高,后者压缩比较高,具体参见UsingLZOCompression。对于想提高HBase读写性能的开发者,采用LZO是比较好的选择。对于非常在乎存储空间的开发者,则建议保持默认。不要在一张表里定义太多的ColumnFamilyHbase目前不能良好的处理超过包含2-3个CF的表。因为某个CF在flush发生时,它邻近的CF也会因关联效应被触发flush,最终导致系统产生更多IO。批量导入在批量导入数据到Hbase前,你可以通过预先创建regions,来平衡数据的负载。详见TableCreation:Pre-CreatingRegions避免CMSconcurrentmodefailureHBase使用CMSGC。默认触发GC的时机是当年老代内存达到90%的时候,这个百分比由-XX:CMSInitiatingOccupancyFraction=N这个参数来设置。concurrentmodefailed发生在这样一个场景:当年老代内存达到90%的时候,CMS开始进行并发垃圾收集,于此同时,新生代还在迅速不断地晋升对象到年老代。当年老代CMS还未完成并发标记时,年老代满了,悲剧就发生了。CMS因为没内存可用不得不暂停mark,并触发一次全jvm的stoptheworld(挂起所有线程),然后采用单线程拷贝方式清理所有垃圾对象。这个过程会非常漫长。为了避免出现concurrentmodefailed,我们应该让GC在未到90%时,就触发。通过设置-XX:CMSInitiatingOccupancyFraction=N这个百分比,可以简单的这么计算。如果你的hfile.block.cache.size和hbase.regionserver.global.memstore.upperLimit加起来有60%(默认),那么你可以设置70-80,一般高10%左右差不多。Hbase客户端优化AutoFlush将HTable的setAutoFlush设为false,可以支持客户端批量更新。即当Put填满客户端flush缓存时,才发送到服务端。默认是true。ScanCachingscanner一次缓存多少数据来scan(从服务端一次抓多少数据回来scan)。默认值是1,一次只取一条。ScanAttributeSelectionscan时建议指定需要的ColumnFamily,减少通信量,否则scan操作默认会返回整个row的所有数据(所有CoulmnFamily)。CloseResultScanners通过scan取完数据后,记得要关闭ResultScanner,否则RegionServer可能会出现问题(对应的Server资源无法释放)。OptimalLoadingofRowKeys当你scan一张表的时候,返回结果只需要rowkey(不需要CF,qualifier,values,timestaps)时,你可以在scan实例中添加一个filterList,并设置MUST_PASS_ALL操作,filterList中addFirstKeyOnlyFilter或KeyOnlyFilter。这样可以减少网络通信量。TurnoffWALonPuts当Put某些非重要数据时,你可以设置writeToWAL(false),来进一步提高写性能。writeToWAL(false)会在Put时放弃写WALlog。风险是,当RegionServer宕机时,可能你刚才Put的那些数据会丢失,且无法恢复。启用BloomFilterBloomFilter通过空间换时间,提高读操作性能。6hbase预分区设计HBase中,表会被划分为1...n个Region,被托管在RegionServer中。Region二个重要的属性:StartKey与EndKey表示这个Region维护的rowKey范围,当我们要读/写数据时,如果rowKey落在某个start-endkey范围内,那么就会定位到目标region并且读/写到相关的数据。简单地说,有那么一点点类似人群划分,1-15岁为小朋友,16-39岁为年轻人,40-64为中年人,65岁以上为老年人。(这些数值都是拍脑袋出来的,只是举例,非真实),然后某人找队伍,然后根据年龄,处于哪个范围,就找到它所属的队伍。:(有点废话了。。。。然后,默认地,当我们只是通过HBaseAdmin指定TableDescriptor来创建一张表时,只有一个region,正处于混沌时期,start-endkey无边界,可谓海纳百川。啥样的rowKey都可以接受,都往这个region里装,然而,当数据越来越多,region的size越来越大时,大到一定的阀值,hbase认为再往这个region里塞数据已经不合适了,就会找到一个midKey将region一分为二,成为2个region,这个过程称为分裂(region-split).而midKey则为这二个region的临界,左为N无下界,右为M无上界。midKey则会被塞到M区。如何找到midKey?涉及的内容比较多,暂且不去讨论,最简单的可以认为是region的总行数/2的那一行数据的rowKey.虽然实际上比它会稍复杂点。如果我们就这样默认地,建表,表里不断地Put数据,更严重的是我们的rowkey还是顺序增大的,是比较可怕的。存在的缺点比较明显。首先是热点写,我们总是会往最大的start-key所在的region写东西,因为我们的rowkey总是会比之前的大,并且hbase的是按升序方式排序的。所以写操作总是被定位到无上界的那个region中。其次,由于写热点,我们总是往最大start-key的region写记录,之前分裂出来的region不会再被写数据,有点被打进冷宫的赶脚,它们都处于半满状态,这样的分布也是不利的。如果在写比较频率的场景下,数据增长快,split的次数也会增多,由于split是比较耗时耗资源的,所以我们并不希望这种事情经常发生。............看到这些缺点,我们知道,在集群的环境中,为了得到更好的并行性,我们希望有好的loadblance,让每个节点提供的请求处理都是均等的。我们也希望,region不要经常split,因为split会使server有一段时间的停顿,如何能做到呢?随机散列与预分区。二者结合起来,是比较完美的,预分区一开始就预建好了一部分region,这些region都维护着自已的start-endkeys,再配合上随机散列,写数据能均等地命中这些预建的region,就能解决上面的那些缺点,大大地提高了性能。提供2种思路:hash与partition.一、hash就是rowkey前面由一串随机字符串组成,随机字符串生成方式可以由SHA或者MD5等方式生成,只要region所管理的start-endkeys范围比较随机,那么就可以解决写热点问题。longcurrentId=1L;byte[]rowkey=Bytes.add(MD5Hash.getMD5AsHex(Bytes.toBytes(currentId)).substring(0,8).getBytes(),Bytes.toBytes(currentId));假设rowKey原本是自增长的long型,可以将rowkey转为hash再转为bytes,加上本身id转为bytes,组成rowkey,这样就生成随便的rowkey。那么对于这种方式的rowkey设计,如何去进行预分区呢?1.取样,先随机生成一定数量的rowkey,将取样数据按升序排序放到一个集合里2.根据预分区的region个数,对整个集合平均分割,即是相关的splitKeys.3.HBaseAdmin.createTable(HTableDescriptortableDescriptor,byte[][]splitkeys)可以指定预分区的splitKey,即是指定region间的rowkey临界值.以上,就已经按hash方式,预建好了分区,以后在插入数据的时候,也要按照此rowkeyGenerator的方式生成rowkey,有兴趣的话,也可以做些试验,插入些数据,看看数据的分布。二、partition故名思义,就是分区式,这种分区有点类似于mapreduce中的partitioner,将区域用长整数(Long)作为分区号,每个region管理着相应的区域数据,在rowKey生成时,将id取模后,然后拼上id整体作为rowKey.这个比较简单,不需要取样,splitKeys也非常简单,直接是分区号即可。直接上代码吧:calcSplitKeys方法比较单纯,splitKey就是partition的编号,我们看看测试类:Java代码收藏代码通过partition实现的loadblance写的话,当然生成rowkey方式也要结合当前的region数目取模而求得,大家同样也可以做些实验,看看数据插入后的分布。在这里也顺提一下,如果是顺序的增长型原id,可以将id保存到一个数据库,传统的也好,redis的也好,每次取的时候,将数值设大1000左右,以后id可以在内存内增长,当内存数量已经超过1000的话,再去load下一个,有点类似于oracle中的sqeuence.随机分布加预分区也不是一劳永逸的。因为数据是不断地增长的,随着时间不断地推移,已经分好的区域,或许已经装不住更多的数据,当然就要进一步进行split了,同样也会出现性能损耗问题,所以我们还是要规划好数据增长速率,观察好数据定期维护,按需分析是否要进一步分行手工将分区再分好,也或者是更严重的是新建表,做好更大的预分区然后进行数据迁移。7yarn编程模型文章思路:首先提出第一代MRv1的局限性,然后解释YARN是怎么克服这些局限性的,接着说了YARN的编程模型,说了YARN的组成,YARN的通信协议和YARN的运行过程。通过这样的描述来认识YARN的。MRv1的局限性YARN是在MRv1的基础上演化而来,它克服了MRv1的各种局限性:1:扩展性差MRv1中,Jobracker同事兼备了资源管理和作业控制两个功能。2:可靠性差 MRv1才用了master/slave结构,master存在单点故障的问题。3:资源利用率低MRv1采用了基于槽位的资源分配模型,槽位是一种粗粒度的资源划分单位,通常一个任务不会用完槽位对应的资源,且其他任务无法使用这些空闲的资源。4:无法支持多种计算框架。不能支持新的计算框架:包括内存计算框架,流式计算框架和迭代式计算框架。第二代的mapreduce框架的TaskScheduler就是yarnYARN的编程模型1:保证编程模型的向下兼容性,MRv2重用了MRv1的编程模型和数据处理引擎,但运行环境被重写。2:编程模型与数据处理引擎mapreduce应用程序编程接口有两套:新的API(mapred)和旧的API(mapreduce)采用MRv1旧的API编写的程序可直接运行在MRv2上采用MRv1新的API编写的程序需要使用MRv2编程库重新编译并修改不兼容的参数和返回值3:运行时环境MRv1:Jobracker和TasktrackerMRv2:YARN和ApplicationMasterYARN的组成yarn主要由ResourceManager,NodeManager,ApplicationMaster和Container等几个组件组成。ResourceManager(RM)RM是全局资源管理器,负责整个系统的资源管理和分配。主要由两个组件组成:调度器和应用程序管理器(ASM)调度器调度器根据容量,队列等限制条件,将系统中的资源分配给各个正在运行的应用程序不负责具体应用程序的相关工作,比如监控或跟踪状态不负责重新启动失败任务资源分配单位用“资源容器”resourceContainer表示Container是一个动态资源分配单位,它将内存,CPU,磁盘,网络等资源封装在一起,从而限定每个任务的资源量调度器是一个可插拔的组件,用户可以自行设计YARN提供了多种直接可用的调度器,比如fairScheduler和CapacityScheduler等。应用程序管理器负责管理整个系统中所有应用程序ApplicationMaster(AM)用户提交的每个应用程序均包含一个AMAM的主要功能与RM调度器协商以获取资源(用Container表示)将得到的任务进一步分配给内部的任务与NM通信以自动/停止任务监控所有任务运行状态,并在任务运行失败时重新为任务申请资源以重启任务当前YARN自带了两个AM实现一个用于演示AM编写方法的实例程序distributedshell一个用于Mapreduce程序---MRAppMaster其他的计算框架对应的AM正在开发中,比如spark等。Nodemanager(NM)和ContainerNM是每个节点上的资源和任务管理器定时向RM汇报本节点上的资源使用情况和各个Container的运行状态接收并处理来自AM的Container启动/停止等各种要求Container是YARN中的资源抽象,它封装了某个节点上的多维度资源YARN会为每个任务分配一个Container,且改任务只能使用该Container中描述的资源Container不同于MRv1的slot,它是一个动态资源划分单位,是根据应用程序的需求动态产生的yarn的通信协议YARN主要由以下几个协议组成ApplicationClientProtocolJobclient通过该RPC协议提交应用才程序,查询应用程序状态等ResourceManagerAdministrationProtocolAdmin通过该协议更新系统配置文件,比如节点黑名单,用户队列权限等。ApplicationMasterProtocolAM通过该RPC协议想RM注册和撤销自己,并为各个任务申请资源ContainerManagementProtocolAM通过要求NM启动或者停止Container,获取各个Container的使用状态等信息ResourceTrackerNM通过该RPC协议向RM注册,并定时发送心跳信息汇报当前节点的资源使用情况和Container运行状况YARN的工作流程文字描述一下这个过程:1:由客户端提交一个应用,由RM的ASM接受应用请求提交过来的应用程序包括哪些内容:a:ApplicationMasterb:启动Applicationmaster的命令c:本身应用程序的内容2:提交了三部分内容给RM,然后RM找NodeManager,然后Nodemanager就启用Applicationmaster,并分配Container接下来我们就要执行这个任务了,3:但是执行任务需要资源,所以我们得向RM的ASM申请执行任务的资源(它会在RM这儿注册一下,说我已经启动了,注册了以后就可以通过RM的来管理,我们用户也可以通过RM的web客户端来监控任务的状态)ASM只是负责APplicationMaster的启用4::我们注册好了后,得申请资源,申请资源是通过第四步,向ResourceScheduler申请的5:申请并领取资源后,它会找Nodemanager,告诉他我应经申请到了,然后Nodemanager判断一下,6:知道他申请到了以后就会启动任务,当前启动之前会准备好环境,7:任务启动以后会跟APplicationmaster进行通信,不断的心跳进行任务的汇报。8:完成以后会给RM进行汇报,让RSM撤销注册。然后RSM就会回收资源。当然了,我们是分布式的,所以我们不会只跟自己的Nodemanager通信。也会跟其他的节点通信。8Shuffle过程Shuffle过程是MapReduce的核心,也被称为奇迹发生的地方。要想理解MapReduce,Shuffle是必须要了解的。我看过很多相关的资料,但每次看完都云里雾里的绕着,很难理清大致的逻辑,反而越搅越混。前段时间在做MapReducejob性能调优的工作,需要深入代码研究MapReduce的运行机制,这才对Shuffle探了个究竟。考虑到之前我在看相关资料而看不懂时很恼火,所以在这里我尽最大的可能试着把Shuffle说清楚,让每一位想了解它原理的朋友都能有所收获。如果你对这篇文章有任何疑问或建议请留言到后面,谢谢!Shuffle的正常意思是洗牌或弄乱,可能大家更熟悉的是JavaAPI里的Collections.shuffle(List)方法,它会随机地打乱参数list里的元素顺序。如果你不知道MapReduce里Shuffle是什么,那么请看这张图:点击查看原始大小图片这张是官方对Shuffle过程的描述。但我可以肯定的是,单从这张图你基本不可能明白Shuffle的过程,因为它与事实相差挺多,细节也是错乱的。后面我会具体描述Shuffle的事实情况,所以这里你只要清楚Shuffle的大致范围就成-怎样把maptask的输出结果有效地传送到reduce端。也可以这样理解,Shuffle描述着数据从maptask输出到reducetask输入的这段过程。在Hadoop这样的集群环境中,大部分maptask与reducetask的执行是在不同的节点上。当然很多情况下Reduce执行时需要跨节点去拉取其它节点上的maptask结果。如果集群正在运行的job有很多,那么task的正常执行对集群内部的网络资源消耗会很严重。这种网络消耗是正常的,我们不能限制,能做的就是最大化地减少不必要的消耗。还有在节点内,相比于内存,磁盘IO对job完成时间的影响也是可观的。从最基本的要求来说,我们对Shuffle过程的期望可以有:完整地从maptask端拉取数据到reduce端。在跨节点拉取数据时,尽可能地减少对带宽的不必要消耗。减少磁盘IO对task执行的影响。OK,看到这里时,大家可以先停下来想想,如果是自己来设计这段Shuffle过程,那么你的设计目标是什么。我想能优化的地方主要在于减少拉取数据的量及尽量使用内存而不是磁盘。我的分析是基于Hadoop0.21.0的源码,如果与你所认识的Shuffle过程有差别,不吝指出。我会以WordCount为例,并假设它有8个maptask和3个reducetask。从上图看出,Shuffle过程横跨map与reduce两端,所以下面我也会分两部分来展开。先看看map端的情况,如下图:上图可能是某个maptask的运行情况。拿它与官方图的左半边比较,会发现很多不一致。官方图没有清楚地说明partition,sort与combiner到底作用在哪个阶段。我画了这张图,希望让大家清晰地了解从map数据输入到map端所有数据准备好的全过程。整个流程我分了四步。简单些可以这样说,每个maptask都有一个内存缓冲区,存储着map的输出结果,当缓冲区快满的时候需要将缓冲区的数据以一个临时文件的方式存放到磁盘,当整个maptask结束后再对磁盘中这个maptask产生的所有临时文件做合并,生成最终的正式输出文件,然后等待reducetask来拉数据。 当然这里的每一步都可能包含着多个步骤与细节,下面我对细节来一一说明:1.在maptask执行时,它的输入数据来源于HDFS的block,当然在MapReduce概念中,maptask只读取split。Split与block的对应关系可能是多对一,默认是一对一。在WordCount例子里,假设map的输入数据都是像“aaa”这样的字符串。2.在经过mapper的运行后,我们得知mapper的输出是这样一个key/value对:key是“aaa”,value是数值1。因为当前map端只做加1的操作,在reducetask里才去合并结果集。前面我们知道这个job有3个reducetask,到底当前的“aaa”应该交由哪个reduce去做呢,是需要现在决定的。MapReduce提供Partitioner接口,它的作用就是根据key或value及reduce的数量来决定当前的这对输出数据最终应该交由哪个reducetask处理。默认对keyhash后再以reducetask数量取模。默认的取模方式只是为了平均reduce的处理能力,如果用户自己对Partitioner有需求,可以订制并设置到job上。在我们的例子中,“aaa”经过Partitioner后返回0,也就是这对值应当交由第一个reducer来处理。接下来,需要将数据写入内存缓冲区中,缓冲区的作用是批量收集map结果,减少磁盘IO的影响。我们的key/value对以及Partition的结果都会被写入缓冲区。当然写入之前,key与value值都会被序列化成字节数组。整个内存缓冲区就是一个字节数组,它的字节索引及key/value存储结构我没有研究过。如果有朋友对它有研究,那么请大致描述下它的细节吧。3.这个内存缓冲区是有大小限制的,默认是100MB。当maptask的输出结果很多时,就可能会撑爆内存,所以需要在一定条件下将缓冲区中的数据临时写入磁盘,然后重新利用这块缓冲区。这个从内存往磁盘写数据的过程被称为Spill,中文可译为溢写,字面意思很直观。这个溢写是由单独线程来完成,不影响往缓冲区写map结果的线程。溢写线程启动时不应该阻止map的结果输出,所以整个缓冲区有个溢写的比例spill.percent。这个比例默认是0.8,也就是当缓冲区的数据已经达到阈值(buffersize*spillpercent=100MB*0.8=80MB),溢写线程启动,锁定这80MB的内存,执行溢写过程。Maptask的输出结果还可以往剩下的20MB内存中写,互不影响。当溢写线程启动后,需要对这80MB空间内的key做排序(Sort)。排序是MapReduce模型默认的行为,这里的排序也是对序列化的字节做的排序。在这里我们可以想想,因为maptask的输出是需要发送到不同的reduce端去,而内存缓冲区没有对将发送到相同reduce端的数据做合并,那么这种合并应该是体现是磁盘文件中的。从官方图上也可以看到写到磁盘中的溢写文件是对不同的reduce端的数值做过合并。所以溢写过程一个很重要的细节在于,如果有很多个key/value对需要发送到某个reduce端去,那么需要将这些key/value值拼接到一块,减少与partition相关的索引记录。在针对每个reduce端而合并数据时,有些数据可能像这样:“aaa”/1,“aaa”/1。对于WordCount例子,就是简单地统计单词出现的次数,如果在同一个maptask的结果中有很多个像“aaa”一样出现多次的key,我们就应该把它们的值合并到一块,这个过程叫reduce也叫combine。但MapReduce的术语中,reduce只指reduce端执行从多个maptask取数据做计算的过程。除reduce外,非正式地合并数据只能算做combine了。其实大家知道的,MapReduce中将Combiner等同于Reducer。如果client设置过Combiner,那么现在就是使用Combiner的时候了。将有相同key的key/value对的value加起来,减少溢写到磁盘的数据量。Combiner会优化MapReduce的中间结果,所以它在整个模型中会多次使用。那哪些场景才能使用Combiner呢?从这里分析,Combiner的输出是Reducer的输入,Combiner绝不能改变最终的计算结果。所以从我的想法来看,Combiner只应该用于那种Reduce的输入key/value与输出key/value类型完全一致,且不影响最终结果的场景。比如累加,最大值等。Combiner的使用一定得慎重,如果用好,它对job执行效率有帮助,反之会影响reduce的最终结果。4.每次溢写会在磁盘上生成一个溢写文件,如果map的输出结果真的很大,有多次这样的溢写发生,磁盘上相应的就会有多个溢写文件存在。当maptask真正完成时,内存缓冲区中的数据也全部溢写到磁盘中形成一个溢写文件。最终磁盘中会至少有一个这样的溢写文件存在(如果map的输出结果很少,当map执行完成时,只会产生一个溢写文件),因为最终的文件只有一个,所以需要将这些溢写文件归并到一起,这个过程就叫做Merge。Merge是怎样的?如前面的例子,“aaa”从某个maptask读取过来时值是5,从另外一个map读取时值是8,因为它们有相同的key,所以得merge成group。什么是group。对于“aaa”就是像这样的:{“aaa”,[5,8,2,…]},数组中的值就是从不同溢写文件中读取出来的,然后再把这些值加起来。请注意,因为merge是将多个溢写文件合并到一个文件,所以可能也有相同的key存在,在这个过程中如果client设置过Combiner,也会使用Combiner来合并相同的key。至此,map端的所有工作都已结束,最终生成的这个文件也存放在TaskTracker够得着的某个本地目录内。每个reducetask不断地通过RPC从JobTracker那里获取maptask是否完成的信息,如果reducetask得到通知,获知某台TaskTracker上的maptask执行完成,Shuffle的后半段过程开始启动。简单地说,reducetask在执行之前的工作就是不断地拉取当前job里每个maptask的最终结果,然后对从不同地方拉取过来的数据不断地做merge,也最终形成一个文件作为reducetask的输入文件。见下图:点击查看原始大小图片如map端的细节图,Shuffle在reduce端的过程也能用图上标明的三点来概括。当前reducecopy数据的前提是它要从JobTracker获得有哪些maptask已执行结束,这段过程不表,有兴趣的朋友可以关注下。Reducer真正运行之前,所有的时间都是在拉取数据,做merge,且不断重复地在做。如前面的方式一样,下面我也分段地描述reduce端的Shuffle细节:1.Copy过程,简单地拉取数据。Reduce进程启动一些数据copy线程(Fetcher),通过HTTP方式请求maptask所在的TaskTracker获取maptask的输出文件。因为maptask早已结束,这些文件就归TaskTracker管理在本地磁盘中。2.Merge阶段。这里的merge如map端的merge动作,只是数组中存放的是不同map端copy来的数值。Copy过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比map端的更为灵活,它基于JVM的heapsize设置,因为Shuffle阶段Reducer不运行,所以应该把绝大部分的内存都给Shuffle用。这里需要强调的是,merge有三种形式:1)内存到内存2)内存到磁盘3)磁盘到磁盘。默认情况下第一种形式不启用,让人比较困惑,是吧。当内存中的数据量到达一定阈值,就启动内存到磁盘的merge。与map端类似,这也是溢写的过程,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的那个文件。3.Reducer的输入文件。不断地merge后,最后会生成一个“最终文件”。为什么加引号?因为这个文件可能存在于磁盘上,也可能存在于内存中。对我们来说,当然希望它存放于内存中,直接作为Reducer的输入,但默认情况下,这个文件是存放于磁盘中的。至于怎样才能让这个文件出现在内存中,之后的性能优化篇我再说。当Reducer的输入文件已定,整个Shuffle才最终结束。然后就是Reducer执行,把结果放到HDFS上。上面就是整个Shuffle的过程。细节很多,我很多都略过了,只试着把要点说明白。当然,我可能也有理解或表述上的很多问题,不吝指点。我希望不断地完善和修改这篇文章,能让它通俗、易懂,看完就能知道Shuffle的方方面面。至于具体的实现原理,各位有兴趣就自己去探索,如果不方便的话,留言给我,我再来研究并反馈。9K-Means聚类算法原理一,K-Means聚类算法原理k-means算法接受参数k;然后将事先输入的n个数据对象划分为k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”(引力中心)来进行计算的。  K-means算法是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一。K-means算法的基本思想是:以空间中k个点为中心进行聚类,对最靠近他们的对象归类。通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果。  假设要把样本集分为c个类别,算法描述如下:  (1)适当选择c个类的初始中心;  (2)在第k次迭代中,对任意一个样本,求其到c个中心的距离,将该样本归到距离最短的中心所在的类;  (3)利用均值等方法更新该类的中心值;  (4)对于所有的c个聚类中心,如果利用(2)(3)的迭代法更新后,值保持不变,则迭代结束,否则继续迭代。  该算法的最大优势在于简洁和快速。算法的关键在于初始中心的选择和距离公式。二,mahoutKmeans聚类实现:(1)参数input指定待聚类的所有数据点,clusters指定初始聚类中心如果指定参数k,由org.apache.mahout.clustering.kmeans.RandomSeedGenerator.buildRandom通过org.apache.hadoop.fs直接从input指定文件中随机读取k个点放入clusters中(2)根据原数据点和上一次迭代(或初始聚类)的聚类中心计算本次迭代的聚类中心,输出到clusters-N目录下。该过程由org.apache.mahout.clustering.kmeans下的KMeansMapperKMeansCombinerKMeansReducerKMeansDriver实现KMeansMapper:在configure中初始化mapper时读入上一次迭代产生或初始聚类中心(每个mapper都读入所有的聚类中心);map方法对输入的每个点,计算距离其最近的类,并加入其中输出key为该点所属聚类ID,value为KMeansInfo实例,包含点的个数和各分量的累加和KMeansCombiner:本地累加KMeansMapper输出的同一聚类ID下的点个数和各分量的和KMeansReducer:累加同一聚类ID下的点个数和各分量的和,求本次迭代的聚类中心;并根据输入Delta判断该聚类是否已收敛:上一次迭代聚类中心与本次迭代聚类中心距离

当前文档最多预览五页,下载文档查看全文

此文档下载收益归作者所有

当前文档最多预览五页,下载文档查看全文
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,天天文库负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。
关闭