Hadoop一网打尽

239 阅读10分钟
Hadoop三大金刚:HDFS/MapReduce/Yarn,聊聊原理、流程、调优。下面是三台主机上的部署情况

image.png

1、HDFS

  • 简介:HDFS是分布式文件存储系统
  • 组成:NameNode(NN)、DataNode(DN)、SecondaryNameNode(2NN)
  • 优点:可搭建在廉价的机器上
  • 缺点:
    • 只适合查、追加,不适合删、改
    • 存储小文件不高效
    • 不支持并发

1.1 写流程

image.png

  • Client向NN请求在某目录存放某个文件(文件=多个Block)
  • NN检查权限,并检查有无此目录
  • Client向NN请求上传数据
  • NN就近返回三个DN地址给Client(机架感知)
  • Client向DN请求建立数据传输通道,串联三个DN
  • DN串联完毕,给Client应答
  • Client传送数据(Block=多个Packet),按串联通道以Packet为单位传输,DN先将Packet放在应答队列(内存)里等待应答,全部DN收到了才写入磁盘

1.2 读流程

image.png

  • Client向NN请求下载某目录下的某个文件
  • NN检查元数据,得到Block存放的DN地址
  • 就近选择一台DN读取Block,还是按Packet为单位接收,先存在本地缓存,再写入磁盘

1.3 NN与DN

image.png

  • DN启动后向NN注册
  • NN在元数据里写好DN注册成功
  • DN每隔6h向NN上报所有块信息,存在NN元数据里
  • DN每隔3s向NN心跳,返回NN给DN的指令
  • DN超过10min+30s未向NN心跳,则认为此DN不可用 DN数据完整性:
  • 当DN读取Block时,计算CheckSum,若与创建时不一致,则文件损坏,去其他DN上读取此Block
  • 常见校验算法:奇偶校验与crc校验一定程度能检测并纠正数据传输中的误码,但却不能防止对数据的恶意破坏。md5和sha1由于有“数据指纹”特征,可抗数据篡改,是目前应用最广泛的

1.4 NN与2NN

image.png

  • NN首次启动,创建Edits、Fsimage(检查点,相当于元数据副本);若非首次,直接加载两文件
  • Client请求增删改查
  • NN的Edits记录操作日志,并对元数据进行增删改查,把命令通过DN的心跳传给DN,让DN对Block进行增删改查
  • 2NN发现定时时间到/Edits文件满了,执行CheckPoint(NN内存运行不过来,帮NN更新元数据)
  • NN滚动生成Edits副本给2NN,并拷贝Fsimage到2NN
  • 2NN加载Edits和Fsimage并运行,结果生成新Fsimage,拷贝到NN中替换原来的Fsimage

2、MapReduce

2.1 流程

Input--->InputFormat--->Mapper--->Shuffle--->Reducer--->OutputFormat--->Output
  • InputFormat:选择TextInputFormat还是CombineTextInputFormat或其他。切片给Maper,1切片=1MapTask=1G+1CPU
  • Shuffle:环形缓冲区溢写前,先检查有几个ReduceTask,才溢写几个分区
    • Partitioner分几个区=分几个文件,默认分区是根据key的hashCode对ReduceTask个数求模
    • WritableComparable决定怎么排序;
    • Combiner对map结果先合并,减轻Reduce压力
  • OutputFormat:选择输出样式

2.2 InputFormat机制

2.2.1 FileInputFormat

  • 切片时不考虑数据集整体,而是每个文件单独切片,无论剩多少也是单独一切片==>很多文件剩余的小文件都成单独切片了。
  • 针对一些文件格式,FileInputFormat常见的实现类有:TextInputFormat、KeyValueTextInputFormat、 NLineInputFormat、CombineTextInputFormat 和自定义 InputFormat 等

2.2.2 TextInputFormat

  • TextInputFormat 是默认的 FileInputFormat 实现类。按行读取每条记录
  • 键是存储该行在整个文件中的起始字节偏移量, LongWritable 类型。值是这行的内容,不包括任何行终止符(换行符和回车符),Text 类型。
  • 所以写mapper时,extends Mapper<LongWritable,Text, 输出K,输出V>

2.2.3 CombineTextInputFormat

  • 用于小文件过多的场景,它可以将多个小文件从逻辑上规划到一个切片中,这样,多个小文件就可以交给一个 MapTask 处理,即采用虚拟存储过程+切片过程
  • 虚拟存储过程:假设文件<setMaxInputSplitSize,则暂时划分成一块(不是切片);setMaxInputSplitSize<文件<2*setMaxInputSplitSize,则对半划分两块。
  • 切片过程:虚拟存储文件块大小>=setMaxInputSplitSize,单独切片;虚拟存储文件块大小<=setMaxInputSplitSize,则跟下一个虚拟存储文件块合并,共同形成一个切片

2.3 Shuffle机制

image.png

2.3.1 Partition分区

