n40ZB
b(bit) 位 最小数据单位 二进制 (0,1)
B(Byte) 字节 1B=8b 汉字占2个字节
KB 千字节 1KB=1024B 一小段文字
MB 兆字节 1MB=1024KB 一首歌(3-5M)
GB 吉字节 1GB=1024MB 一个视频
TB 太字节 1TB=1024GB 数据库(25张高清照片)
PB 拍字节 1PB=1024TB 公司,数据中心 2亿张照片
EB 艾字节 1EB=1024PB 全球级别 全球天气观测和气候的数据
ZB 泽字节 1ZB=1024EB 全球数量的总和
YB 尧字节 1YB=1024ZB
大模型:deepseek,chatgpt,豆包
大数据:hadoop 分布式计算系统+分布式数据库+Spark
hadoop(HDFS+MAPREDUCE+yarn) spark hive hbase
怎么学:带电脑,csdn(deepseek+豆包),实验部分耐心,不怕难
分布式计算存储:E-R
1.1大数据定义

TB PB EB
结构化数据:固定格式 关系型数据库,excel
半结构化数据:具有一定的层次结构,介于结构化数据与非结构化数据之间 json文件,xml文件,电子邮件
非结构化数据:无固定格式 文本文件,图片,音乐
快速化
价值密度低
大数据技术要面对的基本问题,也是最核心的问题:就是海量数据如何可靠存储和高效计算。
leetcode
1.2 Google的三驾马车
GFS:The Google File System HDFS
MapReduce:Simplfied Data Processing on Large Clusters大型集群上的简单数据处理 MapReduce
Bigtable:A Distributed Storage System for Structured Data一个分布式的结构化数据存储系统 Hbase
1.2.1.GFS的思想

GFS架构:
(1)GFS Master 节点管理所有的文件系统元数据,包括命名空间、访问控制权、文件和块的映射信息以及当前块的位置信息。
(2)GFS存储的文件都被分割成固定大小的块,每个块都会被复制到多个块服务器上(可靠性)。块的冗余度默认为3。
(3)GFS Master还管理着系统范围内的活动,比如块服务器之间的数据迁移等。
(4)GFS Master与每个块服务器通信(发送心跳包),发送指令,获取状态。
副本位置选择的策略要满足两个目标:最大化数据的可靠性和可用性。
1.2.2MapReduce的思想

MapReduce采用“分而治之”的思想,把对大规模数据集的操作,分发给一个主节点管理下的
各个子节点共同完成,然后整合各个子节点的中间结果,得到最终的计算结果。MapReduce就是“分散任务,汇总结果”。
Map的输出是Reduce的输入。
1.2.3BigTable的思想

1.3 Hadoop的概述
1.3.1Hadoop是什么?
(1)Hadoop是一个由Apache基金会所开发的分布式计算基础框架。 分布式不是一台机器能搞定的
(2)主要解决,海量数据的存储和海量数据的分析计算问题。
(3)广义上来讲,Hadoop通常是指一个更广泛的概念——Hadoop生态圈。
1.3.2Hadoop根据是Google三篇论文实现
Hadoop根据是Google三篇论文实现
HDFS GFS
MapReduce MapReduce
HBase BigTable
HDFS:Hadoop Distributed File System,是Hadoop项目的核心子项目,是分布式计算中数据存储管理的基础。


1.3.3Hadoop发表


1.3.4Hadoop的优势
高可靠性:Hadoop底层维护多个数据副本,所以即使Hadoop某个计算元素或存储出现故障,也不会导致数据的丢失。
高扩展性:在集群分配任务数据,可方便的扩展数以千计的节点。
高效性:在MapReduce的思想下,Hadoop是并行工作的,以加快任务处理速度。
高容错性:能够自动将失败的任务从新分配。
1.3.5Hadoop生态圈
| HDFS | 分布式文件系统 |
| YARN | 资源管理和调度器 |
| MapReduce | 分布式并行编程模型 |
| HBase | Hadoop上的非关系型的分布式数据库 |
| Hive | Hadoop上的数据仓库 |
| Pig | 一个基于Hadoop的大规模数据分析平台,提供类似SQL的查询语言Pig Latin |
| Flume | 一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统 |
| Sqoop | 用于在Hadoop与传统数据库之间进行数据传递 |
| Zookeeper | 提供分布式协调一致性服务 |
| Ambari | Hadoop快速部署工具,支持Apache Hadoop集群的供应、管理和监控 |
| Mahout | 提供一些可扩展的机器学习领域经典算法的实现 |
| Spark | 类似于Hadoop MapReduce的通用并行框架 |
1.4Hadoop的组成
1.4.1HDFS架构概述

NameNode:存储文件的元数据,如文件名、文件目录结构、文件属性(生成时间、副本数、文件权限),以及每个文件的块列表和块所在的DataNode等。
DataNode:在本地文件系统存储文件块数据,以及块数据的校验和。
Secondary NameNode:每隔一段时间对NameNode元数据备份。(Secondary NameNode是NameNode的高可用×,Secondary NameNode只是和NameNode近似一致,不是完全一致)
1.4.2Yarn架构概述
ResourceManager:整个集群资源(内存、cpu等)的老大。
NodeManager:单个节点服务器资源老大。
ApplicationMaster:单个任务运行的老大。(1)负责数据的切分(2)为应用程序申请资源并分配内部任务(3)任务的监控与容错
Container:容器,相当于一台独立的服务器,里面封装了任务运行所需要的资源,如内存、cpu、磁盘、网络等。
1.4.3Mapreduce架构
Mapreduce将计算过程分为两个阶段:Map和Reduce(这两个之间有一个任务shuffle)
Map阶段并行处理输入数据
Reduce阶段对Map结果进行汇总
2.环境安装
用来将一个Docker镜像从/cg/images/hadoop_node.tar.gz压缩包加载到本地Docker环境里面
docker load < /cg/images/hadoop_node.tar.gz

docker run --name master --privileged --ulimit nofile=65535:65535 --hostname master --ip 172.18.0.2 --add-host=slave1:172.18.0.3 --add-host=slave2:172.18.0.4 --add-host=slave3:172.18.0.5 -itd -v /cgsrc:/cgsrc:ro -v /headless/course/:/course hadoop_node /service_start.sh
docker run --name slave1 --privileged --ulimit nofile=65535:65535 --hostname slave1 --ip 172.18.0.3 --add-host=master:172.18.0.2 --add-host=slave2:172.18.0.4 --add-host=slave3:172.18.0.5 -itd -v /cgsrc:/cgsrc:ro hadoop_node /service_start.sh
docker run --name slave2 --privileged --ulimit nofile=65535:65535 --hostname slave2 --ip 172.18.0.4 --add-host=master:172.18.0.2 --add-host=slave1:172.18.0.3 --add-host=slave3:172.18.0.5 -itd -v /cgsrc:/cgsrc:ro hadoop_node /service_start.sh
docker run --name slave3 --privileged --ulimit nofile=65535:65535 --hostname slave3 --ip 172.18.0.5 --add-host=master:172.18.0.2 --add-host=slave1:172.18.0.3 --add-host=slave2:172.18.0.4 -itd -v /cgsrc:/cgsrc:ro hadoop_node /service_start.sh
docker run --name master --privileged --ulimit nofile=65535:65535 --hostname master --ip 172.18.0.2 --add-host=slave1:172.18.0.3 --add-host=slave2:172.18.0.4 --add-host=slave3:172.18.0.5 -itd -v /cgsrc:/cgsrc:ro -v /headless/course/:/course hadoop_node /service_start.sh
docker run创建并启动一个docker容器
--name master:容器的名字叫master
--privileged:赋予容器特权模式。允许它访问主机上所有设备
--ulimit nofile=65535:65535 设置容器的最大文件描述符数量(软限制/硬限制)。默认值较小,Hadoop、HDFS等大数据组件会频繁打开文件/网络连接,必须调高避免“too many open files”错误。
集群(跟多台电脑,组成的一个集合。)
2.docker ps 查看所有启动的容器
docker ps -a 查看所有的容器

