Hadoop知识点总结

什么是Hadoop

Hadoop是一个由Apache基金会所开发的分布式系统基础架构。主要解决,海量数据的存储和海量数据的分析计算问题。广义上来说,Hadoop通常是指一个更广泛的概念——Hadoop生态圈

Hadoop常用的端口号

HDFS写数据流程

(1)HDFS客户端通过Distributed FileSystem,向NameNode请求上传文件,NameNode检查目标文件是否已存在和父目录是否存在。

(2)NameNode返回是否可以上传。

(3)如果响应可以上传,客户端(切块)请求第一个 Block上传到哪几个DataNode服务器上。

(4)NameNode返回多个DataNode节点,假设分别为dn1、dn2、dn3(机架感知)。

(5)客户端通过FSDataOutputStream数据流向dn1请求建立Block传输通道,dn1收到请求继续先dn2请求建立通道,dn2收到请求继续向dn3请求建立通道。

(6)dn3,dn2,dn1逐级应答回客户端。

(7)通道建立成功,客户端开始往dn1上传第一个Block以Packet为单位,dn1收到第一个Packet后一部分往磁盘上写,另一部分放到内存中传给dn2,dn2传给dn3。客户端每传一个Packet会放入一个ack应答队列等待应答,逐级应答成功后删除缓存。

(8)当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。

HDFS读数据流程

(1)客户端通过 DistributedFileSystem 向 NameNode 请求下载文件,NameNode 通过查询元数据,找到文件块所在的 DataNode 地址并返回。

(2)客户端通过FSDataInputStream数据流,请求读取数据。这里首先选择节点距离最近的一台 DataNode服务器(结合负载能力),然后随机选择。

(3)DataNode 开始传输数据给客户端(从磁盘里面读取数据输入流,以 Packet 为单位来做校验)。

(4)客户端以 Packet 为单位接收,先在本地缓存,拼接后写入目标文件。

NN和2NN工作机制

 第一阶段:NameNode启动

(1)第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存(edits_inprogress_001、fsimage合并)。

(2)客户端对元数据进行增删改的请求。

(3)NameNode记录操作日志,更新滚动日志(先记录到磁盘,防宕机)。

(4)NameNode在内存中对元数据进行增删改。

第二阶段:Secondary NameNode工作

(1)Secondary NameNode询问NameNode是否需要CheckPoint(定时时间到,edits数据满了)。直接带回NameNode是否检查结果。

(2)Secondary NameNode请求执行CheckPoint。

(3)NameNode滚动正在写的Edits日志(生成新的edits_inprogress_002用来记录接下来进行的元数据操作,edits_inprogress_001改名为edits_001)。

(4)将滚动前的编辑日志和镜像文件(edits_001、fsimage)拷贝到Secondary NameNode。

(5)Secondary NameNode加载编辑日志和镜像文件到内存,并合并。

(6)生成新的镜像文件fsimage.chkpoint。

(7)拷贝fsimage.chkpoint到NameNode。

(8)NameNode将fsimage.chkpoint重新命名成fsimage。

DataNode与NameNode工作机制

(1)一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。

(2)DataNode启动后向NameNode注册,通过后,周期性(6小时)地向NameNode上报所有的块信息。

(3)心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟+30秒没有收到某个DataNode的心跳,则认为该节点不可用。

(4)集群运行中可以安全加入和退出一些机器。

MapReduce工作流程

MapTask工作流

(1)Read 阶段:MapTask 通过 InputFormat 获得的 RecordReader(RecordReader表示以怎样的方式从分片中读取一条记录,每读取一条记录都会调用RecordReader类,系统默认的RecordReader是LineRecordReader),从输入 InputSplit 中 解析出一个个 key/value。

(2)Map 阶段:该节点主要是将解析出的 key/value 交给用户编写 map()函数处理,并 产生一系列新的 key/value。

(3)Collect 收集阶段:在用户编写 map()函数中,当数据处理完成后,一般会调用 OutputCollector.collect()输出结果。在该函数内部,它会将生成的 key/value 分区(调用 Partitioner),并写入一个环形内存缓冲区中。

(4)Spill 阶段:即“溢写”,当环形缓冲区满后,MapReduce 会将数据写到本地磁盘上, 生成一个临时文件。需要注意的是,将数据写入本地磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行合并、压缩等操作。

