大数据1.0

Hadoop框架

hadoop是什么?

​ Hadoop是由Apache基金开发的分布式系统基础架构。

​ Hadoop主要解决的是海量数据的存储和分析计算问题

​ 广义上Hadoop经常指代Hadoop生态圈

Hadoop 三大发行版本

Apache:基础版本

Cloudera:内部集成了很多大数据框架,对应产品时CDH

Hortonworks:文档较好。对应产品HDP

Hadoop的优势
高可靠性 :

​ Hadoop底层维护了多个数据副本,所以即使Hadoop某个计算元素或者存储出现了故障也不会导致数据丢失

高效性:

​ 在集群间分配任务数据,可方便扩展数以千计的节点。

高扩展性:

​ 在MapReduce的思想下Hadoop是并行工作的,以加快任务的处理速度。

高容错性:

​ 能够自动将失败的任务重新分配。

Hadoop的组成
Hadoop1.x和Hadoop2.x间的区别
组成部分

Hadoop1.x Hadoop2.x

MapReduce(计算+资源调度) MapReduce(计算)

​ Yarn(资源调度)

hdfs(数据存储) HDFS(数据存储)

Common(辅助工具) Common(辅助工具)

Hadoop1.x阶段Hadoop中的Mapeduce阶段同时处理业务逻辑和资源调度,耦合性较大。
Hadoop2.x阶段将运算和资源调度分开,新增加了yarn,Yarn仅负责资源调度,MapReduce仅负责运算。

HDFS架构
组成概述
NameNode(nn):

​ NameNode简称nn,存储文件的元数据,如文件名,文件目录,文件属性(生成时间,副本数,文件权限),以及每个文件的块列表和块所在的DataNode等。

DataNode(dn):

​ 在本地文件系统存储文件块数据,以及块数据的校验和。

Secondary NameNnode(2nn):

​ 每隔一段时间对NameNode元数据备份。

MapReduce架构
map阶段

​ 并行处理输入数据。

reduce阶段

​ Reduce阶段对Map阶段的结果进行汇中。

YARN架构
分为以下四部分

ResourceManager,NodeManager,ApplicationMaster,Container

ResourceManager(RM)主要作用

​ 处理客户端请求。

​ 监控NodeManager

​ 启动或者监控ApplicationMaster

​ 资源的分配与调度

NodeManager(NM)主要作用

​ 管理每个节点上的资源

​ 处理来自ResourceManager的命令

​ 处理来自ApplicationMaster的命令

ApplicationMater(AM)作用

​ 负责数据的切割

​ 为应用程序申请资源并分配给内部的任务

​ 任务的监控与容错

image-20210719084605167
Container

​ container是Yarn中的资源抽象,它封装了某个节点上的多维度资源。

image-20210802140615198

Hadoop环境搭建

安装虚拟机(Vmware,CentOS7.x最小安装)

单台虚拟机安装文件

sudo yum install -y epel-release
sudo yum install -y psmisc nc net-tools rsync vim lrzsz ntp libzstd openssl-static tree iotop git

关闭防火墙

 sudo systemctl stop firewalld
 sudo systemctl disable firewalld

设置静态IP

vim /etc/sysconfig/network-scripts/ifcfg-ens33
DEVICE=ens33
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=static
NAME="ens33"
IPADDR=192.168.1.101			#IP地址
PREFIX=24						
GATEWAY=192.168.1.2				#网关
DNS1=192.168.1.2				#域名

设置主机映射

192.168.1.100 hadoop100
192.168.1.101 hadoop101
192.168.1.102 hadoop102
192.168.1.103 hadoop103
192.168.1.104 hadoop104
192.168.1.105 hadoop105
192.168.1.106 hadoop106
192.168.1.107 hadoop107

window下设置主机映射

C:\Windows\System32\drivers\etc

hosts文件

新建用户设置修改权限

sudo useradd 用户名
sudo passwd  密码

vim /etc/sudoers	#将用户添加root权限
## Allow root to run any commands anywhere
root ALL=(ALL) ALL
yin ALL=(ALL) ALL

/opt下创建文件夹并修改权限

sudo mkdir module
sudo mkdir software

sudo chown yin:yin /opt/module /opt/software

配置java和Hadoop

rpm -qa | grep -i java | xargs -n1 sudo rpm -e --nodeps		#卸载现有jdk
tar -zxvf jdk-8u212-linux-x64.tar.gz -C /opt/module/
java -version
tar -zxvf hadoop-3.1.3.tar.gz -C /opt/module/
hadop version

添加个人权限

sudo vim /etc/profile.d/my_env.sh		#新建my_enc.sh该文件中放置自己的软件权限

#JAVA_HOME
export JAVA_HOME=/opt/module/jdk1.8.0_212
export PATH=$PATH:$JAVA_HOME/bin
#HADOOP_HOME 
export HADOOP_HOME=/opt/module/hadoop-3.1.3 
export PATH=$PATH:$HADOOP_HOME/bin 
export PATH=$PATH:$HADOOP_HOME/sbin
source /etc/profile						#让变量生效

复制两台虚拟机

复制过后的虚拟机已经按照java和Hadoop此时仅需要修改主机名和ip地址,完成以后的任务

修改主机名

vim  /etc/hostname

修改ip

vim /etc/sysconfig/network-scripts/ifcfg-ens33
完成完全分布式

修改第一台服务器上的Hadoop中的配置文件

注意

**NameNode(HDFS)和SecondaryNameNode(HDFS)**不要放到同一台服务器上。

**ResourceManager(YARN)**也十分消耗内存,

不要和**NameNode(HDFS)和SecondaryNameNode(HDFS)**配置在同一台服务器上

核心配置文件
cd $HADOOP_HOME/etc/hadoop

vim core-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
	<configuration>
	<!-- 指定 NameNode 的地址 -->
		<property>
			<name>fs.defaultFS</name>
			<value>hdfs://hadoop102:8020</value>
		</property>
	<!-- 指定 hadoop 数据的存储目录 -->
		<property>
			<name>hadoop.tmp.dir</name>
			<value>/opt/module/hadoop-3.1.3/data</value>
		</property>
	<!-- 配置 HDFS 网页登录使用的静态用户为 yin -->
		<property>
			<name>hadoop.http.staticuser.user</name>
			<value>yin</value>
		</property>
</configuration>

vim hdfs-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
	<configuration>
	<!-- nn web 端访问地址-->
		<property>
 			<name>dfs.namenode.http-address</name>
		 	<value>hadoop102:9870</value>
 		</property>
	<!-- 2nn web 端访问地址-->
 		<property>
 			<name>dfs.namenode.secondary.http-address</name>
 			<value>hadoop104:9868</value>
 		</property>
	</configuration>

vim yarn-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
	<configuration>
 	<!-- 指定 MR 走 shuffle -->
 		<property>
 			<name>yarn.nodemanager.aux-services</name>
 			<value>mapreduce_shuffle</value>
 		</property>
 	<!-- 指定 ResourceManager 的地址-->
 		<property>
 			<name>yarn.resourcemanager.hostname</name>
 			<value>hadoop103</value>
 		</property>
 	<!-- 环境变量的继承 -->
 		<property>
 			<name>yarn.nodemanager.env-whitelist</name>							<value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value>
 		</property>
	</configuration>

vim mapred-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
	<configuration>
	<!-- 指定 MapReduce 程序运行在 Yarn 上 -->
 		<property>
 			<name>mapreduce.framework.name</name>
 			<value>yarn</value>
 		</property>
	</configuration>

使用scp命令将配置文件发放到各个服务器上

scp -r /opt/module/jdk1.8.0_212 yin@hadoop103:/opt/module 
#在hadoop102上,将 hadoop102 中/opt/module/jdk1.8.0_212 目录拷贝到hadoop103 上。
 scp -r yin@hadoop102:/opt/module/hadoop-3.1.3 /opt/module/ 