启动容器
docker start 容器的名字
3.进入容器
docker exec -it --privileged 容器的名字 /bin/bash

4.安装java环境
1.创建java文件夹
cd /usr/local
mkdir 创建的文件夹

2.
cp 被复制文件的位置 最后文件放到的位置
cp /cgsrc/jdk-8u171-linux-x64.tar.gz /usr/local/java/

ll显示详细信息
3.解压
tar -zxvf 压缩包的名字
tar -zxvf jdk-8u171-linux-x64.tar.gz

4.删除
rm -f 要删除的文件

5.编辑环境变量
编辑文件的内容vim
vim 文件的名字
vim ~/bashrc

o下一行输入
i这一行输入
6.让环境变量生效
source ~/.bashrc

1.配置分布式模式
HDFS:NameNode(1个) DataNode(多个)SecondaryNameNode(1个)
NataNode和SecondaryNameNode尽量不要安装在同一台服务器(一个坏了都坏了)
YARN:ResourceManager(1个)NodeManager(多个)
ResourceManager很消耗内存,尽量不要和NameNode,SecondaryNameNode放在同一台机器上
| hadoop1 | hadoop2 | hadoop3 | hadoop4 |
|
NameNode | DataNode | DataNode |
DataNode SecondaryNameNode |
| NodeManager |
NodeManager ResourceManager | NodeManager | NodeManager |
2.ping ip地址:ICMP协议,测试两台计算机之间的连通性(OSI第三层)
ping master
ssh(.ssh免密登录),结合CG看
Secure Shell(安全外壳协议)
一台电脑 --->(去控制) 另外一台电脑(有密码才行)dddddddd
客户端 ---》服务端
场景:杜盈寄一个箱子给龙佳,这个箱子比较私密,中途不能让人打开。
1.龙佳打造一把锁(公钥)和一把钥匙(私钥)。
2.龙佳把这把锁(公钥)给了杜盈,但是钥匙龙佳自己要藏好。(把公钥放到服务器)
3.杜盈把箱子用龙佳给她的锁锁上。(用公钥加密)
4.箱子寄出去,中途任何没有钥匙的人都打不开。
5.只有龙佳可以用她的钥匙打开。
龙佳:客户端
杜盈:服务端

1.
生成公钥和私钥
ssh-keygen -t rsa
rsa是名字
.ssh文件夹下的文件功能解释:
| known_hosts | 记录ssh访问过计算机的公钥(public key) |
| id_rsa | 生成的私钥 |
| id_rsa.pub | 生成的公钥 |
| authourized_key | 存放授权过的无密码登录服务器公钥 |
2.
将公钥文件追加到另一个文件authorized_keys中
cat ./id_rsa.pub >> /authorized_keys
3.scp从一台电脑的~/.ssh/id_rsa.pub的文件,复制到slave1这台电脑上,用户是root,放到了slave1里面的/root这个路径下面
scp ~/.ssh/id_rsa.pub root@slave1:/root
安全拷贝
1.从hadoop102服务器上将本服务器的文件推给hadoop103:
scp -r jdk1.8.0_212/ lotus@hadoop103:/opt/module/
2.在hadoop103服务器上操作,将hadoop102的文件拿过来
scp -r lotus@hadoop102:/opt/module/jdk1.8.0_212/ ./

