大数据简介
Hadoop伪分布式安装
- 安装JDK
- 关闭防火墙
- 修改主机名 - 在Hadoop节点的主机名中不允许出现-或者_
vim /etc/sysconfig/network
修改HOSTNAME的属性值,例如
HOSTNAME=hadoop01
保存退出,重新生效
source /etc/sysconfig/network - 将主机名和IP进行映射
vim /etc/hosts
添加IP 主机名,例如
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
10.9.162.133 hadoop01 - 重启
reboot - 配置免密登录
ssh-keygen
ssh-copy-id root@hadoop01
云主机密码:tarena2017Up;
ssh hadoop01
如果不需要,那么输入logout - 进入software
cd /home/software/ - 下载Hadoop的安装包
wget http://bj-yzjd.ufile.cn-north-02.ucloud.cn/hadoop-2.7.1_64bit.tar.gz - 解压
tar -xvf hadoop-2.7.1_64bit.tar.gz - 进入配置目录
cd hadooop-2.7.1/cd etc/hadoop/ - 编辑
vim hadoop-env.sh
修改
export JAVA_HOME=/home/presoftware/jdk1.8
export HADOOP_CONF_DIR=/home/software/hadoop-2.7.1/etc/hadoop
保存退出,重新生效
source hadoop-env.sh - 编辑
vim core-site.xml
添加
fs.defaultFS
hdfs://hadoop01:9000
hadoop.tmp.dir
/home/software/hadoop-2.7.1/tmp
- 编辑
vim hdfs-site.xml
添加
dfs.replication
1
- 复制
cp mapred-site.xml.template mapred-site.xml
编辑
vim mapred-site.xml
添加
mapreduce.framework.name
yarn
- 编辑
vim yarn-site.xml
添加
yarn.resourcemanager.hostname
hadoop01
yarn.nodemanager.aux-services
mapreduce_shuffle
- 编辑
vim slaves
添加当前云主机的主机名 - 配置环境变量
vim /etc/profile
添加
export HADOOP_HOME=/home/software/hadoop-2.7.1
export PATH= P A T H : PATH: PATH:HADOOP_HOME/bin:$HADOOP_HOME/sbin
保存退出,重新生效
source /etc/profile - 格式化NameNode
hadoop namenode -format
如果日志中出现Storage directory /home/software/hadoop-2.7.1/tmp/dfs/name has been successfully formatted.表示格式化成功
***** 如果格式化失败,那么改完错误之后,检查/home/software/hadoop-2.7.1目录下是否存在tmp目录。如果有,那么删掉tmp之后再重新格式化 - 启动Hadoop
start-all.sh
通过jps查看
Jps
NameNode
DataNode
SecondaryNameNode
ResourceManager
NodeManager
如果出现Name or Service not known或者是Unknown Host,那么检查hosts文件是否配置正确
HDFS
使用代码连接hdfs,并上传下载文件
package cn.tedu.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.junit.Test;
import java.io.*;
import java.net.URI;
public class HDFSDemo {
// 下载文件
@Test
public void get() throws IOException {
// 创建环境参数
Configuration conf = new Configuration();
// 连接HDFS
FileSystem fs = FileSystem.get(URI.create("hdfs://10.9.162.133:9000"), conf);
// 指定文件 - 返回输入流
InputStream in = fs.open(new Path("/a.xml"));
// 创建输出流
FileOutputStream out = new FileOutputStream("E:\\test.xml");
// 读取数据,将读取的数据写出
// byte[] bs = new byte[1024];
// int len;
// while ((len = in.read(bs)) != -1)
// out.write(bs, 0, len);
IOUtils.copyBytes(in, out, conf);
// 关流
in.close();
out.close();
}
// 上传文件
@Test
public void put() throws IOException, InterruptedException {
// 创建环境参数
Configuration conf = new Configuration();
// 凡是可以放到配置文件中的属性都可以放在这个参数中
// conf中的配置优先于配置文件中的配置
// 指定Block是64M大小
conf.set("dfs.blocksize", "67108864");
// 指定副本数量
conf.set("dfs.replication", "1");
// 连接HDFS
FileSystem fs = FileSystem.get(
URI.create("hdfs://10.9.162.133:9000"), conf, "root");
// 指定存放位置 - 返回输出流
OutputStream out = fs.create(new Path("/b.xml"));
// 指定要上传的文件
FileInputStream in = new FileInputStream("D:\\test.xml");
// 上传文件
IOUtils.copyBytes(in, out, conf);
// 关流
in.close();
out.close();
}
// 删除文件
@Test
public void delete() throws IOException, InterruptedException {
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(
URI.create("hdfs://10.9.162.133:9000"), conf, "root");
// 删除文件
// 第二个参数表示是否递归
fs.delete(new Path("/a"), true);
}
}
流程
MapReduce
执行过程概括
应该是每一行数据调用一次mapper,每一个键调用一次reducer方法
在reduce刚开始的时候,会将相同的键对应的值放到一块去–分组;这里是如何区分是不是一组的呢,其实是利用的实体封装对象的compareTo()方法进行的比较,如果返回值的为0,就表示这两个对象是同一个;他的另一个作用是进行排序,如果返回的是this-obj,就表示升序排列,如果返回的是obj-this,就表示降序排列。
数据本地化策略
其实就是把处理数据的TaskTrack和存储数据的datanode部署到同一个节点上
job执行流程
Shuffle
InputFormat输入格式
自定义输入格式
package cn.tedu.authinput;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import org.apache.hadoop.util.LineReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
// 自定义输入格式:getSplis计算切片 createRecordReader:获取数据流来读取数据,但是FileInputFormat这个实现类已经
//覆盖了getSplis方法,所以我们只需要写 createRecordReader方法
//这里应该有两个方法:
// 泛型表示读取出来的数据类型
public class AuthInputFormat extends FileInputFormat<Text, Text> {
@Override
public RecordReader<Text, Text> createRecordReader(InputSplit split, TaskAttemptContext context) {
return new AuthReader();
}
}
class AuthReader extends RecordReader<Text, Text> {
private static final byte[] blank = new Text(" ").getBytes();
private LineReader reader;
private Text key;
private Text value;
private float len;
private float pos = 0;
// 在初始化的时候被调用一次
// 这个方法的目的就是为了获取一个真正用于读取数据的流
@Override
public void initialize(InputSplit split, TaskAttemptContext context) throws IOException {
FileSplit fileSplit = (FileSplit) split;
// 获取要读取的切片对应的路径
Path path = fileSplit.getPath();
// 获取切片大小
len = fileSplit.getLength();
// 连接HDFS
FileSystem fs = FileSystem.get(
URI.create(path.toString()), context.getConfiguration());
// 指定要读取的文件
// 获取到输入流用于读取文件
// 输入流是一个字节流,但是原始文件是字符文件
// 考虑将字节流转化为字符流,最好能够按行读取
InputStream in = fs.open(path);
reader = new LineReader(in);
}
// 判断是否有下一个键值对要传递到map方法中
// 如果读取到了数据,那么说明还有数据要处理,那么就返回true
// 如果没有读取到数据,那么说明数据已经处理完,那么就返回false
@Override
public boolean nextKeyValue() throws IOException {
key = new Text();
value = new Text();
Text tmp = new Text();
// 会将读取到的一行数据放到传入的tmp中
// 返回值表示读取到的一行的字节个数
if (reader.readLine(tmp) <= 0) return false;
key.set(tmp.toString());
pos += tmp.getLength();
if (reader.readLine(tmp) <= 0) return false;
value.set(tmp.toString());
value.append(blank, 0, blank.length);
pos += tmp.getLength();
if (reader.readLine(tmp) <= 0) return false;
byte[] data = tmp.getBytes();
value.append(data, 0, data.length);
pos += tmp.getLength();
return true;
}
// 获取当前要处理的键
@Override
public Text getCurrentKey() {
return key;
}
// 获取当前要处理的值
@Override
public Text getCurrentValue() {
return value;
}
// 获取执行进度
@Override
public float getProgress() {
return pos / len;
}
@Override
public void close() throws IOException {
if (reader != null) {
reader.close();
}
}
}
多源输入
同时处理多个文件
切片过程中保证数据完整性的策略
解释:基本上一个block对应着一个切片,那么一个block的默认大小是128M,但是maptask在处理数据的时候都是按行处理的,这就导致可能一行数据处理到一半就到128m了,就会导致数据的不完整性,如下图:
输出格式
小文件
数据倾斜
推测执行机制
稳定排序与非稳定排序
冒泡排序是属于稳定排序,选择排序是非稳定排序
冒泡排序:从第一位开始,相邻两位互相比较,如果位置错误则交换位置,这样经过一轮的比较,最大的就排到了最后。然后比较除最后一位的其他数字,重复之前的逻辑。
选择排序:第一位和剩余所有数字相互比较,如果位置错误则交换位置。然后第二位,第三位…
稳定与非稳定的判断依据是:去除已经排好的元素之后,如果剩余元素的相对位置没有发生变化,那么这就是一个稳定排序,否则就是非稳定排序。
Hadoop完全分布式的搭建
完全分布式结构图
安装步骤
- 三台云主机需要安装JDK和Zookeeper
- 三台云主机需要关闭防火墙
- 修改三台云主机的主机名
vim /etc/sysconfig/network
修改HOSTNAME属性
统一要求,第一台云主机改为hadoop01,第二台云主机改为hadoop02,第三台云主机改为hadoop03
改完之后,保存退出,重新生效
source /etc/sysconfig/network - 三台云主机做IP映射
vim /etc/hosts
添加IP 主机名
例如
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
10.9.12.131 hadoop01
10.9.23.140 hadoop02
10.9.133.238 hadoop03
正常情况下,三台云主机改完之后的内容配置应该一模一样
改完之后,保存退出 - 三台云主机重启
reboot - 三台云主机之间需要相互免密,每个云主机上都执行一遍
ssh-keygen
ssh-copy-id root@hadoop01
ssh-copy-id root@hadoop02
ssh-copy-id root@hadoop03
云主机密码:tarena2017Up;
ssh hadoop01 如果不需要输入密码,logout
ssh hadoop02 如果不需要输入密码,logout
ssh hadoop03 如果不需要输入密码,logout - 三台云主机开启Zookeeper
cd /home/software/zookeeper-3.4.8/bin
sh zkServer.sh start
sh zkServer.sh status - 需要将第一台云主机上的伪分布式保留下来
cd /home/software/
mv hadoop-2.7.1 hadoop-alone - 下载完全分布式安装包
wget http://bj-yzjd.ufile.cn-north-02.ucloud.cn/hadoop-dist.tar.gz - 解压
tar -xvf hadoop-dist.tar.gz - 三台云主机配置环境变量
vim /etc/profile
在文件末尾添加
export HADOOP_HOME=/home/software/hadoop-2.7.1
export PATH= P A T H : PATH: PATH:HADOOP_HOME/bin:$HADOOP_HOME/sbin
改完之后,保存退出,重新生效
source /etc/profile - 在第一台云主机上远程拷贝
scp -r hadoop-2.7.1 root@hadoop02:/home/software/
scp -r hadoop-2.7.1 root@hadoop03:/home/software/ - 在第一台云主机上格式化Zookeeper - 这一步实际上是在Zookeeper上注册节点
hdfs zkfc -formatZK
如果出现Successfully created /hadoop-ha/ns in ZK.表示格式化成功 - 三台节点启动JournalNode
hadoop-daemon.sh start journalnode - 在第一台云主机上格式化NameNode
hadoop namenode -format
如果出现Storage directory /home/software/hadoop-2.7.1/tmp/hdfs/name has been successfully formatted.表示格式化成功 - 在第一台云主机上启动NameNode
hadoop-daemon.sh start namenode - 在第二台节点上格式化NameNode
hdfs namenode -bootstrapStandby
如果出现Storage directory /home/software/hadoop-2.7.1/tmp/hdfs/name has been successfully formatted.表示格式化成功 - 在第二台云主机上启动NameNode
hadoop-daemon.sh start namenode - 三台云主机启动DataNode
hadoop-daemon.sh start datanode - 在第一台和第二台云主机上启动DFSFailOverController
hadoop-daemon.sh start zkfc - 在第三台云主机上启动YARN
start-yarn.sh - 在第一台云主机上启动ResourceManager
yarn-daemon.sh start resourcemanager
通过jps查看
第一台云主机上出现8个进程
Jps
NameNode
DataNode
JournalNode
DFSZKFailoverController
ResourceManager
NodeManager
QuorumPeerMain
第二台云主机上出现7个进程
Jps
NameNode
DataNode
JournalNode
DFSZKFailoverController
NodeManager
QuorumPeerMain
第三台云主机上出现6个进程
Jps
DataNode
JournalNode
ResourceManager
NodeManager
QuorumPeerMain
出现问题
- 如果出现Name or Service not known或者是Unknown host,那么检查hosts文件是否配置正确
- 如果出现Command not found,那么检查环境变量是否配置正确
- 如果在执行hdfs zkfc -formatZK出现了Hadoop HA is not available/enable,那么这个问题无法解决,这是系统兼容性问题 - 重装系统
- 如果在执行hdfs zkfc -formatZK出现IllegalArgument,那么检查命令是否书写正确
- 如果jps之后少了QuorumPeerMain,那么检查Zookeeper是否启动成功
- 如果少了NameNode/DataNode/JournalNode/DFSZKFailoverController,通过hadoop-daemon.sh start namenode/datanode/journalnode/zkfc来启动,例如hadoop-daemon.sh start journalnode
- 如果少了ResourceManager/NodeManager,那么通过yarn-daemon.sh start resourcemanager/nodemanager来启动,例如yarn-daemon.sh start nodemanager
- 如果格式化NameNode失败,那么检查完错误之后,需要删除/home/software/hadoop-2.7.1/tmp再重新格式化
- 如果启动过程中,出现running as processing 进程号,那么可以先kill -9 进程号,再重新启动这个进程
- 后续启动Hadoop完全分布式之前,需要先启动Zookeeper,然后再启动Hadoop
YARN
概述
job执行流程
注意问题
流程图
面试题
搭建Haddoop集群,应该如何选购云主机?
云主机的类型:内存型 高IO型 大磁盘型 计算型 CPU密集型
解释:这个问题实际上是在考察对Hadoop各个节点的理解,Namenode里边因为需要内存存储了大量的文件的元数据,所以选择内存型。DataNode上需要大量读取、存储block,所以选择高IO型。而且DataNode还会有数据的本地化策略,会将处理任务的TaskTracker保存到上边,所以选择计算型或者是CPU密集型。journalNode因为需要考虑消息的传递,所以选择io型,或者是内存型。ResourceManager