溢写阶段详情:

 1:利用快速排序算法对缓存区内的数据进行排序,排序方式是,先按照分区编号 Partition 进行排序,然后按照 key 进行排序。这样,经过排序后,数据以分区为单位聚集在 一起,且同一分区内所有数据按照 key 有序。

 2:按照分区编号由小到大依次将每个分区中的数据写入任务工作目录下的临时文 件 output/spillN.out(N 表示当前溢写次数)中。如果用户设置了 Combiner,则写入文件之 前,对每个分区中的数据进行一次聚集操作。

 3:将分区数据的元信息写到内存索引数据结构 SpillRecord 中,其中每个分区的元 信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小。如果当前内存索引大 小超过 1MB,则将内存索引写到文件 output/spillN.out.index 中。

(5)Merge 阶段:当所有数据处理完成后,MapTask 对所有临时文件进行一次合并, 以确保最终只会生成一个数据文件。 当所有数据处理完后,MapTask 会将所有临时文件合并成一个大文件,并保存到文件output/file.out 中,同时生成相应的索引文件 output/file.out.index。 在进行文件合并过程中,MapTask 以分区为单位进行合并。对于某个分区,它将采用多 轮递归合并的方式。每轮合并 mapreduce.task.io.sort.factor(默认10)个文件,并将产生的文 件重新加入待合并列表中,对文件排序后,重复以上过程,直到最终得到一个大文件。 让每个 MapTask 最终只生成一个数据文件,可避免同时打开大量文件和同时读取大量 小文件产生的随机读取带来的开销。

MapTask工作流优化

1)自定义分区,减少数据倾斜

定义类,继承Partitioner接口,重写getPartition方法

2)减少溢写的次数

mapreduce.task.io.sort.mb Shuffle的环形缓冲区大小,默认100m,可以提高到200m mapreduce.map.sort.spill.percent 环形缓冲区溢出的阈值,默认80% ,可以提高的90%

3)增加每次Merge合并次数

mapreduce.task.io.sort.factor默认10,可以提高到20

4)在不影响业务结果的前提条件下可以提前采用Combiner

job.setCombinerClass(xxxReducer.class);

5)为了减少磁盘IO,可以采用Snappy或者LZO压缩

conf.setBoolean("mapreduce.map.output.compress", true); conf.setClass("mapreduce.map.output.compress.codec",SnappyCodec.class,CompressionCodec.class);

6)mapreduce.map.memory.mb 默认MapTask内存上限1024MB。 可以根据128m数据对应1G内存原则提高该内存。

7)mapreduce.map.java.opts:控制MapTask堆内存大小。(如果内存不够, 报:java.lang.OutOfMemoryError)

8)mapreduce.map.cpu.vcores 默认MapTask的CPU核数1。计算密集型任 务可以增加CPU核数

9)异常重试 mapreduce.map.maxattempts每个Map Task最大重试次数,一旦重试 次数超过该值,则认为Map Task运行失败,默认值:4。根据机器 性能适当提高。

ReduceTask工作流

(1)Copy 阶段:ReduceTask 从各个 MapTask 上远程拷贝一片数据,并针对某一片数 据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。

(2)Sort 阶段:在远程拷贝数据的同时,ReduceTask 启动了两个后台线程对内存和磁 盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。按照 MapReduce 语义,用 户编写 reduce()函数输入数据是按 key 进行聚集的一组数据。为了将 key 相同的数据聚在一 起,Hadoop 采用了基于排序的策略。由于各个 MapTask 已经实现对自己的处理结果进行了 局部排序,因此,ReduceTask 只需对所有数据进行一次归并排序即可。

(3)Reduce 阶段:reduce()函数将计算结果写到 HDFS 上(传入reduce的kv使用了对象重用)。

ReduceTask工作流优化

1)mapreduce.reduce.shuffle.parallelcopies每个Reduce去Map 中拉取数据的并行数,默认值是5。可以提高到10。

2)mapreduce.reduce.shuffle.input.buffer.percent Buffer大小占Reduce可用内存的比例,默认值0.7。可以提高到0.8

3)mapreduce.reduce.shuffle.merge.percent Buffer中的数据达到多少比例 开始写入磁盘,默认值0.66。可以提高到0.75

4)mapreduce.reduce.memory.mb 默认ReduceTask内存上限1024MB, 根据128m数据对应1G内存原则,适当提高内存到4-6G

5)mapreduce.reduce.java.opts:控制ReduceTask堆内存大小。(如果内 存不够,报:java.lang.OutOfMemoryError)

6)mapreduce.reduce.cpu.vcores默认ReduceTask的CPU核数1个。可 以提高到2-4个

