1.大数据概论
1.1大数据的概念
- 大数据是指无法在一定时间范围内使用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产
- 主要解决,海量数据的存储和分析计算问题
1.2大数据的业务分析流程
- 产品人员提需求
- 数据部门搭建数据平台、分析数据指标
- 数据可视化
1.3大数据的应用场景
- 零售
- 物流仓库
- 旅游
2.Hadoop
2.1Hadoop概述
2.1.1Hadoop的优势
- 高可靠性:Hadoop底层维护多个数据副本,防止某个节点宕机而产生数据流失
- 高扩展性:在集群间分配任务数据,可方便的拓展数以千计的节点
- 高效性:在mapreduce的思想下,Hadoop并行工作
- 高容错性:能将失败的任务重新分配
2.1.2Hadoop1版本和2版本的区别
1版本中的mapreduce既要处理计算,又要管理资源的调度,耦合性非常大,新增了yarn之后,mapreduce只负责计算
2.2Hadoop的安装
- 安装JDK
1.1新建一个虚拟机,从已安装的jdk中输入命令
scp -r /usr/lcoal/jdk1.8.0_192
scp -r /etc/profile
scp -r /etc/hosts
- 关闭防火墙
sytemctl stop firewalld.service
sytemctl disable firewalld.service
- 重写source文件
source /etc/profile
- 配置免密登录
//所有会话
ssh-keygen -t rsa
//所有会话连点三次回车
ssh-copy-id bigdata1
123456
ssh-copy-id bigdata2
ssh-copy-id bigdata3
ssh-copy-id bigdata4
ssh-copy-id bigdata5
- 配置核心文件
put 路径 文件名
6 分发到其他的主机上
scp -r /usr/local/hadoop-2.8.4 bigdata1:/usr/local
- 配置Hadoop环境变量
scp -r /etc/profile bigdata1:/etc/profile
export HADOOP_HOME=/usr/local/hadoop-2.8.4
export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
- 检测Hadoop是否安装配置成功
Hadoop
- 格式化
//首先启动小弟的journalnode
hadoop-daemon.sh start journalnode
//在其中一个大哥里面输入
hdfs namenode -format
hdfs zkfc -formatZK
//第二台也要格式化
hdfs namenode -format
//看看log中是否有successfully
- 启动大哥的namenode
hadoop-daemon.sh start namenode//在所有的大哥上都要执行
hadoop-daemon.sh start zkfc //启动注册active
- 检验bigdata4:50070
出现一个可视化界面 - 启动DataNode
//每一个zk上启动一个,都可以看到一个节点上线
hadoop-daemon.sh start datanode
- 启动资源调度
start-yarn.sh
- 检查是否是动态ip,导致节点无法上线
vi /etc/sysconfig/network-scripts/ifcfg-ens33
- 配置一键启动命令
//cat hadoop-env.sh 在usr/local/hadoop-2.8.4/etc/hadoop下面
export JAVA_HOME=/usr/local/jdk1.8.0_192
- 启动和停止命令
start-dfs.sh
stop-dfs.sh
2.4Hadoop三大组成
2.4.1HDFS
2.4.1.1hdfs的组成
- 图解
- namenode管理hdfs的命名空间、配置副本的策略、管理数据块的映射信息、处理客户端的读写请求
- datanode就是slaves,执行namenode下达的命令,负责存储实际的数据块、执行读写操作
- client客户端负责文件切分,切分成一个个block上传、与namnode交互获得文件的位置信息、与datanode交互读写数据、用一些命令管理hadfs增删改查
- secondarynamenode辅助namenode分担工作量
2.4.1.2hdfs读写文件
- hdfs写文件
- client创建一个distractedfilesystem向namenode请求上传数据
- namenode响应可以上传数据
- client请求上传第一个block的位置
- 返回datanode1\datanode2\datanode3用这三个节点存储
- fsoutputStream请求建立传输通道
- 应答成功
- 传输数据
- 传输成功
- 关闭流
2.hdfs读文件
- 客户端打开分布式文件系统请求下载文件
- namenode返回目标文件的元数据信息
- 客户端打开fsinputstream请求在datanode1读取数据block1
- 传数据
- 客户端打开fsinputstream请求在datanode2读取数据block2
- 传数据
- 关流
2.4.1.3hdfs的namenode和secondarynamenode
- nn 和 2nn 工作机制
- fsimage备份元数据信息,edits文件当元数据更新时在后面追加
- 一旦namenode断电后可以通过fsimage和edits的合并得到元数据
- 为了解决edits文件过大,需要定期合并,如果让namenode来做的话效率过低,所以引入了2nn
- namenode内存128g每个block占150byte
- 启动namenode创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
- 客户端传来元数据的更新请求
- namenode记录操作日志,更新滚动日志
- 当edits文件过大,或者checkpoint 时间到了之后,nm向2nm发送checkpoint请求
- 2nm执行checkpoint将fsimage和edits拷贝到奥2nm
- 加载到内存进行合并,生成fsimge.checkpoint
- 拷贝到nm重命名
2.4.1.4hdfs的datanode
- datanode1启动后向nm注册
- 注册成功
- datanode1每个周期向nm汇报所有块信息
- datanode2每三秒钟发送一次心跳
- 返回带有nm的指令
- 超过十分钟没有心跳,就会认为该节点不可用
2.4.2mapreduce
2.4.2.1核心思想
- map阶段读数据,一个分区对应一个maptask,将数据按行读入,
- 以空格划分
- 形成键值对
- 通过不同的首字母划分成两个分区
- reduce阶段
- 将统计单词个数
- 输出到文件
2.4.2.2序列化
- 定义
- 将对象转换成字节序列以便于存到磁盘(持久化)和网络传输
- 反序列化:将字节序列转换为内存中的对象
- 代码演示
package com.iweb.test;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
public class Sort {
static class SoMapper extends Mapper<LongWritable,Text, Flowbeen,Text>{
Flowbeen k= new Flowbeen();
Text v =new Text();
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String [] split = value.toString().split("\t");
long sum_up = Long.parseLong(split [split.length-3]);
long sum_down = Long.parseLong(split[split.length-2]);
k.set(sum_up,sum_down);
v.set(split[1]);
context.write(k,v);
}
}
static class SoReduce extends Reducer<Flowbeen,Text,Text,Flowbeen>{
protected void reduce(Flowbeen key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
for (Text text:values
) {
context.write(text,key);
}
}
}
public static void main(String[] args) throws Exception{
Configuration conf =new Configuration();
Job job =Job.getInstance(conf);
Path src =null;
Path dst =null;
if(args.length==2){
src =new Path(args[0]);
dst = new Path(args [1]);
}else {
src =new Path("E:\\test\\sort\\in");
dst =new Path("E:\\test\\sort\\out");
conf.set("mapreduce.job.jar","F:\\workSpace\\java\\sort\\target\\sort-1.0-SNAPSHOT-jar-with-dependencies.jar");
}
FileSystem fs = FileSystem.get(conf);
if (fs.exists(dst)){
fs.delete(dst,true);
}
job.setMapperClass(SoMapper.class);
job.setReducerClass(SoReduce.class);
job.setOutputKeyClass(Flowbeen.class);
job.setOutputValueClass(Text.class);
FileInputFormat.addInputPath(job,src);
FileOutputFormat.setOutputPath(job,dst);
System.exit(job.waitForCompletion(true)?0:1);
}
}
//bean
package com.iweb.test;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class Flowbeen implements Writable, WritableComparable<Flowbeen> {
//1 13736230513 192.196.100.1 www.atguigu.com 2481 24681 200
private long up;
private long down;
private long sum;
public Flowbeen() {
super();
}
public Flowbeen(long up, long down) {
super();
this.up = up;
this.down = down;
this.sum = up+down;
}
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeLong(up);
dataOutput.writeLong(down);
dataOutput.writeLong(sum);
}
@Override
public void readFields(DataInput dataInput) throws IOException {
this.up = dataInput.readLong();
this.down = dataInput.readLong();
this.sum = dataInput.readLong();
}
@Override
public String toString() {
return up +
"\t" + down +
"\t" + sum ;
}
public long getUp() {
return up;
}
public void setUp(long up) {
this.up = up;
}
public long getDown() {
return down;
}
public void setDown(long down) {
this.down = down;
}
public long getSum() {
return sum;
}
public void setSum(long sum) {
this.sum = sum;
}
public void set(long upFlow, long downFlow) {
this.up = upFlow;
this.down = downFlow;
this.sum = upFlow + downFlow;
}
@Override
public int compareTo(Flowbeen o) {
int result;
if(sum>o.getSum()){
result = -1;
}else if (sum<o.getSum()){
result = 1;
}else {
result =0;
}
return result;
}
}
2.4.2.3框架原理
- Fileinputformat源码解析
- 首先程序获得文件的存储目录
- 遍历所有像切片的文件
– 遍历第一个文件
–返回文件的大小
– 计算文件的切片:默认情况下 切片大小等于blocksize
– 每次切分都要判断剩下的是否是块的1.1倍决定是否在分一块
– 将切片信息写到有一个切片的规划文件中
– 整个切片的方法在getsplit中完成
– inputformat只记录了切片的元数据信息,起始位置,长度 - 提交切片规划文件到yarn上,yarn的app master就可以通过切片规划文件开启maptask的个数
- CombineTextInputFormat
- 框架默认的TextInputFormat切片机制是对任务按文件规划切片,不管文件多小,都会是一个单独的切片,都会交给一个MapTask,这样如果有大量小文件,就会产生大量的MapTask,处理效率极其低下。
- FileInputFormat实现类
- TextIuputFormat、KeyValueTextIuputFormat、NLineinputFormat、CombineTextInputFormat、自定义inputformat
2.4.2.4shuffle机制
- Shuffle的大致流程为:Maptask会不断收集我们的map()方法输出的kv对,放到内存缓冲区中,当缓冲区达到饱和的时候(默认占比为0.8)就会溢出到磁盘中,如果map的输出结果很多,则会有多个溢出文件,多个溢出文件会被合并成一个大的溢出文件,在文件溢出、合并的过程中,都要调用partitoner进行分组和针对key进行排序(默认是按照Key的hash值对Partitoner个数取模),之后reducetask根据自己的分区号,去各个maptask机器上取相应的结果分区数据,reducetask会将这些文件再进行合并(归并排序)。
合并成大文件后,shuffle的过程也就结束了,后面进入reducetask的逻辑运算过程(从文件中取出每一个键值对的Group,调用UDF函数(用户自定义的方法))
2.4.2.5数据输出
- 自定义
//自定义一个OutputFormat类
public class FilterOutputFormat extends FileOutputFormat<Text, NullWritable>{
@Override
public RecordWriter<Text, NullWritable> getRecordWriter(TaskAttemptContext job) throws IOException, InterruptedException {
// 创建一个RecordWriter
return new FilterRecordWriter(job);
}
}
//编写RecordWriter
public class FilterRecordWriter extends RecordWriter<Text, NullWritable> {
FSDataOutputStream atguiguOut = null;
FSDataOutputStream otherOut = null;
public FilterRecordWriter(TaskAttemptContext job) {
// 1 获取文件系统
FileSystem fs;
try {
fs = FileSystem.get(job.getConfiguration());
// 2 创建输出文件路径
Path atguiguPath = new Path("e:/atguigu.log");
Path otherPath = new Path("e:/other.log");
// 3 创建输出流
atguiguOut = fs.create(atguiguPath);
otherOut = fs.create(otherPath);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void write(Text key, NullWritable value) throws IOException, InterruptedException {
// 判断是否包含“atguigu”输出到不同文件
if (key.toString().contains("atguigu")) {
atguiguOut.write(key.toString().getBytes());
} else {
otherOut.write(key.toString().getBytes());
}
}
@Override
public void close(TaskAttemptContext context) throws IOException, InterruptedException {
// 关闭资源
IOUtils.closeStream(atguiguOut);
IOUtils.closeStream(otherOut); }
}
2.4.2.6join
- Map Join:Map Join适用于一张表十分小、一张表很大的场景。
(1)在Mapper的setup阶段,将文件读取到缓存集合中。
(2)在驱动函数中加载缓存。
// 缓存普通文件到Task运行节点。
job.addCacheFile(new URI(“file://e:/cache/pd.txt”)); - Reduce Join:Map端的主要工作:为来自不同表或文件的key/value对,打标签以区别不同来源的记录。然后用连接字段作为key,其余部分和新加的标志作为value,最后 进行输出。Reduce端的主要工作:在R educe端以连接字段作为key的分组已经完成,我们只需要在一个分组当中将那些来源于不同文件的记录(在Map阶段已经打标志)分开,最后进行台并就k了。
2.4.3yarn
- 运行机制
(1)MR程序提交到客户端所在的节点。
(2)YarnRunner向ResourceManager申请一个Application。
(3)RM将该应用程序的资源路径返回给YarnRunner。
(4)该程序将运行所需资源提交到HDFS上。
(5)程序资源提交完毕后,申请运行mrAppMaster。
(6)RM将用户的请求初始化成一个Task。
(7)其中一个NodeManager领取到Task任务。
(8)该NodeManager创建容器Container,并产生MRAppmaster。
(9)Container从HDFS上拷贝资源到本地。
(10)MRAppmaster向RM 申请运行MapTask资源。
(11)RM将运行MapTask任务分配给另外两个NodeManager,另两个NodeManager分别领取任务并创建容器。
(12)MR向两个接收到任务的NodeManager发送程序启动脚本,这两个NodeManager分别启动MapTask,MapTask对数据分区排序。
(13)MrAppMaster等待所有MapTask运行完毕后,向RM申请容器,运行ReduceTask。
(14)ReduceTask向MapTask获取相应分区的数据。
(15)程序运行完毕后,MR会向RM申请注销自己。 - 任务提交
(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会清理工作状态。作业的信息会被作业历史服务器存储以备之后用户核查。