同理hadoop也可以这么操作
scp -r lotus@hadoop102:/opt/module/hadoop-3.1.3/ ./
3.在103服务器上,将102的文件拷贝到104
scp -r lotus@hadoop102:/opt/module/* lotus@hadoop104:/opt/module/

master登录slave1,slave2,slave3(免密)
任务slave1登录到master(没有密码)
实现场景:在slave1里面执行命令ssh master,就可以不输入密码master
1.slave1先生成私钥和公钥
2.slave1的公钥给到master
scp
3.在master上 公钥给放到~/.ssh/suthorized_keys
4.去slave1里面测试 ssh master
安装Hadoop(结合CG)
1.将hadoop包复制到/usr/local下面
cp /cgsrc/hadoop-3.4.0/tar.gz /usr/local
2.解压
tar 解压、压缩
-z 使用gzip解压
x 解压文件
v显示解压过程
f 指定文件名
tar -zxvf hadoop-3.4.0.tar.gz
3.修改名字
mv 旧的名字 新名字
mv hadoop-3.4.0 hadoop
4.修改配置文件
vim ~/.bashrc
在配置文件添加hadoop的相关路径
export PATH=$PATH:/usr/local/hadoop/bin:/usr/local/hadoop/sbin
让配置文件生效

5.查看hadoop的版本
hadoop version


1.bin:存放操作命令,具体包含如(hdfs,mapred,yarn)
2.etc:所有配置文件
3.include:头文件
4.lib:本地库(native库)压缩的动态链接库
5.libexec:拓展库
6.sbin:集群相关的命令
7.share:学习的资料、文档
配置集群环境
所有的配置文件都在/etc/hadoop/下
/usr/local/hadoop/etc/hadoop/的配置文件下的
workers(DataNode节点)
core-site.xml 整个集群的核心配置
hdfs-site.xml 配置hdfs
mapred-site.xml 配置mapreduce
yarn-site.xml 配置yarn
hadoop-env.sh
1.编辑workers
vim workers
master slave1 slave2 slave3
2.修改core-site.xml
9000是内部连接的接口,core-site中fs.defaultFS规定的
<name>fs.defaultFS</name>
<value>hdfs://master:9000</value>这两行记住
<configuration>
<!--默认文件系统的URI地址(内部接口)-->
<property>
<name>fs.defaultFS</name>
<value>hdfs://master:9000</value>
</property>
<!--临时工作目录(默认一个月就删除)-->
<property>
<name>hadoop.tmp.dir</name>
<value>file:/usr/local/hadoop/tmp</value>
</property>
</configuration>
3.修改hdfs-site.xml
<name>dfs.replication</name>
<value>3</value>代表副本的个数
<configuration>
<!--secondary namenode-->
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>master:50090</value>
</property>
<!--hdfs存数据的份数-->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<!--namenode 元数据所在的物理地址-->
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/usr/local/hadoop/tmp/dfs/name</value>
</property>
<!--datanode 数据所存储的物理地址-->
<property>
<name>dfs.datanode.data.dir</name>
<value>file:/usr/local/hadoop/tmp/dfs/data</value>
</property>
</configuration>
4.配置mapreduce
跑的时候要起yarn,start-yarn.sh
<configuration>
<!--mapreduce程序运行在yarn上-->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<!--job的服务器端地址(内部通讯端口)-->
<property>
<name>mapreduce.jobhistory.address</name>
<value>master:10020</value>
</property>
<!--历史服务器web端地址-->
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>master:19888</value>
</property>
</configuration>
5.任务 配置yarn
<configuration>
<!--指定resource Manager的主机名在master上-->
<property>
<name>yarn.resourcemanager.hostname</name>
<value>master</value>
</property>
<!--NodeManager提供的辅助服务,运行Mapreduce必配-->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<!--nodeManager监控本地磁盘的健康状况(磁盘上限98.5%)-->
<property>
<name>yarn.nodemanager.disk-health-checker.max-disk-utilization-per-disk-percentage</name>
<value>98.5</value>
</property>
</configuration>
配置slave节点
1.将/usr/local/下面的hadoop文件压缩成hadoop.master.tar.gz,并放到root文件夹下
tar -zcvf ~/hadoop.master.tar.gz ./hadoop
2.将master这台机器上的root/hadoop.master.tar.gz这个压缩包上传复制到slave1这台机器的/root路径下,并且登录slave1的账户是root

3.将~/hadoop.master.tar.gz的这个文件解压,而且解压到/usr/local文件夹下面
-z:gz 格式
c:创建压缩包
x:解压
v:显示过程
f:指定文件
tar -zxvf ~/hadoop.master.tar.gz -C /usr/local/
格式化节点,配置集群的时候执行一次
hdfs namenode -format(格式化命令,只能执行一次,首次启动的时候执行)
启动hdfs
start-dfs.sh
master节点:namenode secondarynamenode DataNode(workers如果有localhost,就有)
slave1,2,3:datanode
说明(core-site.xml hdfs-site.xml有问题)




172.18.0.2:9870

启动yarn,必须是在启动了hdfs的前提条件下
stsrt-yarn.sh(/usr/local/hadoop/sbin) stop-yarn.sh(关闭命令)
master节点:resourcemanager,nodemanager
slave1,2,3:nodemanager




172.18.0.2:8088

3.环境测试
启动hdfs start-dfs.sh
namenode(1个) secondarynamenode(1个) datanode(slave1,slave2,slave3,master)
启动yarn start-yarn.sh
resourcemanager(1个) nodemanager(master,slave1,slave2,slave3)
启动顺序:start-dfs.sh --》start-yarn.sh
关闭顺序:start-yarn.sh --》start-dfs.sh
172.18.0.2:9870 172.18.0.2(代表namenode所在机器的ip地址) 9870是默认的web端访问hdfs的端口号
172.18.0.2:8088 172.18.0.2(代表resource manager的ip地址) 8088是默认的web端访问yarn的端口号
测试hdfs上传文件
1.创建一个本地文件longjia.txt
vi 文件名名字


2.创建一个hdfs的文件夹
hadoop fs -mkdir 文件夹的路径和名字


3.将本地文件上传到hdfs系统里面的文件夹里
hadoop fs -put 本地文件的路径和名字 要上传到hdfs这个系统的具体路径

4.查看文件是否上传成功
引入:QQ邮箱 123456@qq.com,逻辑地址(并不是真正意义上存放文件的地址)
物理地址:真实存放文件的地址
hdfs系统
逻辑地址:/lInput/longjia.txt
物理地址:hdfs里面配置了/usr/local/hadoop/tmp/dfs/data
file:/usr/local/hadoop/tmp/dfs/data
/usr/local/hadoop/tmp/dfs/data/current/BP-359494161-172.18.0.2-1774617058921/current/finalized/subdir0/subdir0

问题:hdfs存放数据,datanode真正存放数据的节点。hdfs默认存三份
master,slave1,slave2,salve3存放哪几台机器上了?
如下图:存放位置在slave2,masetr,slave3上面

测试大文件上传
1.本地有一个大文件

cp 被复制文件的地址和文件名 粘贴到哪儿的地址
![]()

2.在HDFS系统里面创建一个新的文件夹


3.本地大文件上传到新的文件夹

4.查看,反思


hdfs系统存放文件是按照块存储。如果是大文件,分成多个块,分别存储。
hdfs:/bigFiles/jdk-8u171-linux-x64.tsr.gz
物理地址(实际存放的位置)/usr/local/hadoop/tmp/dfs/data hdfs-site.xml


cat blk_1073741826 >> tmp.tar.gz 将blk_1073741826追加tmp.tar.gz

wordcount测试
副线任务:
修改配置文件mapred-site.xml(在master,slave1,slave2,slave3上都执行)
为什么配那个属性?


主线任务:
上一节课上传了小文件,上传到HDFS(/lInput/longjia.txt),我们统计这个文件里面每个单词出现的频率。
1.确保你的HDFS系统里面/lInput/longjia.txt这个文件是存在的。

2.测试wordcount是否可以执行成果。
cd /usr/local/hadoop/share/hadoop/mapreduce

hadoop jar hadoop-mapreduce-examples-3.4.0.jar wordcount /lInput/longjia.txt /lOutput
hadoop jar jar包的名字 调用方法的的名字(wordcount) 输入路径(/lInput/longjia.txt这个输入文件必须存在) 输出路径(必须不存在,如果已经存在会报错)
3.解读wordcount运行过程和结果展示

mapreduce :map+reduce
是否成功:job completed successfully
多了一个lOutput,说明有输出

真正的输出结果在part-r-ooooo

查看结果
hadoop fs -cat 文件路径和文件名

4.面试重点
1.常用端口号
hadoop3.X
| HDFS NameNode内部通常端口 | 8020/9000/9820 |
| HDFS NameNode对用户的查询端口 | 9870 |
| Yarn查看任务运行情况的 | 8088 |
| 历史服务器 | 19888 |
hadoop2.X
| HDFS NameNode内部通常端口 | 8020/9000 |
| HDFS NameNode对用户的查询端口 | 50070 |
| Yarn查看任务运行情况的 | 8088 |
| 历史服务器 | 19888 |
2.常用的配置文件
3.X core-site.xml hdfs-site.xml yarn-site.xml mapred-site.xml works (/hadoop/etc/hadoop)
2.X core-site.xml hdfs-site.xml yarn-site.xml mapred-site.xml slaves
HDFS
一、HDFS概述
1.1HDFS产生背景
随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统。HDFS只是分布式文件管理系统中的一种。
1.2HDFS定义
HDFS(Hadoop Distributed Files System),它是一个文件系统,用于存储文件,通过目录树来定义文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。
HDFS的使用场景:适合一次写入,多次读出的场景,且不支持文件的修改,适合用来做数据分析,并不适合来做网盘应用。
1.3HDFS的优缺点
优点:
(1)高容错性
- 数据自动保持多个副本。它通过增加副本的形式,提高容错性。
- 其一个副本丢失以后,它可以自动恢复。
(2)适合处理大数据
- 数据规模:能够处理数据规模达到GB、TB、甚至PB级别的数据。
- 文件规模:能够处理百万规模以上的文件数量,数量相当之大。
(3)可构建在廉价机器上,通过多副本机制,提高可靠性。
缺点:
(1)不适合低延时数据访问,比如毫秒级的存储数据,是做不到的。
(2)无法高效的对大量小文件进行存储。
- 存储大量小文件的话,它会占用NameNode大量的内存来存储文件目录和块信息。这样是不可取的,因为NameNode的内存总是有限的;
- 小文件存储的寻址时间会超过读取时间,它违反了HDFS的设计目标。
(3)不支持并发写入,文件随机修改。
一和文件只能有一个写,不允许多个线程同时写;
仅支持数据append(追加),不支持文件的随机修改。
1.4HDFS组成架构
https://hadoop.apache.org

NameNode(nn):就是Master(老板),它是一个主管、管理者。
- 管理HDFS的名称空间;
- 配置副本策略;
- 管理数据块的映射信息;
- 处理客户端的续写请求。
DataNode:就是Slave.NameNode下达命令。DataNode执行实际的操作。
- 存储实际的数据块。
- 执行数据块的读/写操作。
Client:就是客户端
- 文件切分。文件上传HDFS的时候,Client将文件切分成一个一个Block,然后进行上传。(我上传longjia.txt文件,文件大小200M,正常默认一个Block 134217728B/1024/1024=128M,存两个Block,第一个128M,第二个72M)
- 与NameNode交互,获取文件的位置信息;
- 与DataNode交互,读取或者写入数据;
- Client提供一些命令来管理HDFS,比如NameNode格式化;
- Client可以通过一些命令来访问HDFS,比如对HDFS增删改查操作。
Secondary NameNode:并非NameNode的热备(实时保持一致),当NameNode挂掉的时候,它不能马上替换NameNode并提供服务。(Fsimage和Edits是干什么的,原理是什么)
Fsimage 是 HDFS 里 NameNode 元数据的全量快照文件。
Fsimage 是 NameNode 内存中元数据在某一时刻的全量持久化镜像。
Edits 是 记录元数据变更操作的增量日志文件。
Edits 是记录元数据所有变更操作的增量日志。
- 辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode;
- 在紧急情况下,可辅助恢复NameNode。
作业Fsimage和Edits:
内存(快,断电容易丢失) edits:编辑文件
硬盘 Fsimage:为了防止断电数据丢失,会落盘为fsimage
以上内容全部存为fsimage1
edits1:2026年4月14日10点30-10点40新的东西
edits2:2026年4月14日10点40-10点50新的东西
Secondary NameNode:fsimage2=fsimage1+edits1+edits2
同步传给NameNode,会存fsimage2


secondary namenode

只不过namenode会多一个edits inprogress
1.namenode fsimage edits 会跟secondarynamenode同步
2.secondarynamenode会把 fsimage edits 加起来得到一个新的image,传回给namenode
namenode(物理地址)
/usr/local/hadoop/tmp/dfs
1.5HDFS文件块大小(面试重点)
HDFS中的文件在物理上是分块存储(Block),块的大小可以通过参数配置(dfs.blocksize)来规定,默认大小在Hadoop2.X版本是128M,老版本是64M
block1 block2 block3 block4 ......block65 block66
1.如果寻址时间约为10ms,即找到目标block的时间为10ms(计算机组成原理)
2.寻址时间为传输时间的1%时,则为最佳状态。传输时间=10ms/0.01=1000ms=1s
3.目前磁盘的传输速率普遍为100MB/s 块大小:100MB/s*1s=100M 128M
普通机械硬盘 80M/s-90M/s 块大小:80-90M 128M,64M
固定硬盘 200M/s-300M/s 块大小:200M-300M 256M
思考:为什么块的大小不能设置太小,也不能设置太大?
(1)HDFS的块设置太小,会增加寻址时间,程序一直在找块的开始位置。(找目标)
(2)如果块设置太大,从磁盘传输数据的时间会明显大于定位这个块开始位置的时间(寻址时间)。导致程序在处理这块数据时,会非常慢。
总结:HDFS块的大小设置主要取决于磁盘的传输速率。
1.6HDFS的Shell操作(10分)(要写报告)(必考)
1.基本的语法
bin/hadoop fs 具体命令
bin/hdfs dfs 具体命令
两个完全相同


2.常用命令
(0)启动Hadoop集群
sbin/start-dfs.sh 启动hdfs系统
sbin/start-yarn.sh 启动yarn系统
(1)-help:输出这个命令参数(应该不考)
hadoop fs -help rm:输出rm这个命令的相关解释以及参数说明

(2)-ls:显示目录信息
如果出现两个DataNode,则有错,重启(70%的错都重启)


(3)-mkdir:在HDFS上创建文件夹
hadoop fs -mkdir -p /wangzherongyao/zhonglu

(4)-moveFromLocal:从本地剪切粘贴到HDFS(删除原本的)
1.本地有一个文件
touch buzhihuowu.txt 创建buzhihuowu.txt这个空文件

2.将本地的文件剪切,粘贴到HDFS系统里面
hadoop fs -moveFromLocal 本地文件路径 HDFS的路径


(5)-appendToFile:追加一个文件到已经存在的文件末尾。(中单的英雄小乔。放在zhonglu的文件夹,不用重新创建文件夹)





1.本地文件(一般是有内容的)
2.把本地文件的内容发放到(HDFS系统里面已经存在的)文件的末尾。
hadoop fs -appendToFile 本地文件HDFS的文件:把本地文件的内容放到HDFS文件的末尾

(6)-cat:显示文件内容

(7)-chgrp,chmod,chown:Linux文件系统中的用法一样,修改文件所属权限
(ch:change;grp:group分组;mod:mode;own:owner拥有者)
chgrp[R] 组名 文件或者目录名:更改文件/目录所属的用户组
hadoop fs -chgrp 新改的组名 文件或者文件夹

chown:修改文件/目录所有者
hadoop fs -chown 更改为所有者:组名 文件或者文件夹


chmod:[用户类别][操作符][权限]文件/目录 (修改文件/目录的权限)
1.用户类别(不用看)
u:所有者(user)
g:所属者组(group)
o:其他用户(others)
a:所有用户(all,默认值)
2.操作符
+:添加权限
-:移除权限
=:直接设置权限(覆盖所有的权限)
3.权限
r:读
w:写
x:执行
| 原有的权限 | 命令 | 现有的权限 |
| user原本rw- | chmod u+x script.sh | user的权限rwx |
| group的权限rw- | chmod g-w data.txt | group的权限r-- |
| others的权限rwx | chmod o=r file.txt | others的权限r-- |
数字模式:
chmod [数字组合]文件/目录
1.权限对应数字
r读=4
w写=2
x执行=1
-(无权限)=0
2.数字组合规则
将三类用户(所有者,组,其他用户)的权限,得到三位数字
第一位:所有者权限
第二位:组权限
第三位:其他用户权限
例1:rwxr-xr--
所有者:rwx 4+2+1=7
组:r-x 4+0+1=5
其他用户:r-- 4+0+0=4
chmod 754 file.txt
例2:rw-r--r--
所有者:rw- 4+2+0=6
组:r-- 4+0+0=4
其他用户:r-- 4+0+0=4
chmod 644 file.txt

(8)-copyFromLocal:从本地文件系统中拷贝文件到HDFS路径去(-put)


(9)-copyToLocal:从HDFS拷贝到本地(-get)


(10)-cp:从HDFS的一个路径拷贝到HDFS的另一个路径
1.创建一个其他分路的文件夹


2.我们把王昭君从中路复制一个到游走


(11)mv:在HDFS目录中移动文件
1.先在HDFS创建一个打野的文件夹。

2.把不知火舞从中路移到打野的位置。



(12)-get:等同于copyToLocal,从HDFS下载文件到本地
hadoop fs -get hdfs系统里面的文件 下载到本地的存放路径

(13)-put:等同于copyFromLocal。上传本地文件到HDFS系统里面。
hadoop fs -put 本地文件 HDFS系统的存放路径

(14)-tail:显示一个文件的末尾

(15)-rm:删除文件或文件夹

hadoop fs -rm -r hdfs文件夹:删除hdfs系统里面的这个文件夹以及文件夹下面的所有文件
-r 代表遍历

(16)-du 统计文件夹的大小信息(作业:自己查一下解释)
-s:-s:汇总显示指定目录的总大小,而不是列出每个子项。
-h:以人类可读的格式(KB/MB/GB 等)显示大小。
118:该目录实际占用的物理空间大小(含副本前的原始数据大小)。
354:该目录实际占用的总物理空间大小(按副本数计算后的大小,这里副本数为 3,111×3=333)。

(17)-setrep:设置HDFS文件的副本数量
hadoop fs -setrep 副本数量

(18)-touchz:在hdfs系统里创建一个空的文件(考,要会)
在hdfs系统里面创建一个名为two.txt的空文件
hadoop fs -touchz two.txt
1.7HDFS客户端操作(开发重点)(重点放在代码部分)
master(shell),slave1,slave2,slave3
hdfs客户端代码(集群外的一台电脑) 可以控制整个集群(master,slave1,slave2,slave3)
上传
1.pom.xml
<!--dependencies代表这个项目所有的依赖-->
<dependencies>
<!--junit单元测试类-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--lod4j打印日志,日志等级-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.0</version>
</dependency>
<!--hadoop的客户端-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.3</version>
</dependency>
</dependencies>
2.log4j2.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>
3.HdfsClient
package org.longjia;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.junit.Test;
import java.io.IOException;
import java.net.URI;
public class HdfsClient {
@Test
public void testMkdir() throws IOException, InterruptedException {
//1.创建HDFS的客户端对象fileSystem,(发送了uri网址,配置对象,用户),目的是请求连接集群
FileSystem fileSystem=FileSystem.get(URI.create("hdfs://172.18.0.2:9000"),new Configuration(),"root");
//2.登录HDFS成功,可以对HDFS做操作,所以我在HDFS系统里创建了一个aiqinggongyu的文件夹
fileSystem.mkdirs(new Path("/aiqinggongyu"));
//3.退出登录,关闭资源
fileSystem.close();
}
}
package org.longjia;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.junit.Test;
import java.io.IOException;
import java.net.URI;
public class HdfsClient {
@Test
public void testMkdir() throws IOException, InterruptedException {
//1.创建HDFS的客户端对象fileSystem,(发送了uri网址,配置对象,用户),目的是请求连接集群
URI uri=new URI("hdfs://172.18.0.2(namenode的地址):9000");
Configuration conf=new Configuration();
String user1="root";
//FileSystem.get不一定有三个参数,也有可能是1或者2个
FileSystem fileSystem=FileSystem.get(uri,conf,user1);
//2.登录HDFS成功,可以对HDFS做操作,所以我在HDFS系统里创建了一个aiqinggongyu的文件夹
fileSystem.mkdirs(new Path("/aiqinggongyu"));
//3.退出登录,关闭资源
fileSystem.close();
}
}
@Test
public void testCopyFromLocalFile() throws URISyntaxException, IOException, InterruptedException {
//1.
//连接集群的namenode地址,HDFS的链接地址
URI uri=new URI("hdfs://172.18.0.2:9000");
//创建一个配置文件
Configuration configuration=new Configuration();
//登录用户
String user="root";
//获取到HDFS客户端对象
FileSystem fileSystem=FileSystem.get(uri,configuration,user);
//2.操作HDFS系统
//copyFromLocalFile 从本地上传文件到HDFS系统
//参数1:表示是否删除本地的原数据,参数2:表示是否覆盖HDFS里面已经存在的文件
//参数3:需要上传的本地文件的路径地址,参数4:HDFS的目标地址(要上传到HDFS的那个位置的地址)
fileSystem.copyFromLocalFile(false,true,new Path("/root/IdeaProjects/HdfsDemo/src/main/resources/zengxiaoxian.txt"),new Path("/aiqinggongyu"));
//3.关闭资源
fileSystem.close();
}
}
public class HdfsClientV1 {
private FileSystem fileSystem;
@Before
public void init() throws URISyntaxException, IOException, InterruptedException {
//1.
URI uri=new URI("hdfs://172.18.0.2:9000");
Configuration configuration=new Configuration();
String user="root";
fileSystem=FileSystem.get(uri,configuration,user);
}
@After
public void close() throws IOException {
//3.
fileSystem.close();
}
@Test
public void testCopyFromLocalFile() throws URISyntaxException, IOException, InterruptedException {
//2.
fileSystem.copyFromLocalFile(false,true,new Path("/root/IdeaProjects/HdfsDemo/src/main/resources/zengxiaoxian.txt"),new Path("/aiqinggongyu"));
}
}
测试参数优先级
(1)客户端代码中设置的值 (2)ClassPath下用户自定义配置文件
(3)服务器自定义配置(hadoop/etc/hadoop/xxx-site.xml)
(4)服务器的自定义配置(xxx-default.xml)





下载
public void testCopyToLocalFile() throws IOException {
//copyToLocalFile从HDFS系统里面下载文件到本地
//参数1 boolean delsrc:是否删除HDFS上的源文件
//参数2 Path src:HDFS上要被下载的文件路径
//参数3 Path dst:要将文件下载到本地的路径
//参数4 boolean useRawLocalFileSystem:是否开启文件严重(false下载完会有两个文件,crc检验文件;但如果是true,就不会有crc检验文件)
fileSystem.copyToLocalFile(false,
new Path("/wangzherongyao/youzou/wangzhaojun.txt"),
new Path("/root/IdeaProjects/HdfsDemo/src/main/resources/"),
false);
}


作业:测试上面两个boolean类型变成true会有什么效果(xiaoqiao.txt)

public void testCopyToLocalFile() throws IOException {
fileSystem.copyToLocalFile(true,
new Path("/wangzherongyao/zhonglu/xiaoqiao.txt"),
new Path("/root/IdeaProjects/HdfsDemo/src/main/resources/"),
true);
}


删除

public void testDelete() throws IOException {
File file=new File("/aiqinggongyu/zengxiaoxian.txt");
//exists()判断文件是否存在
if(file.exists()){
System.out.print("存在")
}
//delete删除HDFS里面的文件或者文件夹
//参数1:要删除的文件或者是文件夹
//参数2:是否遍历
fileSystem.delete(new Path("/aiqinggongyu/zengxiaoxian.txt"),false);
}
当参数2是false时,要删除的文件夹必须是空文件夹,如果不是空文件夹会报错。
若要删除的文件夹不是空文件夹,参数2应该为true。

HDFS文件名更改/移动
(1)文件名更改

public void testRename() throws IOException {
//rename()这个方法是用来修改文件名字或者移动文件的
//参数1:HDFS系统原本文件的路径和名字
//参数2:HDFS系统里被修改后的名字和路径
fileSystem.rename(new Path("/wangzherongyao/zhonglu/anqila.txt"),
new Path("/wangzherongyao/zhonglu/shuangmawei.txt"));
}
结果图:

(2)文件移动

public void testRename() throws IOException {
fileSystem.rename(new Path("/wangzherongyao/zhonglu/shuangmawei.txt"),
new Path("/anqila.txt"));
}
结果图:

HDFS文件详情查看
查看文件名称、权限、长度、块信息
public void testListFiles() throws IOException {
//listFiles()这个方法是返回/目录下所有的子文件和子目录的详细信息,包括文件的长度、块大小、备份数、修改时间、所有者、权限
//参数1:要遍历的目标起始路径
//参数2:boolean recursive:是否递归遍历子目录。如果设置为true时,会返回路径下所有子目录中的文件,false时仅返回当前目录下的直接文件
//左边:右边listFiles()这个方法返回来的所有信息都存储在左边listFiles里面
RemoteIterator<LocatedFileStatus> listFiles=fileSystem.listFiles(new Path("/"),
true);
//遍历
while(listFiles.hasNext()){
//status存储的listFiles里面的一条信息的对象
LocatedFileStatus status=listFiles.next();
//文件名称
System.out.println(status.getPath().getName());
//文件长度
System.out.println(status.getLen());
//文件权限
System.out.println(status.getPermission());
//文件的分组
System.out.println(status.getGroup());
System.out.println("------");
//获取一个文件存储的所有块信息,块信息存放在blockLocations(大文件会有多个块)
BlockLocation[] blockLocations=status.getBlockLocations();
//遍历一个文件的每个块
for (BlockLocation blockLocation:blockLocations){
//获取存储这个块的hosts(他会包含多个节点,这取决于备份数和datanode有多少节点)
String[] hosts = blockLocation.getHosts();
//遍历所有节点的hosts
for (String host : hosts) {
System.out.print(host+"|");
}
System.out.println();
}
System.out.println("+++++yitiaoxinxidejieshu+++++++++++++++++++++++");
}
}



HDFS判断是文件还是文件夹
(1)isFile()方法判断是不是文件

public void testListStatus() throws IOException {
//获取在HDFS系统里面/根目录下,所有文件以及文件夹的状态
FileStatus[] listStatus=fileSystem.listStatus(new Path("/"));
//遍历listStatus
for (FileStatus fileStatus:listStatus){
//isFile()方法判断是不是文件
if(fileStatus.isFile()){
System.out.println("wenjian:"+fileStatus.getPath().getName());
}else{
System.out.println("wenjianjia:"+fileStatus.getPath().getName());
}
}
}
}

(2)isDirectory()方法判断是不是文件夹

public void testListStatus() throws IOException {
FileStatus[] listStatus=fileSystem.listStatus(new Path("/"));
for (FileStatus fileStatus:listStatus){
if(fileStatus.isDirectory()){
System.out.println("wenjianjia:"+fileStatus.getPath().getName());
}else{
System.out.println("wenjian:"+fileStatus.getPath().getName());
}
}
}

hdfs写数据流程(面试重点)
(1)客户端通过Distriduted FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已经存在,父目录是否存在。(Name Node检查权限,检查目录结构)
(2)NameNode返回是否可以上传。
(3)客户端请求第一个Block上传到哪几个DataNode服务器上。
(4)NameNode返回3个DataNode节点,分别是dn1(DataNode1),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传输完成后,客户端再次请求Name Node上传第二个Block的服务器。(重复执行3-7)
网络拓扑-节点距离计算
在HDFS写数据的过程中,NameNode会选择距离待上传数据最近距离的DataNode接收数据。那么这个最近距离怎么计算呢?

节点距离:两个节点到达最近的共同祖先的距离总和。
Distance(d1/r1/n0,d1/r1/n0)=0;
Distance(d1/r1/n1,d1/r1/n2)=1+1=2;
Distance(d1/r2/n1,d1/r3/n2)=2+2=4;
Distance(d1/r2/n0,d2/r4/n1)=3+3=6;

机架感知(副本存储节点选择)
机架感知(replication副本存储节点选择):数据可靠性,传递速度快

第一个副本在Client所处的节点上。如果客户端在集群外,随机选一个。(传输速度快)
第二个副本在另一个机架的随机一个节点。(为了数据可靠性)
第三个副本在第二个副本所在机架的随机节点。(速度)
hdfs读取数据流程

(1)客户端通过Distriduted FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
(2)挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
(3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)
(4)客户端以Packet为单位接收,现在本地缓存,然后写入目标文件。
NameNode(nn)和secondarynamenode(2nn)
NameNode中元数据存储在哪儿?(内存、磁盘)
内存<------开机加载------fsimage存储元数据,edits追加的信息------关机合并到fsimage
NameNode工作机制

第一阶段:NameNode启动
(1)第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
(2)客户端对元数据进行增删改的请求。
(3)NmaeNode记录操作日志,更新滚动日志。
(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-------------(nn)
(8)NameNode将fsimage.chkpoint重新命名为fsimage。
DataNode工作机制

(1)一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度、块数据的校验和、以及时间戳。
(2)DataNode启动后会向NaeNode注册,通过后,周期性(1小时)的向NameNode上报所有的块信息。(跟老板汇报工作)
(3)心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另外一台机器,或删除某个数据块。(保持副本数)如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。
(4)集群运行中可以安全加入和退出一些机器。(datanode 10---->100)
MapReduce(出代码题)
1.1MapReduce定义
MapReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。
MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上。
1.2优缺点
优点
1.MapReduce易于编程
它简单的实现一些接口,就可以完成一个分布式程序,这个分布式程序可以分到大量廉价的PC机器上运行。也就是说你写一个分布式程序,跟写一个简单的串行程序是一模一样的。就是因为这个特点是得MapReduce编程变得很流行。
2.良好的扩展性
当你的计算资源不能得到满足的时候,你可以通过简单的增加机器来扩展他的计算能力。
3.高容错性
MapReduce设计的初衷就是使程序能够部署在廉价的PC机器上,这就要求它具有很高的容错性。比如其中有台机器挂了,它可以把上面的计算任务转移到另外一个节点上运行,不至于这个任务运行失败,而且这个过程不需要人工参与,而完全是由Hadoop内部完成的。
4.适合PB级以上海量数据的离线处理
可以实现上千台服务器集群并发工作,提供数据处理能力。
缺点
1.不擅长实时计算
MapReduce无法像MySQL一样,在毫秒或者秒级内返回结果。
2.不擅长流式计算
流式计算得输入数据是动态的,而MapRduce得输入数据集是静态的,不能动态变化。这是因为MapRduce自身的设计特点决定了数据源必须是静态的。
3.不擅长DGA(有向图)计算
多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。在这种情况下,MapReduce并不是不能做,而是使用后,每个MapReduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常的低下。
1.3MapReduce进程
一个完整的MapReduce程序在分布式运行时有三类实例进程:
(1)MrAppMaster:负责整个程序的过程调度及状态协调。(处于Yarn)
(2)MapTask:负责Map阶段的整个数据处理流程。
(3)ReduceTask:负责Reduce阶段的整个数据处理流程。

1.4常用数据序列化类型
| Java类型 | Hadoop Writable类型 |
| Boolean | BooleanWritable |
| Byte | ByteWritable |
| Int | IntWritable |
| Float | FloatWritable |
| Long | LongWritable |
| Double | DoubleWritable |
| String | Text |
| Map | MapWritable |
| Array | ArrayWritable |
1.5MapReduce编程规范
用户编程的程序分为三类部分:Mapper,Reducer和Driver
1.Mapper阶段
(1)用户自定义的Mapper要继承自己的父类
(2)Mapper的输入数据是KV对的形式(KV的类型可自定义)
(3)Mapper中的业务逻辑写在map()方法里面
(4)Mapper的输出数据是KV对的形式(KV的类型可自定义)
(5)map()方法(MapTask进程)对每一个<K,V>调用一次
2.Reduce的阶段
(1)用户自定义的Reducer要继承自己的父类
(2)Reducer的输入数据类型对应Mapper的输出数据类型,也是KV
(3)Reducer的业务逻辑写在reduce()方法中
(4)ReduceTask进程对每一组相同的<K,V>组调用一次reduce()方法
3.Driver阶段
相当于YARN集群的客户端,用于提交我们整个程序到YARN集群,提交的是封装了的MapReduce程序相关运行参数的job对象。
wordcount案例
(1)输入数据
I am somebody
I am smart and kind
I am important
I am starve of education
I have places to go
I have people to impress
Ihave world to change
(2)
I 7
am 4
and 1
Mapper
k1代表偏移量,v1这一行的内容
| I | a | m | s | o | m | e | b | o | d | y | 换行符 | |||
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
<0,I am somebody>
<14,I am smart and kind>
<29,I am important>
<40,I am starve of education>
<51,I have places to go>
<63,I have people to impress>
<77,Ihave world to change>
1.Mapper阶段
(1)用户自定义的Mapper要继承自己的父类
(2)Mapper的输入数据是KV对的形式(KV的类型可自定义)
(3)Mapper中的业务逻辑写在map()方法里面
(4)Mapper的输出数据是KV对的形式(KV的类型可自定义)
(5)map()方法(MapTask进程)对每一个<K,V>调用一次(我们有7行文件,所以有7对输出,所以我们会执行map()
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io,IOException;
//mapper输入key的类型:LongWritable
//mapper输入value的类型:Text
//mapper输出key的类型:Text
//mapper输出value的类型:IntWritable
public class wordCountMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
@Override
protected void map(LongWritable key,Text value,Context context) throws IOException,InterruptedException{
//将输入的value数据转换成String类型
String line=value.toString();
//将line这个字符串切割成单词
//line "I am somebody"
String[] words=line.split(" ");
//遍历words
for(String word:words){
//输出单词,还有单词的个数1(一定要注意输出的类型必须保持一致)
context.write(new Text(word),new IntWritable(1));
}
//输出
//<I,1>
//<am,1>
//<somebody,1>
}
}
Reduce
mapper的输入
<0,I am somebody> 会调用一次map()
<14,I am smart and kind> 会调用一次map()
<29,I am important> 会调用一次map()
<40,I am starve of education> 会调用一次map()
<51,I have places to go> 会调用一次map()
<63,I have people to impress> 会调用一次map()
<77,Ihave world to change> 会调用一次map()
mapper的输出
map()执行完后的结果<I,1>,<am,1>,<somebody,1>
map()执行完后的结果<I,1>,<am,1>,<smart,1>,<and,1>,<kinnd,1>
map()执行完后的结果<I,1>,<am,1>,<important,1>
map()执行完后的结果<I,1>,......
map()执行完后的结果<I,1>,......
map()执行完后的结果<I,1>,......
map()执行完后的结果<I,1>,......
shuffle过程
<I,<1,1,1,1,1,1,1>>
<am,<1,1,1>>
<sombody,1>
Reduce的输入
<I,<1,1,1,1,1,1,1>>
<am,<1,1,1>>
<sombody,1>
Reduce的输出
<I,7>
<am,3>
<somebody,1>
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
//reduce输入key的类型:Text
//reduce输入value的类型:IntWritable
//reduce输出key的类型:Text
//reduce输出value的类型:IntWritable
public class wordCountReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
@Override
protected void reduce(Text k3,Iterable<IntWritable> value3,Context context) throws IOException, InterruptedException {
//累加,初始单词个数是0
int total=0;
//遍历reduces输入的value
for(IntWritable v:value3){
total+=v.get();//total=total+=v.get();累加单词出现的次数
}
//reduce的输出<单词,单词的个数>
context.write(k3,new IntWritable(total));
}
}
Driver
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;
import java.io.IOException;
public class wordCountDriver {
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
Configuration configuration=new Configuration();
Job job=Job.getInstance(configuration);
job.setJarByClass(wordCountDriver.class);
job.setMapperClass(wordCountMapper.class);
job.setReducerClass(wordCountReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.setInputPaths(job,new Path("/root/IdeaProjects/mapReduceTest/src/main/resources/longjia.txt"));
FileOutputFormat.setOutputPath(job,new Path("/root/IdeaProjects/mapReduceTest/src/main/resources/output"));
boolean result=job.waitForCompletion(true);
System.exit(result?0:1);
}
}



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;
import java.io.IOException;
public class wordCountDriver {
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
//1.获取配置信息以及封装任务,获得一个job对象
Configuration configuration=new Configuration();
Job job=Job.getInstance(configuration);
//2.设置jar加载路径(就是自己的本类,生成的class文件)
job.setJarByClass(wordCountDriver.class);
//3.设置Mapper和Reducer类
job.setMapperClass(wordCountMapper.class);
job.setReducerClass(wordCountReducer.class);
//4.设置map输出的key和value
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//5.设置最终输出的key和value
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//6.设置输入和输出路径
FileInputFormat.setInputPaths(job,new Path("/root/IdeaProjects/mapReduceTest/src/main/resources/longjia.txt"));
FileOutputFormat.setOutputPath(job,new Path("/root/IdeaProjects/mapReduceTest/src/main/resources/output"));
//7.提交
boolean result=job.waitForCompletion(true);
System.exit(result?0:1);
}
}
本地测试
输入文件和输出文件都在本地
执行Driver类后,结果如下:

使用HDFS系统作为文件的输入输出测试
1.启动HDFS系统
2.修改Driver类代码的路径



3.执行Driver类,可以看到生成了anqilaOutput文件夹,具体结果如下:


在集群上测试
1.启动HDFS系统和yarn

2.修改pom.xml文件(注意:mainClass必须跟的是整个项目的启动类,即我们写的Driver类)
<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-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>WordCountMain</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
3.修改代码
将输入输出的路径设为可以输入的参数

4.打包(双击package,会生成一个target文件夹,文件夹下面会有两个jar包)

5.上传jar包到集群
目前我们所生成的jar包所在地址为cg这台电脑上面的/root/IdeaProjects/mapReduceTest/target/mapReduceTest-jar-with-dependencies.jar
现在需要上传到resourceManager所在电脑上(master)
scp /root/IdeaProjects/mapReduceTest/target/mapReduceTest-jar-with-dependencies.jar

查看是否上传成功,在master机器上执行

6.执行jar包
在master使用如下命令
hadoop jar mapReduceTest-jar-with-dependencies.jar /anqila.txt /yarnOutput

可以通过yarn的客户端查看运行的各个状态

7.最后结果


作业:
1.认真看一下driver的各行代码和注释
2.创建一个新的工程,随便叫啥名字
输入:
学号,姓名,第一次作业成绩,第二次作业成绩,第三次作业成绩
01,xiaohua,85,82,96
02,xiaoming,45,60,47
03,lily,85,49,90
04,rose,75,65,70
创建一个txt文件,文件内容如下:(输入)
01,xiaohua,85,82,96
02,xiaoming,45,60,47
03,lily,85,49,90
04,rose,75,65,70
输出:
学号,最好的一次成绩
01 96
02 60
03 90
04 75
01 96
02 60
03 90
04 75
Mapper类
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
//map 输入 key:LongWritable
//map 输入 value:Text 01,xiaohua,85,82,96
//map 输出 key:Text 01
//map 输出 value:IntWritable
public class zuihaochengjiMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
@Override
protected void map(LongWritable key,Text value,Context context) throws IOException, InterruptedException {
//1.Text类型转换成String类型
String line = value.toString();
//2.切割String类型,split(",")双引号里面是什么就按照什么切割 切割完是String的数组
parts{"01","xiaohua","85","82","96"}
String[] parts = line.split(",");
//3.xuehao=parts[0] 第一行就是01
String xuehao =parts[0];
//4.遍历三次成绩
for(int i=2;i<parts.length;i++){
//5.String类型转换成int类型
int score= Integer.parseInt(parts[i]);
//6.输出,输出的两个参数,第一个是输出的key,第二个输出的是value
context.write(new Text(xuehao),new IntWritable(score));
}
//shuchu
//01,85
//01,82
//01,96
}
}
Reducer类
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
//reduce 输入key:Text
//reduce 输入value:IntWritable
//reduce 输出key:Text
//reduce 输出value:IntWritable
public class zuihaochengjiReducer extends Reducer<Text,IntWritable,Text, IntWritable>{
@Override
//输入:01,<85,82,96>
//输出:01,96
protected void reduce(Text key,Iterable<IntWritable> values,Context context) throws IOException, InterruptedException, IOException {
int max=0;
//遍历所有成绩,找到最高的成绩
for(IntWritable value:values){
if(max<value.get()){
//注意这里获取具体的值用get()方法
max=value.get();}
}
//输出write方法
context.write(key,new IntWritable(max));
}
}
Driver类
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;
import java.io.IOException;
public class zuihaochengjiDriver {
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
//1.获取配置信息以及封装任务
Configuration configuration=new Configuration();
Job job=Job.getInstance(configuration);
//2.设置jar加载路径(程序入口,自己的driver类+.class)
job.setJarByClass(zuihaochengjiDriver.class);
//3.设置Mapper和Reducer类(你自己的Mapper和Reducer的类名)
job.setMapperClass(zuihaochengjiMapper.class);
job.setReducerClass(zuihaochengjiReducer.class);
//4.设置map的输出key和value的类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//5.设置最终输出Key和value的类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//6.设置输入和输出路径
FileInputFormat.setInputPaths(job,new Path("/root/IdeaProjects/zuihaochengjiMapReduce/src/main/resources/chengjidan.txt"));
FileOutputFormat.setOutputPath(job,new Path("/root/IdeaProjects/zuihaochengjiMapReduce/src/main/resources/output .txt"));
//7.提交
boolean result=job.waitForCompletion(true);
System.exit(result?0:1);
}
}
MapReduce核心框架原理

一、InputFormat数据输入
InputFormat功能:数据切分、为Mapper提供输入数据
1.切片(InputFormat完成的)
(1)问题引出
MapTask的并行度决定Map阶段的任务处理并发度,进而影响到整个Job的处理速度。(MapTask不是越多越好)
(2)MapTask并行度决定机制
数据块:Block是HDFS物理上把数据分成一块一块。(默认Block的大小是128M)
数据切片:数据切片只是在逻辑上对输入进行分片,并不会在磁盘上将其切分成片进行存储。
MapTask个数,决定job并行度,MapTask的个数取决于数据切片的个数。(有多少个切片就有多少个MapTask)
MapTask并行度决定机制
(1)一个Job的Map阶段并行度由客户端在提交Job时候的切片数决定
(2)每一个Split切片分配一个MapTask并行实例处理 s.split()方法切割、s.exists()方法用来看s这个类是否存在
(3)默认情况下,切片大小=BlockSize=128M
(4)切片时不考虑数据集整体,而是逐个针对每一个文件单独切片(不管你有几个文件,一个文件一个文件处理,不会让两个文件合并在一起了再切)
例子:
输入:data1.txt 300M data2.txt 1M
切片大小设置为128M(切片大小可以改)
切片:切片1:0M-128M 切片2:128M-256M 切片3:256M-300M 切片4:0M-1M
切片大小计算公式:Math.max(minSize,Math.min(maxSize,blockSize))
Math.max(1,Math.min(最大值,blockSize))=Math.max(1,blockSize)=blockSize
Math.max(1,6)=6;
Math.min(1,6)=1;
问题:一个文件400MB,设置最小分片(minSize)为256MB,设置最大分片(maxSize)就是默认值,BlockSize=128MB,最终切片为多少?有多少个MapTask任务?
Math.max(256,Math.min(最大值,128))=Math.max(256,128)=256
切片大小为256MB
切片1:0MB-256MB 切片2:256MB-400MB
最终切片为2个,有2个MapTask任务
2.FileInputFormat实现类
InputFormat是一个抽象类,定义了一个MapReduce作业必须实现的标准规范
FileInputFormat同样是一个抽象类,它继承自InputFormat。
FileInputFormat常见的接口实现类包括:TextInputFormat、KeyValueTextInputFormat、NLineInputFormat、CombineTextInputFormat和自定义InputFormat。
TextInputFormat是默认的FileInputFormat实现类。
MapReduce的默认输入格式是? TextInputFormat
二、输出数据OutputFormat
OutputFormat接口实现类
OutputFormat是MapReduce输出的基类,所有实现MapReduce输出都实现了OutputFormat接口。
OutputFormat的实现类有TextOutputFormat,SequenceFileOutputFormat,自定义OutputFormat。
默认的输出格式是TextOutputFormat,他把每条记录都写为文本行。
三、Shuffle
Map方法之后,Reduce方法之前的数据处理过程称之为Shuffle。

3.1.分区Partition
(1)问题引入
要求将统计结果按照条件输出到不同文件中(分区)。比如:将统计结果按照号码归属地不同省份输出到不同文件中(分区)。
(2)默认Partition分区
默认分区是根据Key的hashCode对ReduceTasks个数取模(取余)得到的。用户没法控制哪个Key存储到哪个分区。
什么是分区?Mapper任务划分数据的过程称作Partition。负责实现数据的类称作Partitioner,默认的分区是Hash分区(Hash Partition)。
Partition作用:将map阶段产生的所有<key,value>对分配给不同的Reducer处理,可以将Reduce阶段的处理负载进行分摊。
这里回答前面的问题,什么决定Reduce任务的数量,答案是:Partition的数量决定Reducer的数量。
一般Reduce的任务数默认值是1,用户可以通过job.setNumReduceTasks(数字)去设置Reduce的个数。
在Driver类里面有这样的代码:job.setNumReduceTasks(5);代表有5个Reduce。
3.2.合并Combiner
(1)需求
统计过程中对每一个MapTask的输出进行局部汇总,以减少网络传输量即采用Combiner功能。
Combiner是一种特殊Reducer,在Mapper端,先执行一次Reducer
作用:减少Mapper输出到Reduce的数据量,缓解网络传输瓶颈,提高reducer的执行效率。
需要注意的问题:一定要谨慎使用Combiner
有些情况不能使用Combiner ----> 如:求平均值
保证引入Combiner以后,不能改变原来的逻辑


3.3.Shuffle完整流程理解


分区前面还有一个溢出的步骤。
示例背景
假设有2个Map任务处理以下输入数据:
- Map1输出:
("apple",1),("banana",1),("apple",1)
- Map2输出:
("banana",1),("apple",1),("cherry",1)
最终需统计单词出现次数(WordCount),由2个Reduce任务处理(按首字母分区):
- Reduce1:处理a-c(如apple,banana,cherry)
- Reduce2:处理d-z(本例无此类数据)
第一部分:Map任务处理
- Map任务1输入:(apple,1),(banana,1),(apple,1)
- Map任务2输入:(banana,1),(apple,1),(cherry,1)
- Map输出:保持原始键值对
第二部分:Shuffle过程(核心)
1.分区(Partitioning):
- 所有数据分配到Partition0
- 数据标记:(apple,P0),(banana,P0)等
2.Map端排序(Sorting)
- Map1排序后:(apple,1),(apple,1),(banana,1)
- Map2排序后:(apple,1),(banana,1),(cherry,1)
3.Combiner(可选本地聚合)
- Map1聚合后:(apple,2),(banana,1)
- Map2保持原样:(apple,1),(banana,1),(cherry,1)
第三部分:Reduce端处理(shuffle)
1.数据拉取(Fetch):
- 来自Map1:(apple,2),(banana,1)
- 来自Map2:(apple,1),(banana,1),(cherry,1)
2.归并排序(Merge Sort):
- 全局排序结果:(apple,2),(apple,1),(banana,1),(banana,1),(cherry,1)
3.分组(Grouping):
- apple:[2,1]
- banana:[1,1]
- cherry:[1]
第四部分:Reduce任务处理
1.Reduce输入:分组后的数据
2.处理过程:对值求和sum(values)
3.最终输出:
- (apple,3)
- (banana,2)
- (cherry,1)
| 阶段 | 输入 | 输出 | 发生位置 |
| 分区(Partitioning) | (Key,Value) | (Partition,Key,Values) | Map端 |
| 排序(Sorting) | (Partition,Key,Value) | 分区内按键排序的数据 | Map端 |
| Combiner(可选) | 排序后的(Key,Value) | 合并后的(Key,CombinedValue) | Map端 |
| 数据拉取(Fetch) | Map输出的磁盘文件 | 属于同一分区的末排序数据 | Reduce端 |
| 归并排序(Merge) | 来自多个Map的同一分区数据 | 全局按键排序的数据 | Reduce端 |
| 分组(Grouping) | 排序后的(Key,Value)序列 | (Key,Iterable<Value>) | Reduce端 |
Yarn
下面是YARN的架构图,它由Container,ResourceManager,NodeManager,ApplicationMaster几个主要部分组成。

YARN的架构是主从架构,主机为ResourceManager,从机为NodeManager,其中ResourceManager负责接收客户端的作业请求以及为作业分配相应的NodeManager资源,在NodeManager启动Container资源容器,在资源容器中运行相关作业。
(1)Container(容器):YARN中资源包括内存、CPU、磁盘输入输出等等。Container是YARN中资源的抽象,它封装了某个节点上的多维度资源。
(2)ResourceManager(资源管理器):
ResourceManager负责整个系统的资源分配和管理,是一个全局的资源管理器。主要由两个组件构成:调度器和应用程序管理器:
调度器(Scheduler):
调度器根据资源情况为应用程序分配封装在Container中的资源。
应用程序管理器(Application Mananger):
应用程序管理器负责管理整个系统中所有应用程序。
(3)NodeManager(节点管理器):
NodeManager是每个节点上的资源和任务管理器。
定时向ResourceManager汇报本节点上的资源使用情况和各个Container的运行状态;
接收并处理来自ApplicationManager的Container启动/停止等请求。
(4)ApplicationMaster(主应用)
ApplicationMaster是一个详细的框架库,它结合从ResourceManager获得的资源和NodeManager协同工作来运行和监控任务。
用户提交的每一个应用程序均包含一个ApplicationMaster。
主要功能包括:
1)、与ResourceManager调度器协商以获取抽象资源(Container);
2)、负责应用的监控,跟踪应用执行状态,重启失败任务等;
3)、并且与NodeManager协同工作完成Task的执行和监控。
yarn中应用运行机制

yarn监控

YARN调度器
YARN调度器分三种:
(1)FIFO Scheduler 先进先出调度器
(2)Capacity Scheduler 容器调度器
分成多个队列,每个队列占用一定资源,可以看作是FIFO Scheduler的多队列版本。
YARN默认采用Capacity Scheduler
(3)Fair Scheduler 公平调度器
| 调度器 | 工作方法 |
| FIFO Scheduler先进先出调度器 |
(1)单队列 (2)先进先出的原则 |
| Capacity Scheduler容器调度器 |
(1)多队列 (2)计算能力调度器,选择资源使用量占用最小、优先级高的先执行。 (3)多用户的情况下,可以最大化集群的吞吐和利用率。 |
| Fair Scheduler公平调度器 |
(1)多队列 (2)公平调度,所有的job具有相同的资源 |


207

被折叠的 条评论
为什么被折叠?