7)mapreduce.reduce.maxattempts每个Reduce Task最大重试次数, 一旦重试次数超过该值,则认为Map Task运行失败,默认值:4。

8)mapreduce.job.reduce.slowstart.completedmaps当MapTask完成的比 例达到该值后才会为ReduceTask申请资源。默认是0.05。

9)mapreduce.task.timeout如果一个Task在一定时间内没有任何进入, 即不会读取新的数据,也没有输出数据,则认为该Task处于Block状态, 可能是卡住了,也许永远会卡住,为了防止因为用户程序永远Block住 不退出,则强制设置了一个该超时时间(单位毫秒),默认是600000 (10分钟)。如果你的程序对每条输入数据的处理时间过长,建议将 该参数调大。

10)如果可以不用Reduce,尽可能不用

Shuffle详细过程

(1)Map方法之后Reduce方法之前这段处理过程叫「Shuffle」

(2)Map方法之后,数据首先进入到分区方法,把数据标记好分区,然后把数据发送到环形缓冲区;环形缓冲区默认大小100m,环形缓冲区达到80%时,进行溢写(溢写的同时数据反向写入环形缓冲区);溢写前对数据进行排序,排序按照对key的索引进行字典顺序排序,排序的手段「快排」;溢写产生大量溢写文件,需要对溢写文件进行「归并排序」;对溢写的文件也可以进行Combiner操作,前提是汇总操作,求平均值不行。最后将文件按照分区存储到磁盘,等待Reduce端拉取。

(3)每个Reduce拉取Map端对应分区的数据。拉取数据后先存储到内存中,内存不够了,再存储到磁盘。拉取完所有数据后,采用[归并排序]将内存和磁盘中的数据都进行排序。在进入Reduce方法前,可以对数据进行分组操作。

Yarn工作机制(作业提交全过程)

 作业提交全过程详解

(1)作业提交

第 1 步:Client 调用 job.waitForCompletion 方法,向整个集群提交 MapReduce 作业。

第 2 步:Client 向 RM 申请一个作业 id。

第 3 步:RM 给 Client 返回该 job 资源的提交路径和作业 id。

第 4 步:Client 提交 jar 包、切片信息和配置文件到指定的资源提交路径。

第 5 步:Client 提交完资源后,向 RM 申请运行 MrAppMaster。

(2)作业初始化

第 6 步:当 RM 收到 Client 的请求后,将该 job 添加到容量调度器中。

第 7 步:某一个空闲的 NM 领取到该 Job。

第 8 步:该 NM 创建 Container,并产生 MRAppmaster。

第 9 步:下载 Client 提交的资源到本地。

(3)任务分配

第 10 步:MrAppMaster 向 RM 申请运行多个 MapTask 任务资源。

第 11 步:RM 将运行 MapTask 任务分配给另外两个 NodeManager,另两个 NodeManager 分别领取任务并创建容器。

(4)任务运行

第 12 步:MR 向两个接收到任务的 NodeManager 发送程序启动脚本,这两个 NodeManager 分别启动 MapTask,MapTask 对数据分区排序。

第 13 步:MrAppMaster等待所有MapTask运行完毕后,向RM申请容器,运行ReduceTask。

第 14 步:ReduceTask 向 MapTask 获取相应分区的数据。

第 15 步:程序运行完毕后,MR 会向 RM 申请注销自己。

(5)进度和状态更新

YARN 中的任务将其进度和状态(包括 counter)返回给应用管理器, 客户端每秒(通过 mapreduce.client.progressmonitor.pollinterval 设置)向应用管理器请求进度更新, 展示给用户。

(6)作业完成

除了向应用管理器请求作业进度外, 客户端每 5 秒都会通过调用 waitForCompletion()来 检查作业是否完成。时间间隔可以通过 mapreduce.client.completion.pollinterval 来设置。作业 完成之后, 应用管理器和 Container 会清理工作状态。作业的信息会被作业历史服务器存储 以备之后用户核查。

Yarn调度器

先进先出调度器(FIFO)

单队列,根据提交作业的先后顺序,先来先服务。

容量调度器(Capacity Scheduler)

多队列:每个队列可配置一定的资源量,每个队列采用FIFO调度策略。

容量保证:管理员可为每个队列设置资源最低保证和资源使用上限

灵活性:如果一个队列中的资源有剩余,可以暂时共享给那些需要资源的队列,而一旦该队列有新的应用 程序提交,则其他队列借调的资源会归还给该队列。

多租户:支持多用户共享集群和多应用程序同时运行。 为了防止同一个用户的作业独占队列中的资源,该调度器会对同一用户提交的作业所占资源量进行限定