#在hadoop103上,将 hadoop102中/opt/module/hadoop-3.1.3目录拷贝到hadoop103 上。
 scp -r yin@hadoop102:/opt/module/* yin@hadoop104:/opt/module
#在hadoop103上操作,将hadoop102中/opt/module 目录下所有目录拷贝到hadoop104 上。
配置workers

vim /opt/module/hadoop- 3.1.3/etc/hadoop/workers

hadoop102
hadoop103
hadoop104

该文件不允许有空格,空行

经过分发命令后生成/home/yin/.ssh/文件夹

文件夹功能解释

known_hosts 记录 ssh 访问过计算机的公钥(public key)

id_rsa 生成的私钥

id_rsa.pub 生成的公钥

authorized_keys 存放授权过的无密登录服务器公钥

设置无秘登录

ssh #服务器名(可能会输入密码),把三个服务器都设置一遍。如果进行过上面朝左以后可以跳过该步骤。
yes
exit
#进入/home/yin/.ssh文件夹中
ssh-keygen -t rsa				#生成公钥私钥,将生成的公钥分发到其他服务器
ssh-copy-id hadoop102
ssh-copy-id hadoop103 
ssh-copy-id hadoop104

#此四行代码每个服务器均需要经行共12遍,102节点除普通用户以外root用户也需要执行该命令

制作分发脚本

在/bin/yin/bin文件下制作自己的脚本

vim xsync
#!/bin/bash
#1. 判断参数个数
if [ $# -lt 1 ]
then
echo Not Enough Arguement!
exit;
fi

#2. 遍历集群所有机器
for host in hadoop102 hadoop103 hadoop104
do
 echo ==================== $host ====================
 #3. 遍历所有目录,挨个发送
 for file in $@
 do
 #4. 判断文件是否存在
 if [ -e $file ]
 then
 #5. 获取父目录
 pdir=$(cd -P $(dirname $file); pwd)
 #6. 获取当前文件的名称
 fname=$(basename $file)
 ssh $host "mkdir -p $pdir"
 rsync -av $pdir/$fname $host:$pdir
 else
 echo $file does not exists!
 fi
 done
done

#================结束,脚本仅上面部分===============
#修改脚本权限
chmod +x xsync
#将脚本复制到/bin目录中以便全局调用
5
#同步环境变量配置
sudo /home/yin/bin/xsync /etc/profile.d/my_env.sh
#让环境变量生效(需要在每个服务器上都配置一遍)
source /etc/profile

启动集群

先格式化集群hdfs namenode -format关闭所有namenode和datanode进程,在删除所有Hadoop文件夹加下的log和data文件夹

sbin/start-dfs.sh		#启动HDFS
sbin/start-yarn.sh		#启动yarn(在配置ResourceManager的节点上运行)

Web 端查看 HDFS 的 NameNode

浏览器中输入:http://hadoop102:9870

查看 HDFS 上存储的数据信息

Web 端查看 YARN 的 ResourceManager**

浏览器中输入:http://hadoop103:8088

(b)查看 YARN 上运行的 Job 信息

集群基本测试
#**上传小文件**
[yin@hadoop102 ~]$ hadoop fs -mkdir /input
[yin@hadoop102 ~]$ hadoop fs -put $HADOOP_HOME/wcinput/word.txt /input
#**上传大文件**
[yin@hadoop102 ~]$ hadoop fs -put /opt/software/jdk-8u212- linux-x64.tar.gz /
#**上传文件后查看文件存放在什么位置**
[yin@hadoop102 subdir0]$ pwd
/opt/module/hadoop-3.1.3/data/dfs/data/current/BP-1436128598- 192.168.10.102-1610603650062/current/finalized/subdir0/subdir0
#**查看 HDFS 在磁盘存储文件内容**
[yin@hadoop102 subdir0]$ cat blk_1073741825
[yin@hadoop102 subdir0]$ cat blk_1073741836>>tmp.tar.gz
[yin@hadoop102 subdir0]$ cat blk_1073741837>>tmp.tar.gz
#**下载**
[yin@hadoop102 subdir0]$ tar -zxvf tmp.tar.gz
#**执行 wordcount 程序**
[yin@hadoop104 software]$ hadoop fs -get /jdk-8u212-linuxx64.tar.gz ./
[yin@hadoop102 hadoop-3.1.3]$ hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount /input /output

配置历史服务器

[yin@hadoop102 hadoop]$ vim mapred-site.xml
<!-- 历史服务器端地址 -->
<property>
 <name>mapreduce.jobhistory.address</name>
 <value>hadoop102:10020</value>
</property>
<!-- 历史服务器 web 端地址 -->
<property>
 <name>mapreduce.jobhistory.webapp.address</name>
 <value>hadoop102:19888</value>
</property>

分发配置

xsync $HADOOP_HOME/etc/hadoop/mapred-site.xml

在 hadoop102 启动历史服务器

mapred --daemon start historyserver

查看 JobHistory

http://hadoop102:19888/jobhistory

开启日志聚集功能具体步骤

[yin@hadoop102 hadoop]$ vim yarn-site.xml

<!-- 开启日志聚集功能 -->
<property>
 <name>yarn.log-aggregation-enable</name>
 <value>true</value>
</property>
<!-- 设置日志聚集服务器地址 -->
<property>
 <name>yarn.log.server.url</name>
 <value>http://hadoop102:19888/jobhistory/logs</value>
</property>
<!-- 设置日志保留时间为 7 天 -->
<property>
 <name>yarn.log-aggregation.retain-seconds</name>
 <value>604800</value>
</property>

分发

xsync $HADOOP_HOME/etc/hadoop/yarnsite.xml

关闭 NodeManager 、ResourceManager 和 HistoryServer

[yin@hadoop103 hadoop-3.1.3]$ sbin/stop-yarn.sh

[yin@hadoop103 hadoop-3.1.3]$ mapred --daemon stop historyserver

启动 NodeManager 、ResourceManage 和 HistoryServer

[yin@hadoop103 ~]$ start-yarn.sh

[yin@hadoop102 ~]$ mapred --daemon start historyserver

查看日志

历史服务器地址 http://hadoop102:19888/jobhistory

Hadoop 集群启停脚本(包含 HDFS,Yarn,Historyserver):myhadoop.sh
cd /home/yin/bin
vim /myhadoop.sh
#文件内容
#!/bin/bash
if [ $# -lt 1 ]
then
 echo "No Args Input..."
 exit ;
fi
case $1 in
"start")
 echo " =================== 启动 hadoop 集群 ==================="
 echo " --------------- 启动 hdfs ---------------"
 ssh node152 "/opt/module/hadoop-2.7.6/sbin/start-dfs.sh"
 echo " --------------- 启动 yarn ---------------"
  ssh node152 "/opt/module/hadoop-2.7.6/sbin/start-yarn.sh"
 echo " --------------- 启动 historyserver ---------------"
 ssh node152 "/opt/module/hadoop-2.7.6/bin/mapred --daemon start
historyserver"
;;
"stop")
 echo " =================== 关闭 hadoop 集群 ==================="
 echo " --------------- 关闭 historyserver ---------------"
 ssh node152 "/opt/module/hadoop-2.7.6/bin/mapred --daemon stop
historyserver"
 echo " --------------- 关闭 yarn ---------------"
 ssh node153 "/opt/module/hadoop-2.7.6/sbin/stop-yarn.sh"
 echo " --------------- 关闭 hdfs ---------------"
 ssh node152 "/opt/module/hadoop-2.7.6/sbin/stop-dfs.sh"
;;
*)
 echo "Input Args Error..."
;;
esac

chmod +x myhadoop.sh

查看三台服务器 Java 进程脚本:jpsall
vim jpsall

#内容
#!/bin/bash
for host in hadoop102 hadoop103 hadoop104
do
 echo =============== $host ===============
 ssh $host jps
done

chmod +x jpsall

xsync /home/yin/bin/

hdfs

hdfs是一种分布式文件管理系统,可以用来管理多台机器上的文件。

hdfs用于存储文件,通过目录树来定位文件,其次他是分布式的,有很多服务器联合起来来实现其功能,集群中的服务器有各自的角色和功能。

HDFS使用场景:一次写入,多次读出的场景,不支持文件的修改。适合用来做数据分析。

HDFS的优缺点,

高容错

数据自动创建多个副本,通过增加副本个数提高容错

某个副本丢失以后自动恢复

适合处理大数据

数据规模大,可达到gb,tb,甚至是PB的级别

文件规模大,能够处理百万规模以后上的文件数量,

可构建在廉价的机器上

缺点

不适合低延时的数据访问、无法高效的对大量小文件进行存储。

在存储大量小文件的时候他会占用namenode的内存在存储文件目录和块信息。

小文件存储的寻址时间超过读取时间,违反了hdfs的设计目标

不支持并发写入,文件随机修改。

hdfs的常用命令

从本地剪贴到HDFS中 -moveFromLocal

eg:

hadoop fs -moveFromLocal ./12.txt /sanguo/shuguo

从本地拷贝到HDFS中 -copyFromLocal

eg:

hadoop fs -copyFromLocal  123.txt /sanguo/shuguo

追加一个文件到已经存在的文件末尾 -appendToFile

eg:

 hadoop fs -appendToFile 123.txt /sanguo/shuguo/12.txt

-put 等同于copyFromLocal

eg:

 hadoop fs -put ./zaiyiqi.txt /user/atguigu/test/
下载

从 HDFS 拷贝到本地 -copyToLocal

eg:

hadoop fs -copyToLocal /sanguo/shuguo/kongming.txt ./

合并下载多个文件 -getmerge

eg:

 hadoop fs -getmerge /user/atguigu/test/* ./zaiyiqi.txt
HDFS 直接操作

显示目录信息 -ls

eg:

 hadoop fs -ls /

在 HDFS 上创建目录 -mkdir

eg:

 hadoop fs -mkdir -p /sanguo/shuguo

显示文件内容 -cat

eg:

hadoop fs -cat /sanguo/shuguo/12.txt
权限修改

Linux 文件系统中的用法一样,修改文件所属权限

eg:

hadoop fs -copyToLocal /sanguo/shuguo/kongming.txt ./

从 HDFS 的一个路径拷贝到 HDFS 的另一个路径

eg:

hadoop fs -cp /sanguo/shuguo/kongming.txt /zhuge.txt

在 HDFS 目录中移动文件

eg:

 hadoop fs -mv /zhuge.txt /sanguo/shuguo/

显示一个文件的末尾

eg:

hadoop fs -rm /user/atguigu/test/jinlian2.txt

删除文件或文件夹

eg:

hadoop fs -copyToLocal /sanguo/shuguo/kongming.txt ./

删除空目录

eg:

 hadoop fs -rmdir /test

统计文件夹的大小信息

eg:

hadoop fs -copyToLocal /sanguo/shuguo/kongming.txt ./

设置 HDFS 中文件的副本数量

eg:

hadoop fs -setrep 10 /sanguo/shuguo/kongming.txt

这里设置的副本数只是记录在 NameNode 的元数据中,是否真的会有这么多副本,还得 看 DataNode 的数量。因为目前只有 3 台设备,最多也就 3 个副本,只有节点数的增加到 10 台时,副本数才能达到 10。

(MapReduce)

MapReduce定义

MapReduce是一个分布式运算程序的编程框架,是用户开发的基于Hadoop的数据分析应用的核心框架。

MapReduce的核心功能时是将用户写的业务逻辑代码和自带的默认组件组成一个完整的分布式运算程序。并从运行在Hadoop集群上。

优点

Map Reduce易于编程

他简单的实现一些接口,就可以完成一个分布式程序,这个程序可以分布到大量的廉价pc机上。

良好的扩展性

当计算资源不足的时候可以通过简单的增加机器来扩展计算能力。

高容错性

当一个机器挂掉以后他可以把上面的计算任务转移到另一个节点上运行,不至于任务运行失败。

适合PB级以上的海量数据的离线处理

缺点

不擅长实时计算

mapreduce不能像MySQL一样,在秒级或者毫秒级内返回结果。

不擅长流式计算

流式计算的输入数据是动态的,而mapreduce的输入数据是静态的,不能动态变化。

不擅长DAG有向图计算

多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。在 这种情况下,MapReduce并不是不能做,而是使用后,每个MapReduce作业 的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常的低下。

MapReduce 核心思想

1)分布式的运算程序往往需要分成至少 2 个阶段。

2)第一个阶段的 MapTask 并发实例,完全并行运行,互不相干。

3)第二个阶段的 ReduceTask 并发实例互不相干,但是他们的数据依赖于上一个阶段的所有 MapTask 并发实例的输出。

4)MapReduce 编程模型只能包含一个 Map 阶段和一个 Reduce 阶段,如果用户的业务逻辑非常复杂,那就只能多个 MapReduce 程序,串行运行。

分析 WordCount 数据流走向深入理解 MapReduce 核心思想。

MapReduce 进程

一个完整的MapReduce程序在分布式运行时有三类实例进程:

1)MrAppMaster: 负责整个程序的过程调度及状态协调。

2)MapTask: 负责Map阶段的整个数据处理流程。

3)ReduceTask: 负责Reduce阶段的整个数据处理流程。

常用数据序列化类型

常用的数据类型对应的 Hadoop 数据序列化类型

MapReduce 编程规范
用户编写的程序分成三个部分:Mapper、Reducer 和 Driver。
1.Mapper阶段

(1)用户自定义的Mapper要继承自己的父类

(2)Mapper的输入数据是KV对的形式(KV的类型可自定义)

(3)Mapper中的业务逻辑写在map()方法中

(4)Mapper的输出数据是KV对的形式(KV的类型可自定义)

(5)map()方法(MapTask进程)对每一个调用一次

2.Reducer阶段

(1)用户自定义的Reducer要继承自己的父类

(2)Reducer的输入数据类型对应Mapper的输出数据类型,也是KV

(3)Reducer的业务逻辑写在reduce()方法中

(4)ReduceTask进程对每一组相同k的组调用一次reduce()方法

3.Driver阶段

当于YARN集群的客户端,用于提交我们整个程序到YARN集群,提交的是 封装了MapReduce程序相关运行参数的job对象

Hadoop 序列化

序列化是把内存中的对象转化成字节序列或者其他数据传输协议,以便于存储到磁盘(持久化)和网络传输。

反序列话将收到的字节序列(或者其他数据协议)或者是磁盘持久化数据换化成内存中的对象。

序列化的目的。

一般来说,“活的”对象只生存在内存里,关机断电就没有了。而且“活的” 对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机。 然而序 列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机。

Java的序列化是一个重量级序列化框架(Serializable),一个对象被序列化后,会 附带很多额外的信息(各种校验信息,Header,继承体系等),不便于在网络中高效 传输。所以,Hadoop自己开发了一套序列化机制(Writable)。

Hadoop序列化特点:

(1)紧凑 :高效使用存储空间。

(2)快速:读写数据的额外开销小。

(3)可扩展:随着通信协议的升级而可升级

(4)互操作:支持多语言的交互

自定义 bean 对象实现序列化接口(Writable)

在企业开发中往往常用的基本序列化类型不能满足所有需求,比如在 Hadoop 框架内部 传递一个 bean 对象,那么该对象就需要实现序列化接口。

具体实现 bean 对象序列化步骤如下 7 步。

(1)必须实现 Writable 接口

(2)反序列化时,需要反射调用空参构造函数,所以必须有空参构造

public FlowBean() {
    super();
}

(3)重写序列化方法

@Override
public void write(DataOutput out) throws IOException {
    out.writeLong(upFlow);
    out.writeLong(downFlow);
    out.writeLong(sumFlow);
}

(3)重写序列化方法(2)反序列化时,需要反射调用空参构造函数,所以必须有空参构造

public FlowBean() {
    super();
}

(4)重写反序列化方法

@Override
public void readFields(DataInput in) throws IOException {
    upFlow = in.readLong();
    downFlow = in.readLong();
    sumFlow = in.readLong();
}

(5)注意反序列化的顺序和序列化的顺序完全一致

(6)要想把结果显示在文件中,需要重写 toString(),可用”\t”分开,方便后续用。

(7)如果需要将自定义的 bean 放在 key 中传输,则还需要实现 Comparable 接口,因为 MapReduce 框中的 Shuffle 过程要求对 key 必须能排序。详见后面排序案例。

@Override
public int compareTo(FlowBean o) {
    // 倒序排列,从大到小
    return this.sumFlow > o.getSumFlow() ? -1 : 1;
}
MapReduce 框架原理
InputFormat 数据输入

MapReduce的数据流

image-20210804194210652
切片与 MapTask 并行度决定机制

MapTask 的并行度决定 Map 阶段的任务处理并发度,进而影响到整个 Job 的处理速度。

思考:1G 的数据,启动 8 个 MapTask,可以提高集群的并发处理能力。那么 1K 的数 据,也启动 8 个 MapTask,会提高集群性能吗?MapTask 并行任务是否越多越好呢?哪些因 素影响了 MapTask 并行度?

MapTask 并行度决定机制

**数据块:**Block 是 HDFS 物理上把数据分成一块一块。

**数据切片:**数据切片只是在逻辑上对输入进行分片,并不会在磁盘上将其切分成片进行 存储。

数据切片与MapTask并行度决定机制
Job 提交流程源码详解
waitForCompletion()
    submit();
// 1 建立连接
	connect();
        // 1)创建提交 Job 的代理
        new Cluster(getConfiguration());
            // (1)判断是本地 yarn 还是远程
            initialize(jobTrackAddr, conf);
    // 2 提交 job
    submitter.submitJobInternal(Job.this, cluster)
    // 1)创建给集群提交数据的 Stag 路径
    Path jobStagingArea =JobSubmissionFiles.getStagingDir(cluster, conf);
    // 2)获取 jobid ,并创建 Job 路径
    JobID jobId = submitClient.getNewJobID();
    // 3)拷贝 jar 包到集群
    copyAndConfigureFiles(job, submitJobDir);
    rUploader.uploadFiles(job, jobSubmitDir);
    // 4)计算切片,生成切片规划文件
        writeSplits(job, submitJobDir);
            maps = writeNewSplits(job, jobSubmitDir);
            input.getSplits(job);
	// 5)向 Stag 路径写 XML 配置文件
    writeConf(conf, submitJobFile);
    conf.writeXml(out);
// 6)提交 Job,返回提交状态
	status = submitClient.submitJob(jobId,submitJobDir.toString(), job.getCredentials());
FileInputFormat 切片机制

上面的流程是整个 MapReduce 最全工作流程,但是 Shuffle 过程只是从第 7 步开始到第 16 步结束,具体 Shuffle 过程详解,如下:

1)MapTask 收集我们的 map()方法输出的 kv 对,放到内存缓冲区中

2)从内存缓冲区不断溢出本地磁盘文件,可能会溢出多个文件

3)多个溢出文件会被合并成大的溢出文件

4)在溢出过程及合并的过程中,都要调用 Partitioner 进行分区和针对 key 进行排序

5)ReduceTask 根据自己的分区号,去各个 MapTask 机器上取相应的结果分区数据

6)ReduceTask 会取到同一个分区的来自不同 MapTask 的结果文件,ReduceTask 会将这 些文件再进行合并(归并排序)

7)合并成大文件后,Shuffle 的过程也就结束了,后面进入 ReduceTask 的逻辑运算过程 (从文件中取出一个一个的键值对 Group,调用用户自定义的 reduce()方法)

注意

Shuffle 中的缓冲区大小会影响到 MapReduce 程序的执行效率,原则上说,缓冲区越大, 磁盘 io 的次数越少,执行速度就越快。

缓冲区的大小可以通过参数调整,参数:io.sort.mb 默认 100M。

Shuffle 机制

Map 方法之后,Reduce 方法之前的数据处理过程称之为 Shuffle。

image-20210804200049636
WritableComparable 排序

排序是MapReduce框架中最重要的操作之一。

MapTask和ReduceTask均会对数 据按 照key进 行排 序。该操作属于 Hadoop的默认行为。任何应用程序中的数据均会被排序,而不管逻辑上是否需要。

默认排序是按照字典顺序排序,且实现该排序的方法是快速排序。

排序概述

对于MapTask,它会将处理的结果暂时放到环形缓冲区中,当环形缓冲区使 用率达到一定阈值后,再对缓冲区中的数据进行一次快速排序,并将这些有序数 据溢写到磁盘上,而当数据处理完毕后,它会对磁盘上所有文件进行归并排序。

面试题部分

HDFS写数据流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4CN9cjCR-1631501988125)(大数据1.0.assets/image-20210804083356884.png)]

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

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

(3)客户端请求第一个 Block 上传到哪几个 DataNode 服务器上。

(4)NameNode 返回 3 个 DataNode 节点,分别为 dn1、dn2、dn3。

(5)客户端通过 FSDataOutputStream 模块请求 dn1 上传数据,dn1 收到请求会继续调用 dn2,然后 dn2 调用 dn3,将这个通信管道建立完成。

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

(7)客户端开始往 dn1 上传第一个 Block(先从磁盘读取数据放到一个本地内存缓存), 以 Packet 为单位,dn1 收到一个 Packet 就会传给 dn2,dn2 传给 dn3;dn1 每传一个 packet 会放入一个应答队列等待应答。

(8)当一个 Block 传输完成之后,客户端再次请求 NameNode 上传第二个 Block 的服务 器。(重复执行 3-7 步)。

HDFS写数据流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wy46g3tl-1631501988130)(大数据1.0.assets/image-20210804084417696.png)]

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

(2)挑选一台 DataNode(就近原则,然后随机)服务器,请求读取数据。

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

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

NameNode 和 SecondaryNameNode
NN 和 2NN 工作机制

思考:NameNode 中的元数据是存储在哪里的?

首先,我们做个假设,如果存储在 NameNode 节点的磁盘中,因为经常需要进行随机访 问,还有响应客户请求,必然是效率过低。因此,元数据需要存放在内存中。但如果只存在 内存中,一旦断电,元数据丢失,整个集群就无法工作了。因此产生在磁盘中备份元数据的 FsImage。

这样又会带来新的问题,当在内存中的元数据更新时,如果同时更新 FsImage,就会导 致效率过低,但如果不更新,就会发生一致性问题,一旦 NameNode 节点断电,就会产生数 据丢失。因此,引入 Edits 文件(只进行追加操作,效率很高)。每当元数据有更新或者添加元 数据时,修改内存中的元数据并追加到 Edits 中。这样,一旦 NameNode 节点断电,可以通 过 FsImage 和 Edits 的合并,合成元数据。

但是,如果长时间添加数据到 Edits 中,会导致该文件数据过大,效率降低,而且一旦 断电,恢复元数据需要的时间过长。因此,需要定期进行 FsImage 和 Edits 的合并,如果这 个操作由NameNode节点完成,又会效率过低。因此,引入一个新的节点SecondaryNamenode, 专门用于 FsImage 和 Edits 的合并。

NameNode工作机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sN1PkhEa-1631501988133)(大数据1.0.assets/image-20210804085115108.png)]

第一阶段:NameNode 启动

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

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

(3)NameNode 记录操作日志,更新滚动日志。

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

第二阶段:Secondary NameNode 工作

(1)Secondary NameNode 询问 NameNode 是否需要 CheckPoint。直接带回 NameNode 是否检查结果。

(2)Secondary NameNode 请求执行 CheckPoint。

(3)NameNode 滚动正在写的 Edits 日志。

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

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

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

(7)拷贝 fsimage.chkpoint 到 NameNode。

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

Fsimage 和 Edits 解析

NameNode被格式化之后,将在/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current目录中产生如下文件

fsimage_0000000000000000000 
fsimage_0000000000000000000.md5
seen_txid 
VERSION

(1)Fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目 录和文件inode的序列化信息。

(2)Edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先 会被记录到Edits文件中。

(3)seen_txid文件保存的是一个数字,就是最后一个edits_的数字

(4)每 次NameNode启动的时候都会将Fsimage文件读入内存,加 载Edits里面的更新操作,保证内存 中的元数据信息是最新的、同步的,可以看成NameNode启动的时候就将Fsimage和Edits文件进行了合并。

oiv 查看 Fsimage 文件

查看 oiv 和 oev 命令

[atguigu@hadoop102 current]$ hdfs
oiv apply the offline fsimage viewer to an fsimage
oev apply the offline edits viewer to an edits file
#基本语法
hdfs oev -p 文件类型 -i 编辑日志 -o 转换后文件输出路径
#案例实操
[atguigu@hadoop102 current]$ hdfs oev -p XML -i edits_0000000000000000012-0000000000000000013 -o /opt/module/hadoop-3.1.3/edits.xml
[atguigu@hadoop102 current]$ cat /opt/module/hadoop-3.1.3/edits.xml
#将显示的 xml 文件内容拷贝到 Eclipse 中创建的 xml 文件中,并格式化。显示结果如下
<?xml version="1.0" encoding="UTF-8"?>
<EDITS>
<EDITS_VERSION>-63</EDITS_VERSION>
<RECORD>
<OPCODE>OP_START_LOG_SEGMENT</OPCODE>
<DATA>
<TXID>129</TXID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_ADD</OPCODE>
<DATA>
<TXID>130</TXID>
<LENGTH>0</LENGTH>
<INODEID>16407</INODEID>
<PATH>/hello7.txt</PATH>
<REPLICATION>2</REPLICATION>
<MTIME>1512943607866</MTIME>
<ATIME>1512943607866</ATIME>
<BLOCKSIZE>134217728</BLOCKSIZE>
<CLIENT_NAME>DFSClient_NONMAPREDUCE_-
1544295051_1</CLIENT_NAME>
<CLIENT_MACHINE>192.168.1.5</CLIENT_MACHINE>
<OVERWRITE>true</OVERWRITE>
<PERMISSION_STATUS>
<USERNAME>atguigu</USERNAME>
<GROUPNAME>supergroup</GROUPNAME>
<MODE>420</MODE>
</PERMISSION_STATUS>
<RPC_CLIENTID>908eafd4-9aec-4288-96f1-
e8011d181561</RPC_CLIENTID>
<RPC_CALLID>0</RPC_CALLID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_ALLOCATE_BLOCK_ID</OPCODE>
<DATA>
<TXID>131</TXID>
<BLOCK_ID>1073741839</BLOCK_ID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_SET_GENSTAMP_V2</OPCODE>
<DATA>
<TXID>132</TXID>
<GENSTAMPV2>1016</GENSTAMPV2>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_ADD_BLOCK</OPCODE>
<DATA>
<TXID>133</TXID>
<PATH>/hello7.txt</PATH>
<BLOCK>
<BLOCK_ID>1073741839</BLOCK_ID>
<NUM_BYTES>0</NUM_BYTES>
<GENSTAMP>1016</GENSTAMP>
</BLOCK>
<RPC_CLIENTID></RPC_CLIENTID>
<RPC_CALLID>-2</RPC_CALLID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_CLOSE</OPCODE>
<DATA>
<TXID>134</TXID>
<LENGTH>0</LENGTH>
<INODEID>0</INODEID>
<PATH>/hello7.txt</PATH>
<REPLICATION>2</REPLICATION>
<MTIME>1512943608761</MTIME>
<ATIME>1512943607866</ATIME>
<BLOCKSIZE>134217728</BLOCKSIZE>
<CLIENT_NAME></CLIENT_NAME>
<CLIENT_MACHINE></CLIENT_MACHINE>
<OVERWRITE>false</OVERWRITE>
<BLOCK>
<BLOCK_ID>1073741839</BLOCK_ID>
<NUM_BYTES>25</NUM_BYTES>
<GENSTAMP>1016</GENSTAMP>
</BLOCK>
<PERMISSION_STATUS>
<USERNAME>atguigu</USERNAME>
<GROUPNAME>supergroup</GROUPNAME>
<MODE>420</MODE>
</PERMISSION_STATUS>
</DATA>
</RECORD>
</EDITS >
CheckPoint 时间设置

通常情况下,SecondaryNameNode 每隔一小时执行一次。

[hdfs-default.xml]

<property>
 <name>dfs.namenode.checkpoint.period</name>
 <value>3600</value>
</property>

一分钟检查一次操作次数,3 当操作次数达到 1 百万时,SecondaryNameNode 执行一次。

<property>
 <name>dfs.namenode.checkpoint.txns</name>
  <value>1000000</value>
<description>操作动作次数</description>
</property>
<property>
 <name>dfs.namenode.checkpoint.check.period</name>
 <value>60</value>
<description> 1 分钟检查一次操作次数</description>
</property >

NameNode 故障处理(扩展)

NameNode 故障后,可以采用如下两种方法恢复数据。

1)将 SecondaryNameNode 中数据拷贝到 NameNode 存储数据的目录;

(1)kill -9 NameNode 进程

(2)删除 NameNode 存储的数据(/opt/module/hadoop-3.1.3/data/tmp/dfs/name)

[atguigu@hadoop102 hadoop-3.1.3]$ rm -rf /opt/module/hadoop- 3.1.3/data/tmp/dfs/name/*

(3)拷贝 SecondaryNameNode 中数据到原 NameNode 存储数据目录

[atguigu@hadoop102 dfs]$ scp -r atguigu@hadoop104:/opt/module/hadoop-
3.1.3/data/tmp/dfs/namesecondary/* ./name/

(4)重新启动 NameNode

[atguigu@hadoop102 hadoop-3.1.3]$ hdfs --daemon start namenode

2)使用-importCheckpoint 选项启动 NameNode 守护进程,从而将 SecondaryNameNode 中 数据拷贝到 NameNode 目录中。

(1)修改 hdfs-site.xml 中的

<property>
 <name>dfs.namenode.checkpoint.period</name>
 <value>120</value>
</property>
<property>
 <name>dfs.namenode.name.dir</name>
 <value>/opt/module/hadoop-3.1.3/data/tmp/dfs/name</value>
</property>

(2)kill -9 NameNode 进程

(3)删除 NameNode 存储的数据(/opt/module/hadoop-3.1.3/data/tmp/dfs/name)

[atguigu@hadoop102 hadoop-3.1.3]$ rm -rf /opt/module/hadoop-
3.1.3/data/tmp/dfs/name/*

( 4 ) 如 果 SecondaryNameNode 不 和 NameNode 在 一 个 主 机 节 点 上 , 需 要 将 SecondaryNameNode 存储数据的目录拷贝到 NameNode 存储数据的平级目录,并删除 in_use.lock 文件

[atguigu@hadoop102 dfs]$ scp -r atguigu@hadoop104:/opt/module/hadoop-
3.1.3/data/tmp/dfs/namesecondary ./
[atguigu@hadoop102 namesecondary]$ rm -rf in_use.lock
[atguigu@hadoop102 dfs]$ pwd
/opt/module/hadoop-3.1.3/data/tmp/dfs
[atguigu@hadoop102 dfs]$ ls
data name namesecondary

(5)导入检查点数据(等待一会 ctrl+c 结束掉)

[atguigu@hadoop102 hadoop-3.1.3]$ bin/hdfs namenode -importCheckpoint

启动 NameNode

[atguigu@hadoop102 hadoop-3.1.3]$ hdfs --daemon start namenode
集群安全模式

1、NameNode启动

NameNode启动时,首先将镜像文件(Fsimage)载入内存,并执行编辑日志(Edits)中的各项操作。一 旦在内存中成功建立文件系统元数据的映像,则创建一个新的Fsimage文件和一个空的编辑日志。此 时, NameNode开始监听DataNode请求。这个过程期间,NameNode一直运行在安全模式,即NameNode的文件系 统对于客户端来说是只读的。

2、DataNode启动

系统中的数据块的位置并不是由NameNode维护的,而是以块列表的形式存储在DataNode中。在系统的 正常操作期间,NameNode会在内存中保留所有块位置的映射信息。在安全模式下,各 个DataNode会 向 NameNode发送最新的块列表信息,NameNode了解到足够多的块位置信息之后,即可高效运行文件系统。

3、安全模式退出判断

如果满足“最小副本条件”,NameNode会在30秒钟之后就退出安全模式。所谓的最小副本条件指的是在 整个文件系统中99.9%的块满足最小副本级别(默认值:dfs.replication.min=1)。在启动一个刚刚格式化的 HDFS集群时,因为系统中还没有任何块,所以NameNode不会进入安全模式。

DataNode 工作机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VLnQ7L8F-1631501988138)(大数据1.0.assets/image-20210804091618315.png)]

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

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

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

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

数据完整性

思考:如果电脑磁盘里面存储的数据是控制高铁信号灯的红灯信号(1)和绿灯信号(0), 但是存储该数据的磁盘坏了,一直显示是绿灯,是否很危险?

同理 DataNode 节点上的数据 损坏了,却没有发现,是否也很危险,那么如何解决呢?

如下是 DataNode 节点保证数据完整性的方法。

(1)当 DataNode 读取 Block 的时候,它会计算 CheckSum。

(2)如果计算后的 CheckSum,与 Block 创建时值不一样,说明 Block 已经损坏。

(3)Client 读取其他 DataNode 上的 Block。

(4)DataNode 在其文件创建后周期验证 CheckSum。

image-20210804092315232
掉线时限参数设置
DataNode掉线时限参数设置
image-20210804092414759

需要注意的是 hdfs-site.xml 配置文件中的 heartbeat.recheck.interval 的单位为毫秒, dfs.heartbeat.interval 的单位为秒。

<property>
 <name>dfs.namenode.heartbeat.recheck-interval</name>
 <value>300000</value>
</property>
<property>
 <name>dfs.heartbeat.interval</name>
 <value>3</value>
</property>
服役新数据节点

环境准备

(1)在 hadoop104 主机上再克隆一台 hadoop105 主机

(2)修改 IP 地址和主机名称

(3)删除原来 HDFS 文件系统留存的文件(/opt/module/hadoop-3.1.3/data 和 log)

(4)source 一下配置文件

source /etc/profile

服役新节点具体步骤

(1)直接启动 DataNode,即可关联到集群

[atguigu@hadoop105 hadoop-3.1.3]$ hdfs --daemon start datanode

[atguigu@hadoop105 hadoop-3.1.3]$ sbin/yarn-daemon.sh start nodemanager

如果数据不均衡,可以用命令实现集群的再平衡

atguigu@hadoop102 sbin]$ ./start-balancer.sh
starting balancer, logging to /opt/module/hadoop-3.1.3/logs/hadoopatguigu-balancer-hadoop102.out
Time Stamp Iteration# Bytes Already Moved Bytes Left To Move
Bytes Being Moved
退役旧数据节点

添加白名单

添加到白名单的主机节点,都允许访问 NameNode,不在白名单的主机节点,都会被退出。

配置白名单的具体步骤如下:

在 NameNode 的/opt/module/hadoop-3.1.3/etc/hadoop 目录下创建 dfs.hosts 文件

[atguigu@hadoop102 hadoop]$ pwd
/opt/module/hadoop-3.1.3/etc/hadoop
[atguigu@hadoop102 hadoop]$ touch dfs.hosts
[atguigu@hadoop102 hadoop]$ vi dfs.hosts
hadoop102
hadoop103
hadoop104
#在 NameNode 的 hdfs-site.xml 配置文件中增加 dfs.hosts 属性
<property>
<name>dfs.hosts</name>
<value>/opt/module/hadoop-3.1.3/etc/hadoop/dfs.hosts</value>
</property>
#配置文件分发
[atguigu@hadoop102 hadoop]$ xsync hdfs-site.xml
#刷新 NameNode
[atguigu@hadoop102 hadoop-3.1.3]$ hdfs dfsadmin -refreshNodes
Refresh nodes successful
#更新 ResourceManager 节点
[atguigu@hadoop102 hadoop-3.1.3]$ yarn rmadmin -refreshNodes
17/06/24 14:17:11 INFO client.RMProxy: Connecting to ResourceManager at
hadoop103/192.168.1.103:8033
#如果数据不均衡,可以用命令实现集群的再平衡
[atguigu@hadoop102 sbin]$ ./start-balancer.sh
starting balancer, logging to /opt/module/hadoop-3.1.3/logs/hadoopatguigu-balancer-hadoop102.out
Time Stamp Iteration# Bytes Already Moved Bytes Left To Move
Bytes Being Moved


黑名单退役

在黑名单上面的主机都会被强制退出。

#在 NameNode 的/opt/module/hadoop-3.1.3/etc/hadoop 目录下创建 dfs.hosts.exclude 文件
[atguigu@hadoop102 hadoop]$ pwd
/opt/module/hadoop-3.1.3/etc/hadoop
[atguigu@hadoop102 hadoop]$ touch dfs.hosts.exclude
[atguigu@hadoop102 hadoop]$ vi dfs.hosts.exclude
#添加如下主机名称(要退役的节点)
hadoop105
#在 NameNode 的 hdfs-site.xml 配置文件中增加 dfs.hosts.exclude 属性
<property>
<name>dfs.hosts.exclude</name>
 <value>/opt/module/hadoop-3.1.3/etc/hadoop/dfs.hosts.exclude</value>
</property>
#刷新 NameNode、刷新 ResourceManager
[atguigu@hadoop102 hadoop-3.1.3]$ hdfs dfsadmin -refreshNodes
Refresh nodes successful
[atguigu@hadoop102 hadoop-3.1.3]$ yarn rmadmin -refreshNodes
17/06/24 14:55:56 INFO client.RMProxy: Connecting to ResourceManager at
hadoop103/192.168.1.103:8033
#如果数据不均衡,可以用命令实现集群的再平衡
[atguigu@hadoop102 sbin]$ ./start-balancer.sh
starting balancer, logging to /opt/module/hadoop-3.1.3/logs/hadoopatguigu-balancer-hadoop102.out
Time Stamp Iteration# Bytes Already Moved Bytes Left To Move
Bytes Being Moved

注意:不允许白名单和黑名单中同时出现同一个主机名称。

Datanode 多目录配置

DataNode 也可以配置成多个目录,每个目录存储的数据不一样。即:数据不是副本

具体配置如下 hdfs-site.xml

<property>
 <name>dfs.datanode.data.dir</name>
<value>file:///${hadoop.tmp.dir}/dfs/data1,file:///${hadoop.tmp.dir}/dfs/
data2</value>
</property>
网络拓扑-节点距离计算

在 HDFS 写数据的过程中,NameNode 会选择距离待上传数据最近距离的 DataNode 接收数据。那么这个最近距离怎么计算呢?

节点距离:两个节点到达最近的共同祖先的距离总和。

Distance(/d1/r1/n0, /d1/r1/n0)=0 (同一节点上的进程)

Distance(/d1/r2/n0, /d1/r3/n2)=4 (同一数据中心不同机架上的节点)

Distance(/d1/r1/n1, /d1/r1/n2)=2 (同一机架上的不同节点)

Distance(/d1/r2/n1, /d2/r4/n1)=6 (不同数据中心的节点)

image-20210804083926961

例如,假设有数据中心 d1 机架 r1 中的节点 n1。该节点可以表示为/d1/r1/n1。利用这种 标记,这里给出四种距离描述。

大家算一算每两个节点之间的距离。

image-20210804084003191
Hadoop 副本节点选择

第一个副本在Client所处的节点上。 如果客户端在集群外,随机选一个。

第二个副本和第一个副本位于相 同机架,随机节点。

第三个副本位于不同机架,随机节点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1NCxMoEn-1631501988141)(大数据1.0.assets/image-20210804084306838.png)]

官方 WordCount 源码

采用反编译工具反编译源码,发现 WordCount 案例有 Map 类、Reduce 类和驱动类。且 数据的类型是 Hadoop 自身封装的序列化类型。

WordCount 案例实操
1.需求

在给定的文本文件中统计输出每一个单词出现的总次数

(1)输入数据

txt文本文件

(2)期望输出数据

atguigu 2 
banzhang 1
cls 2 
hadoop 1 
jiao 1 
ss 2 
xue 1
2.需求分析

按照 MapReduce 编程规范,分别编写 Mapper,Reducer,Driver,

image-20210804145315257
环境准备
创建 maven 工程
在 pom.xml 文件中添加如下依赖
<dependencies>
    <dependency>
    	<groupId>junit</groupId>
    	<artifactId>junit</artifactId>
    	<version>4.12</version>
    </dependency>
    <dependency>
    	<groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.12.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
    	<artifactId>hadoop-client-api</artifactId>
        <version>3.1.3</version>
    </dependency>
    <dependency>
    	<groupId>org.apache.hadoop</groupId>
   		<artifactId>hadoop-client-runtime</artifactId>
    <version>3.1.3</version> 
    </dependency>
</dependencies>
创建日志文件

在项目的 src/main/resources 目录下,新建一个文件,命名为“log4j.xml”,在文件 中填入。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error" strict="true" name="XMLConfig">
 <Appenders>
    <!-- 类型名为 Console,名称为必须属性 -->
    <Appender type="Console" name="STDOUT">
    <!-- 布局为 PatternLayout 的方式, 输 出 样 式 为 [INFO] [2018-01-22 17:34:01][org.test.Console]I'm here -->
    <Layout type="PatternLayout" pattern="[%p] [%d{yyyy-MM-dd HH:mm:ss}][%c{10}]%m%n" />
    </Appender>
    </Appenders>
    <Loggers>
    <!-- 可加性为 false -->
    <Logger name="test" level="info" additivity="false">
    <AppenderRef ref="STDOUT" />
    </Logger>
    <!-- root loggerConfig 设置 -->
    <Root level="info">
    <AppenderRef ref="STDOUT" />
    </Root>
    </Loggers>
</Configuration>
编写程序
编写Maper类
package com.yin.mapreduce.wordcount;
import java.io.IOException;
import org.apache.hadoop.io.IntWrittable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Mapper;

public class WordcountMapper extends Mapper<LongWritable,Text,Text,IntWriable>{
    
    Text outKey = new Text();
    IntWritable outValue = new IntWritable(1);
    
    public class void map(LongWritable key ,Text value ,Context context) throws IOException ,InterruptedException{
        
        //获取一行数据
        String line = value.toString();
        //切割数据
        String[] words = line.split(" ");
        
        //输出
        for(String word:words){
            
            outKey.set(word);
            context.write(outKey,outValue);
        }        
    }
}
编写 Reducer 类
package com.yin.mapreduce.wordcount;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class WordcountReducer extends Reducer<Text,IntWritable,Text,IntWritable>{
    int sum ;
    IntWritable outValue = new IntWritable ;
    
    @Overeide
    protected void reduce(Text key,Iterable<IntWritable> values,Context context) throws IOException,InterruptedException {
        sum = 0;
        //累加求和
        for(IntWritable count : values){
            sum += count.get();      
        }
        //输出
        outValue.set(sum);
        context.write(key,outValue);
    }   
}
编写 Driver 驱动类
package com.yin.mapreduce.wordcount;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import
org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import
org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class WordcountDriver {
    public static void main(String[] args) throws IOException,
        ClassNotFoundException, InterruptedException {
        // 1 获取配置信息以及封装任务
        Configuration configuration = new Configuration();
        Job job = Job.getInstance(configuration);
        // 2 设置 jar 加载路径
        job.setJarByClass(WordcountDriver.class);
        // 3 设置 map 和 reduce 类
        job.setMapperClass(WordcountMapper.class);
        job.setReducerClass(WordcountReducer.class);
        // 4 设置 map 输出
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);
        // 5 设置最终输出 kv 类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        // 6 设置输入和输出路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new
        Path(args[1]));
        // 7 提交
        boolean result = job.waitForCompletion(true);
        System.exit(result ? 0 : 1);
	}
}
本地测试

(1)需要首先配置好 HadoopHome 变量以及 Windows 运行依赖

(2)在 Eclipse/Idea 上运行程序

集群上测试

用 maven 打 jar 包,需要添加的打包插件依赖

注意:标记红颜色的部分需要替换为自己工程主类

<build>
    <plugins>
    	<plugin>
    		<artifactId>maven-compiler-plugin</artifactId>
			<version>2.3.2</version>
			<configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin </artifactId>
                <configuration>
                    <descriptorRefs>
                    	<descriptorRef>jar-withdependencies</descriptorRef>
					</descriptorRefs>
					<archive>
						<manifest>
	<mainClass>com.yin.mapreduce.WordcountDriver</mainClass>
						</manifest>
					</archive>
				</configuration>
					<executions>
						<execution>
							<id>make-assembly</id>
                            <phase>package</phase>
                            <goals>
								<goal>single</goal>
							</goals>
						</execution>
					</executions>
				</plugin>
			</plugins>
	</build>

注意:如果工程上显示红叉。在项目上右键->maven->update project 即可。

(1)将程序打成 jar 包,然后拷贝到 Hadoop 集群中

步骤详情:右键->Run as->maven install。等待编译完成就会在项目的 target 文件夹中生 成 jar 包。如果看不到。在项目上右键-》Refresh,即可看到。修改不带依赖的 jar 包名称为 wc.jar,并拷贝该 jar 包到 Hadoop 集群。

(2)启动 Hadoop 集群

(3)执行 WordCount 程序

[atguigu@hadoop102 software]$ hadoop jar wc.jar
com.atguigu.wordcount.WordcountDriver /user/atguigu/input
/user/atguigu/output

在 Windows 上向集群提交任务

(1)添加必要配置信息

MapReduce 跑的慢的原因

MapReduce 程序效率的瓶颈在于两点:

1. 计算机性能

CPU、内存、磁盘健康、网络

2.I/O 操作优化

(1)数据倾斜

(2)Map和Reduce数设置不合理

(3)Map运行时间太长,导致Reduce等待过久

(4)小文件过多

(5)大量的不可分块的超大文件

(6)Spill次数过多

(7)Merge次数过多等。

MapReduce 优化方法主要从六个方面考虑:数据输入、Map 阶段、Reduce 阶段、IO 传 输、数据倾斜问题和常用的调优参数

数据输入

MapReduce优化方法

(1)合并小文件:在执 行MR任 务前将小文件进行合并,大量的小文件会 产生大量的Map任务,增大Map任务装载次数,而任务的装载比较耗时,从而 导致MR运行较慢。

(2)采用CombineTextInputFormat来作为输入,解决输入端大量小文件场景。

** Map 阶段**

2 Map阶段

(1)减 少溢写(Spill)次数:

通过调整io.sort.mb及sort.spill.percent参数值,增大触发Spill的内存上限,减少Spill次数,从而减少磁盘IO。

(2)减少合并(Merge)次数:

通过调整io.sort.factor参数,增大Merge的 文件数目,减少Merge的次数,从而缩短MR处理时间。

(3)在Map之后,不影响业务逻辑前提下,先进行Combine处理,减少 I/O。

Reduce 阶段

(1)合理设置Map和Reduce数:

两个都不能设置太少,也不能设置太多。 太少,会导致Task等待,延长处理时间;太多,会导致Map、Reduce任务间竞 争资源,造成处理超时等错误。

(2)设置Map、Reduce共存:

调整slowstart.completedmaps参数,使Map运 行到一定程度后,Reduce也开始运行,减少Reduce的等待时间。

(3)规避使用Reduce:

因为Reduce在用于连接数据集的时候将会产生大量 的网络消耗。

(4)合理设置Reduce端的Buffer:

默认情况下,数据达到一个阈值的时候, Buffer中的数据就会写入磁盘,然后Reduce会从磁盘中获得所有的数据。也就是 说,Buffer和Reduce是没有直接关联的,中间多次写磁盘->读磁盘的过程,既然 有这个弊端,那么就可以通过参数来配置,使得Buffer中的一部分数据可以直接 输送到Reduce,从而减少IO开销:mapreduce.reduce.input.buffer.percent,默认为 0.0。当值大于0的时候,会保留指定比例的内存读Buffer中的数据直接拿给 Reduce使用。这样一来,设置Buffer需要内存,读取数据需要内存,Reduce计算 也要内存,所以要根据作业的运行情况进行调整。

** I/O 传输**

1)采用数据压缩的方式,减少网络IO的的时间。安装Snappy和LZO压缩编 码器。

2)使用SequenceFile二进制文件。

数据倾斜问题

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

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

Zookeeper三个核心选举原则:

(1)Zookeeper集群中只有超过半数以上的服务器启动,集群才能正常工作;

(2)在集群正常工作之前,myid小的服务器给myid大的服务器投票,直到集群正常工作,选出Leader;

(3)选出Leader之后,之前的服务器状态由Looking改变为Following,以后的服务器都是Follower。

下面以一个简单的例子来说明整个选举的过程:

img

假设有五台服务器组成的Zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的。

假设这些服务器从id1-5,依序启动:

因为一共5台服务器,只有超过半数以上,即最少启动3台服务器,集群才能正常工作。

(1)服务器1启动,发起一次选举。

​ 服务器1投自己一票。此时服务器1票数一票,不够半数以上(3票),选举无法完成;

​ 服务器1状态保持为LOOKING;

(2)服务器2启动,再发起一次选举。

​ 服务器1和2分别投自己一票,此时服务器1发现服务器2的id比自己大,更改选票投给服务器2;

​ 此时服务器1票数0票,服务器2票数2票,不够半数以上(3票),选举无法完成;

​ 服务器1,2状态保持LOOKING;

(3)服务器3启动,发起一次选举。

​ 与上面过程一样,服务器1和2先投自己一票,然后因为服务器3id最大,两者更改选票投给为服务器3;

​ 此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数(3票),服务器3当选Leader。

​ 服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;

(4)服务器4启动,发起一次选举。

​ 此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为1票。

​ 此时服务器4服从多数,更改选票信息为服务器3;

​ 服务器4并更改状态为FOLLOWING;

(5)服务器5启动,同4一样投票给3,此时服务器3一共5票,服务器5为0票;

​ 服务器5并更改状态为FOLLOWING;

最终Leader是服务器3,状态为LEADING;

其余服务器是Follower,状态为FOLLOWING。

延长处理时间;太多,会导致Map、Reduce任务间竞 争资源,造成处理超时等错误。

(2)设置Map、Reduce共存:

调整slowstart.completedmaps参数,使Map运 行到一定程度后,Reduce也开始运行,减少Reduce的等待时间。

(3)规避使用Reduce:

因为Reduce在用于连接数据集的时候将会产生大量 的网络消耗。

(4)合理设置Reduce端的Buffer:

默认情况下,数据达到一个阈值的时候, Buffer中的数据就会写入磁盘,然后Reduce会从磁盘中获得所有的数据。也就是 说,Buffer和Reduce是没有直接关联的,中间多次写磁盘->读磁盘的过程,既然 有这个弊端,那么就可以通过参数来配置,使得Buffer中的一部分数据可以直接 输送到Reduce,从而减少IO开销:mapreduce.reduce.input.buffer.percent,默认为 0.0。当值大于0的时候,会保留指定比例的内存读Buffer中的数据直接拿给 Reduce使用。这样一来,设置Buffer需要内存,读取数据需要内存,Reduce计算 也要内存,所以要根据作业的运行情况进行调整。

** I/O 传输**

1)采用数据压缩的方式,减少网络IO的的时间。安装Snappy和LZO压缩编 码器。

2)使用SequenceFile二进制文件。

数据倾斜问题

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

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

Zookeeper三个核心选举原则:

(1)Zookeeper集群中只有超过半数以上的服务器启动,集群才能正常工作;

(2)在集群正常工作之前,myid小的服务器给myid大的服务器投票,直到集群正常工作,选出Leader;

(3)选出Leader之后,之前的服务器状态由Looking改变为Following,以后的服务器都是Follower。

下面以一个简单的例子来说明整个选举的过程:

img

假设有五台服务器组成的Zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的。

假设这些服务器从id1-5,依序启动:

因为一共5台服务器,只有超过半数以上,即最少启动3台服务器,集群才能正常工作。

(1)服务器1启动,发起一次选举。

​ 服务器1投自己一票。此时服务器1票数一票,不够半数以上(3票),选举无法完成;

​ 服务器1状态保持为LOOKING;

(2)服务器2启动,再发起一次选举。

​ 服务器1和2分别投自己一票,此时服务器1发现服务器2的id比自己大,更改选票投给服务器2;

​ 此时服务器1票数0票,服务器2票数2票,不够半数以上(3票),选举无法完成;

​ 服务器1,2状态保持LOOKING;

(3)服务器3启动,发起一次选举。

​ 与上面过程一样,服务器1和2先投自己一票,然后因为服务器3id最大,两者更改选票投给为服务器3;

​ 此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数(3票),服务器3当选Leader。

​ 服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;

(4)服务器4启动,发起一次选举。

​ 此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为1票。

​ 此时服务器4服从多数,更改选票信息为服务器3;

​ 服务器4并更改状态为FOLLOWING;

(5)服务器5启动,同4一样投票给3,此时服务器3一共5票,服务器5为0票;

​ 服务器5并更改状态为FOLLOWING;

最终Leader是服务器3,状态为LEADING;

其余服务器是Follower,状态为FOLLOWING。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值