默认HashPartitioner分区

  • 默认分区是根据key的hashCode对ReduceTask 个数 (分几个区/分几个文件,默认为1) 取模得到的。可以在driver里job.setNumReduceTasks修改默认值。但用户没法控制哪个key存储到哪个分区。
  • 如果分区数不是1,但是ReduceTask为1,是否执行分区过程。答案是:不执行分区过程。因为在MapTask的源码中,执行分区的前提是先判断ReduceNum个数是否大于1。不大于1肯定不执行。 自定义Partitioner分区

使用:xxx继承Partitioner;在getPartition()方法里写好控制分区逻辑代码(根据什么分区);再job驱动类里修改partition,即job.setPartitonerClass(xxx);设置numReduceTasks值(分几个区/文件),即job.setNumReduceTasks(x)

image.png

2.3.2 WritableComparable排序

排序是mapreduce框架最重要的操作之一。默认排序是字典排序,即快排 接口WritableComparable继承了Writable(序列/反序列)、Comparable

2.3.3 Combiner合并

Combiner组件的父类就是Reducer。Combiner的意义就是对每一个MapTask的输出进行局部汇总,以减小网络传输量,减轻Reduce压力。Combiner的输出kv应该跟Reducer的输入kv类型要对应起来。能用加减,不能用乘除,因为可能会失真
使用:

  • 方案一:自定义Combiner继承Reducer,重写Reduce方法,job里绑定
  • 方案二:由于Combiner业务代码跟Reduce一样,提前排序,减轻压力,所以可以不用写Combiner,直接job里设置为Reduce

2.4 OutPutFormat机制

  • 默认TextOutputFormat按行写出
  • 自定义OutputFormat(输出到Mysql/es/hbase等存储框架),使用步骤:
    • xxxRecordWriter继承RecordWriter,重写write方法(用流)、close方法(关流);写有参构造函数,参数为job,创造流
    • xxx OutputFormat继承FileOutputFormat,重写getRecordWriter方法,参数为job,job传给xxxRecordWriter并返回此对象
    • xxxDriver里job.setOutputFormatClass,设置文件输入输出路径

2.5 MapTask与ReduceTask

MapTask :并行度由切片个数决定,切片个数由输入文件和切片规则决定。 ReduceTask:并行度由ReduceTask数量决定

  • ReduceTask=0,表示没有Reduce阶段,输出文件个数和Map个数一致。
  • ReduceTask默认值就是1,所以输出文件个数为一个。
  • 如果数据分布不均匀,就有可能在Reduce阶段产生数据倾斜
  • ReduceTask数量并不是任意设置,还要考虑业务逻辑需求,有些情况下,需要计算全局汇总结果,就只能有1个ReduceTask。
  • 具体多少个ReduceTask,需要根据集群性能而定。
  • 如果分区数不是1,但是ReduceTask为1,是否执行分区过程。答案是:不执行分区过程。因为在MapTask的源码中,执行分区的前提是先判断ReduceNum个数是否大于1。不大于1肯定不执行。

2.6 数据压缩

输入端---> LZO(因为可切片),Mapper输出--->Snappy(不用切片了),Reducer输出--->永久用Bzip2\Gzip

3、Yarn

3.1 原理

image.png

3.2 调度器

  • RM中的资源调度器将资源分配给各个AM:资源分配过程是异步的。资源调度器将资源分配给一个应用程序后,它不会立刻push给对应的AM,而是暂时放到一个缓冲区中,等待AM通过周期性的心跳主动来取
  • Hadoop 作业调度器主要有三种:FIFO、容量(Capacity Scheduler)和公平(Fair Scheduler)。Apache Hadoop3.1.3 默认的资源调度器是 Capacity Scheduler;CDH 框架默认调度器是 Fair Scheduler

3.2.1 先进先出调度器FIFO

image.png

  • 单队列。多个job排队,有空余资源时,job先进先出。一个job有多个maptask\reducetask时,能出几个出几个,没出完的继续排队
  • 并不适用于共享集群,大的应用可能会占用所有集群资源,这就导致其它应用被阻塞。在共享集群中,更适合采用Capacity Scheduler或Fair Scheduler,这两个调度器都允许大任务和小任务在提交的同时获得一定的系统资源

3.2.2 容量调度器Capacity Scheduler

  • 多队列,每个队列采用FIFO策略
  • 有一个专门的队列用来运行小任务,但是为小任务专门设置一个队列会预先占用一定的集群资源(20%),这就导致大任务(80%)的执行时间会落后于使用FIFO调度器时的时间

3.2.3 公平调度器Fair Scheduler

  • 多队列,每个队列采用Fair策略
  • 当第一个大job提交时,只有这一个job在运行,此时它获得了所有集群资源;当第二个小任务提交后,Fair调度器会分配一半资源给这个小任务,让这两个任务公平的共享集群资源。公平调度器,就是能够共享整个集群的资源;不用预先占用资源,每一个作业都是共享的