1)队列资源分配

从root开始,使用深度优先算法,优先选择资源占用率最低的队列分配资源。

2)作业资源分配

默认按照提交作业的优先级和提交时间顺序分配资源。

3)容器资源分配

按照容器的优先级分配资源; 如果优先级相同,按照数据本地性原则:

(1)任务和数据在同一节点 (2)任务和数据在同一机架 (3)任务和数据不在同一节点也不在同一机架

公平调度器(Fair Scheduler)

与容量调度器相同点:

多队列、容量保证、灵活性、多租户

与容量调度器不同点:

(1)核心调度策略不同

容量调度器:优先选择资源利用率低的队列 公平调度器:优先选择对资源的缺额比例大的

(2)每个队列可以单独设置资源分配方式

容量调度器:FIFO、 DRF  公平调度器:FIFO、FAIR、DRF

(3)并行度

容量调度器:同一个队列同时间只有一个任务在运行  公 平调度器:同个队列同一时间有多个任务在运行

Yarn优化

(1)Resourcemanager 相关

yarn.resourcemanager.scheduler.client.thread-count   ResourceManager 处理调度 器请求的线程数量

yarn.resourcemanager.scheduler.class  配置调度器

(2)Nodemanager 相关

yarn.nodemanager.resource.memory-mb NodeManager  使用内存数

yarn.nodemanager.resource.system-reserved-memory-mb NodeManager  为系统保留 多少内存,和上一个参数二者取一即可

yarn.nodemanager.resource.cpu-vcores NodeManager   使用 CPU 核数

yarn.nodemanager.resource.count-logical-processors-as-cores  是否将虚拟核 数当作 CPU 核数

yarn.nodemanager.resource.pcores-vcores-multiplier 虚拟核数和物理核数乘数,例 如:4 核 8 线程,该参数就应设为 2

yarn.nodemanager.resource.detect-hardware-capabilities 是否让 yarn 自己检测硬 件进行配置

yarn.nodemanager.pmem-check-enabled 是否开启物理内存检查限制

container yarn.nodemanager.vmem-check-enabled 是否开启虚拟内存检查限制

container yarn.nodemanager.vmem-pmem-ratio 虚拟内存物理内存比例

(3)Container 容器相关

yarn.scheduler.minimum-allocation-mb 容器最小内存

yarn.scheduler.maximum-allocation-mb 容器最大内存

yarn.scheduler.minimum-allocation-vcores 容器最小核数

yarn.scheduler.maximum-allocation-vcores 容器最大核数

小文件

HDFS 上每个文件都要在 NameNode 上创建对应的元数据,这个元数据的大小约为 150byte,这样当小文件比较多的时候,就会产生很多的元数据文件,一方面会大量占用 NameNode 的内存空间,另一方面就是元数据文件过多,使得寻址索引速度变慢。

小文件过多,在进行 MR 计算时,会生成过多切片,需要启动过多的 MapTask。每个 MapTask 处理的数据量小,导致 MapTask 的处理时间比启动时间还小,白白消耗资源。

解决方案

1)在数据采集的时候,就将小文件或小批数据合成大文件再上传 HDFS(存储方向)

sequence file由一系列的二进制key/value组成,如果为key小文件名,value为文件内容,则可以将大 批小文件合并成一个大文件。

2)Hadoop Archive(存储方向)

是一个高效的将小文件放入 HDFS 块中的文件存档工具,能够将多个小文件打包成一 个 HAR 文件,从而达到减少 NameNode 的内存使用

3)CombineTextInputFormat(计算方向)

CombineTextInputFormat 用于将多个小文件在切片过程中生成一个单独的切片或者少 量的切片。

4)开启 uber 模式,实现 JVM 重用(计算方向)

默认情况下,每个 Task 任务都需要启动一个 JVM 来运行,如果 Task 任务计算的数据 量很小,我们可以让同一个 Job 的多个 Task 运行在一个 JVM 中,不必为每个 Task 都开启 一个 JVM。

数据倾斜

1)数据倾斜现象

数据频率倾斜——某一个区域的数据量要远远大于其他区域。

数据大小倾斜——部分记录的大小远远大于平均值。

2)减少数据倾斜的方法

(1)首先检查是否空值过多造成的数据倾斜生产环境,可以直接过滤掉空值;如果想保留空值,就自定义分区,将空值加随机数打散。最后再二次聚合。

(2)能在 map 阶段提前处理,最好先在 Map 阶段处理。如:Combiner、MapJoin

(3)设置多个 reduce 个数

存储优化

1)纠删码

纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k 策略的支持,如要使用别的策略需要提前启用。

RS-6-3-1024k:使用 RS 编码,每 6 个数据单元,生成 3 个校验单元,共 9 个单元,也 就是说:这 9 个单元中,只要有任意的 6 个单元存在(不管是数据单元还是校验单元,只要 总数=6),就可以得到原始数据。每个单元的大小是 1024k=1024*1024=1048576。

2)异构存储(冷热数据分离)

所谓的异构存储就是将不同需求或者冷热的数据存储到不同的介质中去,实现既能兼顾性能又能兼顾成 本。

HA

概述

(1)所谓 HA(High Availablity),即高可用(7*24 小时不中断服务)。

(2)实现高可用最关键的策略是消除单点故障。HA 严格来说应该分成各个组件的 HA 机制:HDFS 的 HA 和 YARN 的 HA。

(3)NameNode 主要在以下两个方面影响 HDFS 集群

➢ NameNode 机器发生意外,如宕机,集群将无法使用,直到管理员重启

➢ NameNode 机器需要升级,包括软件、硬件升级,此时集群也将无法使用

HDFS HA 功能通过配置多个 NameNodes(Active/Standby)实现在集群中对 NameNode 的 热备来解决上述问题。如果出现故障,如机器崩溃或机器需要升级维护,这时可通过此种方 式将 NameNode 很快的切换到另外一台机器。

核心问题

1)怎么保证三台 namenode 的数据一致

a.Fsimage:让一台 nn 生成数据,让其他机器 nn 同步

b.Edits:需要引进新的模块 JournalNode 来保证 edtis 的文件的数据一致性

2)怎么让同时只有一台 nn 是 active,其他所有是 standby 的

a.手动分配

b.自动分配

3)2nn 在 ha 架构中并不存在,定期合并 fsimage 和 edtis 的活谁来干

由 standby 的 nn 来干

4)如果 nn 真的发生了问题,怎么让其他的 nn 上位干活

a.手动故障转移

b.自动故障转移

手动模式

配置 core-site.xml:

把多个 NameNode 的地址组装成一个集群 mycluster

配置 hdfs-site.xml:

JournalNode 数据存储目录

指定 NameNode 元数据在 JournalNode 上的存放位置

完全分布式集群名称

集群中 NameNode 节点都有哪些

NameNode 的 RPC 通信地址

NameNode 的 http 通信地址

访问代理类:client 用于确定哪个 NameNode 为 Active

配置隔离机制,即同一时刻只能有一台服务器对外响应

使用隔离机制时需要 ssh 秘钥登录

启动 HDFS-HA 集群,手动选择Active

hdfs haadmin -transitionToActive nn1

此时,改变Active需要全部NameNode启动并能够相互通信,防止脑裂

自动模式

自动故障转移为 HDFS 部署增加了两个新组件:ZooKeeper 和 ZKFailoverController (ZKFC)进程,如图所示。ZooKeeper 是维护少量协调数据,通知客户端这些数据的改变 和监视客户端故障的高可用服务。

1)zkfc就是zookeeper的客户端。zkfc要和zookeeper集群保持长连接,心跳;

2)在集群启动的时候,两个NameNode均处于standby状态。

3)两个NameNode的zkfc要向zookeeper集群抢占一个临时节点,该临时节点保存了主NameNode的信息,哪个抢先zkfc创建成功,则该zkfc所在的NameNode就成为activeNameNode;备份的NameNode上的zkfc监控主NameNode创建的临时节点。

4)一旦主NameNode宕机,zkfc不能和zookeeper集群保持心跳链接,临时节点消失。

5)备份NameNode上的zkfc要向zookeeper集群抢占临时节点,如果抢占成功,备份NameNode将变成主NameNode。

在 hdfs-site.xml 中增加:启用 nn 故障自动转移

在 core-site.xml 文件中增加:指定 zkfc 要连接的 zkServer 地址

YARN-HA

工作机制

核心问题

a .如果当前 active rm 挂了,其他 rm 怎么将其他 standby rm 上位

核心原理跟 hdfs 一样,利用了 zk 的临时节点

b. 当前 rm 上有很多的计算程序在等待运行,其他的 rm 怎么将这些程序接手过来接着跑

rm 会将当前的所有计算程序的状态存储在 zk 中,其他 rm 上位后会去读取,然后接着跑

 

posted @ 2022-03-08 09:07  1243741754  阅读(325)  评论(0编辑  收藏  举报