Fair Scheduler与Capacity Scheduler区别

  • 资源公平共享:在每个队列中,Fair Scheduler可选择按照FIFO、Fair或DRF策略为应用程序分配资源。Fair策略即平均分配,默认情况下,每个队列采用该方式分配资源
  • 支持资源抢占:当某个队列中有剩余资源时,调度器会将这些资源共享给其他队列,而当该队列中有新的应用程序提交时,调度器要为它回收资源。为了尽可能降低不必要的计算浪费,调度器采用了先等待再强制回收的策略,即如果等待一段时间后尚有未归还的资源,则会进行资源抢占;从那些超额使用资源的队列中杀死一部分任务,进而释放资源
  • 负载均衡:Fair Scheduler提供了一个基于任务数的负载均衡机制,该机制尽可能将系统中的任务均匀分配到各个节点上。此外,用户也可以根据自己的需求设计负载均衡机制
  • 调度策略灵活配置:Fiar Scheduler允许管理员为每个队列单独设置调度策略(当前支持FIFO、Fair或DRF三种)
  • 提高小应用程序响应时间:由于采用了最大最小公平算法,小作业可以快速获取资源并运行完成

4、整个流程

看到这里,用大白话帮你把所有的内容都串联一遍
  • Client向Yarn-RM请求运行MR,Yarn-RM让Client上传资源到HDFS
  • Client请求HDFS-NN上传某文件,HDFS-NN检查权限与目录是否存在,给Client三台DN的地址(机架感知),Client与三台DN建立通信通道,三台DN依次串联好后让Client传过来,Client将文件分多个Block单元以多个Packet传送,DN先存在缓冲队列里,等三台DN都存好了,刷写到磁盘,然后让Client继续传; 如果Client是要读取文件,就向HDFS-NN请求读取文件,NN查找元数据里Block信息,告诉Client就近的一台DN里读取,DN计算Block的ClickSum,若于存入时不一致,则不能用,Client读取其他DN里的Block
  • DN启动会去NN元数据注册,每隔6h就把自己Block信息全部告诉给NN,NN更新元数据;DN每隔3s心跳,并带回NN的命令;若DN超过10min+30s无心跳,则认为此DN挂掉
  • NN的元数据里每个Block占150byte,Client对数据进行操作时,NN更新元数据的同时会在Edits里更新日志,Fsimage里存检查点;当2NN发现检查点时间到了或者Edits满了,就启动检查,滚动Edits复制到2NN,拷贝Fsimage到2NN,在2NN内存里操作更新Fsimage,再传给NN,覆盖NN的Fsimage
  • Client传完数据了,Yarn-RM将job存在队列里,调度器有FIFO/容量/公平调度器。某个NM领取到job,启动容器加载HDFS资源,运行MRAppMaster,如果有空的NM,MRAppMaster向RM申请用它,运行一个Task
  • MT中,数据选择TextInputFormat还是CombineInputFormat读取,每个文件一个切片,在环形缓冲区中达到80%就按ReduceTask个数采取分区数,要么指定分区规则,要么按Key哈希RT个数分区,分区中完成一次Combiner落盘,一个MT环形缓冲区多次溢写就有多个分区落盘文件,按分区进行一次归并排序,再Combiner落盘,生成一个大的分区落盘文件;其他MT最后也生成各自的大分区落盘文件。
  • RT中,一个RT将各MT落盘文件一个相同分区数据先放缓存里,放不下就放磁盘里,然后对这些分区数据进行一次归并排序,再进行reduce方法
  • 选择压缩,最开始HDFS里可选LZO+索引,MT完成落盘时可选Snappy,RT完成可选Gzip

5、生产调优

5.1 小文件

  • 小文件弊端:每个小文件=1Block=NN内存元数据存150byte=寻址索引慢 每个小文件一个切片==一个MapTask
  • 小文件解决方案:
    • 采集==》采集时将小文件合成大文件再上传HDFS
    • 存储==》Hadoop Archive存储工具,将多个小文件打包成HAR文件
    • 计算==》CombineTextInputFormat;JVM重用。默认一个Task开启一个JVM,但若一个Job中多个Task 都很小,就可以都运行在一个JVM

5.2 HDFS

  • NN:NN与DN内存都是自动分配且相等,不合理;NN线程池处理DN并发心跳、客户端并发的元数据操作,默认10
  • DN:存储可采用纠删码,3个副本(3300M)化成5个单元(3100M数据单元+2*100M纠删单元)

5.3 MR

  • MT:减少溢写次数(增大环形缓冲区大小、溢写阈值);自定义分区条件,减少数据倾斜;采用Combiner;增加合并次数;落盘采用Snappy/LZO压缩;增加MT失败重试次数;增加MT内存、CPU核数
  • RT:去Map拉取数据并行数;缓冲区大小、比例;增加RT失败重试次数;增加RT内存、CPU核数;如果可以不用Reduce,尽量不用
  • 数据倾斜:map去重空值,conbiner,mapjoin,增加reduce个数

5.4 Yarn

  • RM:增加线程池数量;配置调配器
  • NM:增加内存、CPU;关闭内存检查限制Container
  • Container:最大/小内存/核数