HDFS存储数据
hdfs操作指令(Linux) hadoop fs
1.查看文件
hadoop fs -ls / 或hdfs dfs -ls /
2.创建文件夹
hadoop fs -mkdir /hdfs01
3.移动或重命名
hadoop fs -mv /data /hadf01
hadoop fs -mv /data /data_blk
4.复制
cp [-p] …
hadoop fs -cp -p /data /hdfs01文件夹
5.删除
[-rm [-f] [-r|-R] [-skipTrash] …]
hadoop fs -rm -r /data 文件夹
生产上一般开启回收站 回收站有效期至少七天以上 fs.trash.interval 10080
stop-dfs.sh
vim core-site.xml
fs.trash.interval
10080
start-dfs.sh
6.上传
hadoop fs -put ./wc.data /
hadoop fs -copyFromLocal ./wc.data /
7.下载
hadoop fs -get /data ./
hadoop fs -copyToLocal /data ./
8.修改权限
hadoop fs -chmod 664 /1.log
9.修改拥有者
hadoop fs -chown uzi:DL2262 /1.log
10.查看文件内容
hadoop fs -cat /1.log
补充:
查看hdfs上的文件:
hadoop fs -ls hdfs://bigdata13:9000/1.log
hadoop fs -ls /1.log
查看Linux文件:
hadoop fs -ls file:home/hadoop/1.log
hdfs指令(java)
建idea项目时在pom.xml上添加
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.3.4</version>
</dependency>
@Test
public void mkdir() throws Exception {
// 创建文件夹
Configuration conf = new Configuration();
// 获取程序入口
conf.set("fs.defaultFS","hdfs://bigdata13:9000");
System.setProperty("HADOOP_USER_NAME","hadoop");
FileSystem fs = FileSystem.get(conf);
Path path = new Path("/hdfsapi01");
boolean mkdirs = fs.mkdirs(path);
Assert.assertEquals(true,mkdirs);
}
@Test
public void put() throws Exception{
// 上传
Configuration conf = new Configuration();
conf.set("fs.defaultFS","hdfs://bigdata13:9000");
System.setProperty("HADOOP_USER_NAME","hadoop");
FileSystem fs = FileSystem.get(conf);
Path src = new Path("data/wc.data");
Path dst = new Path("/");
// 上传并删除上传文件 允许覆盖
fs.copyFromLocalFile(true,true,src,dst);
}
FileSystem fs = null;
@Before
public void setup() throws Exception{
Configuration conf = new Configuration();
conf.set("fs.defaultFS","hdfs://bigdata13:9000");
System.setProperty("HADOOP_USER_NAME","hadoop");
fs = FileSystem.get(conf);
}
@After
public void teardown() throws Exception{
if (fs != null){
fs.close();
}
}
@Test
public void pull() throws Exception{
// 下载
Path src = new Path("/data");
Path dst = new Path("data");
fs.copyToLocalFile(src,dst);
}
@Test
public void mv() throws Exception{
// 移动
Path src = new Path("/wc.data");
Path dst = new Path("/hdfsapi01");
fs.rename(src,dst);
}
@Test
public void mv01() throws Exception{
// 改名
Path src = new Path("/hdfsapi01/wc.data");
Path dst = new Path("/hdfsapi01/wc.data_blk");
fs.rename(src,dst);
}
@Test
public void rm() throws Exception{
// 递归删除
Path src = new Path("/hdfsapi01/wc.data_blk");
fs.delete(src,true);
}
@Test
public void cp() throws Exception{
Path src = new Path("/data/wc.data");
FSDataInputStream inputstream = fs.open(src);
Path dst = new Path("/data/wc_blk.data");
FSDataOutputStream outputstream = fs.create(dst);
IOUtils.copyBytes(inputstream,outputstream,4096);
IOUtils.closeStream(outputstream);
IOUtils.closeStream(inputstream);
}
@Test
public void ls() throws Exception{
Path path = new Path("/");
RemoteIterator<LocatedFileStatus> listfile = fs.listFiles(path, true);
while (listfile.hasNext()){
LocatedFileStatus filestatus = listfile.next();
String filename = filestatus.getPath().getName();
long len = filestatus.getLen();
FsPermission permission = filestatus.getPermission();
String owner = filestatus.getOwner();
String group = filestatus.getGroup();
long modificationTime = filestatus.getModificationTime();
System.out.println(filename + '\t' + len + '\t' + permission +'\t' + owner +'\t' + group + '\t' + modificationTime);
}
}
参数设置的优先级
1.默认配置文件
core-default.xml
hdfs-default.xml
mapred-default.xml
yarn-default.xml
2.生产配置文件
core-site.xml
hdfs-site.xml
mapred-site.xml
yarn-site.xml
3.代码里面设置的参数
Configuration
优先级排序:代码里面设置的参数 > 生产配置文件 > 默认配置文件
例题
1.使用java api 操作 hdfs在 创建文件夹
/data/dt=20221114
/data/dt=20221115
/data/dt=20221116
2.使用java api 上传本地 wc.txt 到上面三个目录下
3.使用java api 将
/data/dt=20221114/wc.txt
/data/dt=20221115/wc.txt
/data/dt=20221116/wc.txt
目录下的文件移动到 指定目录 并进行改名字
/data/hive/20221114-01.data
/data/hive/20221115-01.data
/data/hive/20221116-01.data
String hdfspath = args[0];
String localpath = args[1];
String filename = args[2];
String renamepath = args[3];
String renameusfname = args[4];
String dt = args[5];
// 获取程序入口
Configuration conf = new Configuration();
conf.set("fs.defaultFS","hdfs://bigdata13:9000");
System.setProperty("HADOOP_USER_NAME","hadoop");
FileSystem fs = FileSystem.get(conf);
// 创建文件夹
fs.mkdirs(new Path(hdfspath+dt));
// 上传
fs.copyFromLocalFile(new Path(localpath+"/"+filename),new Path(hdfspath+dt));
// 改名字
// 判断文件夹是否存在
if (!fs.exists(new Path(renamepath))){
fs.mkdirs(new Path(renamepath));
}
fs.rename(new Path(hdfspath+dt+"/"+filename),new Path(renamepath+"/"+dt+renameusfname));
// 关闭资源
if (fs != null){
fs.close();
}
可以在idea里一个一个的添加参数,参数之间用空格进行分隔
也可以把程序打成夹包,把夹包拽到xshell里面
然后输入hadoop jar /home/hadoop/project/hdfsApi04-1.0.jar com.bigdata.HomeWork01
“/data/dt=” “file:home/hadoop” “1.log” “/data/flink” “-01.data” “20221117” 运行代码
还可以写一个脚本
vim start.sh
if [ $# -lt 6 ];then
echo "Usage:put file to hdfs"
echo "Usage:$0 <hdfspath> <localpath> <filename> <renamepath> <suffname> <dt>"
echo "Usage:<hdfspath> is hdfs path"
echo "eg:$0 '/data/dt=' 'file:home/hadoop' '1.log' '/data/flink' '-01.data' '20221117'"
exit;
fi
hadoop jar /home/hadoop/project/hdfsApi04-1.0.jar \
com.bigdata.HomeWork01 \
"${1}" "${2}" "${3}" "${4}" "${5}" "${6}"
chmod u+x start.sh
./start.sh ‘/data/dt=’ ‘file:home/hadoop’ ‘1.log’ ‘/data/flink’ ‘-01.data’ ‘20221117’
HDFS架构设计以及各个角色的作用
hdfs是主从架构主从架构–一个namenode,多个datanode
生产上用两个namenode,高可用设计模式
当一个namenode突然挂掉了,另一个namenode马上顶上去实时切换
snn nn 冷备 因为snn是定期拿镜像文件和编辑日志进行合并备份和推送给nn dfs.namenode.checkpoint.period 3600s 1h 会导致有一个空档期1h,在这个期间,上传文件即使成功了也检查不到,会间接导致文件丢失
dfs.namenode.checkpoint.txns 1000000 100w 每有一百万个文件块产生就会触发snn拿编辑日志和镜像文件进行合并
举例:
11:00 snn nn 存储hdfs上所有文件的元数据是一致的全的
11:30 nn挂了 此时有新增文件上传 编辑日志没有记录 会导致即使文件上传了,但是客户端查不到
12:00 snn进行合并日志 此时nn和snn又保持了一致,但查不到新上传的文件
nn nn 实时热备 一台对外提供服务,一台等待stand by,万一工作的那台namenode挂了,另一台会马上接替它的工作
namenode: 名称节点 nn
1.负责存储metadata元数据–描述数据的数据–文件名称、文件的目录结构、文件的属性、权限、创建时间、副本数据等
2.负责 映射块文件–一个文件被切分成多少个数据块,以及数据块对应在哪些节点上存储 the mapping of blocks to DataNodes
作用:
1.管理文件系统的命名空间–其实就是维护文件系统树的文件和文件夹 manage the file system namespace
是以镜像文件fsimage和编辑日志文件editlogs(已经写好的和正在写的)两种文件永久的保存在本地磁盘上
2.namenode对外提供服务(客户端要访问HDFS上的文件必须经过namenode同意)regulate access to files clients
secondery namenode: 第二名称节点 snn
定期拿取镜像文件和编辑日志文件(正在写的)进行合并,备份并推送给namenode
fsimage216+editlogs217=>fsimage217=>namenode
datanode:数据节点 dn
每个分布式都有datanode
serve read and write request from the file system’s clients负责文件的读写(具体干活)
1.存储数据块 store in datanodes和数据块的校验(时时刻刻记录文件变化,把发生的变化记录在镜像文件和编辑文件日志里)
作用:
1.每隔三秒发送一次心跳给namenode告诉namenode我还活着 dfs.heartbeat.internal 3s
2.每隔一定时间发送一次块报告
块映射不会永久持久化这个存储
是通过集群启动和运行时,datanode定期发送块报告给namenode来进行动态维护这种映射关系(存在内存中)dfs.blockreport.intervalMsec 21600000ms 6h
dfs.datanode.directoryscan.interval 21600s 6h 每隔6h扫描磁盘和内存中的块进行比较,判断块有没有进行更新
发送块报告的目的:
因为生产上可能会发生文件块丢失或损坏,就是通过datanode每隔6h是扫描判断块是否发生丢失或损坏
hdfs存储时block
block取决于块大小blocksize含有多少个128M和副本数 伪分布式 1
完全分布式 3(大于等于3时)
160M 2个副本 (1)文件在hdfs上存了多少个块?
(2)实际存储到hdfs 存储的大小是多少?
160M:128M => 1 32M => 1
(1)1+1 =2 块 * 2 = 4个块
(2)160M * 2 = 320M
日志级别
日志是一行一行打印的,有时候看见的换行是显示问题
(1)info:这个级别日志多
(2)warn:这个级别日志偏少,但不耽误使用
(3)error:这个级别日志更少,但需要解决
HDFS写流程【面试】
1.客户端调用程序入口FileSystem.create(filrpath)去和namenode通过RPC协议进行通信
namenode会去检查要上传的文件是否已经存在,并且会去检查客户端是否有权限去访问,只有当要上传的文件不存在且客户端有权限访问时,才能创建一个新的文件。但这时还没有写数据,所以是不关联任何block的
namenode会根据客户端要上传文件的大小以及块的大小和副本参数,计算出要上传多少块和块存储在datanode的位置
最终将这些信息以FSDataOutputStream对象返回给客户端
2.拿到信息后,客户端会调用FSDataOutputStream对象的write方法,将文件的第一个块的第一个副本写到第一个datanode节点,第一个副本写完后去第二个datanode节点写第二个副本,第二个副本写完后再去第三个datanode节点写第三个副本,当第三个副本写完时就返回一个ack的确认包给datanode2节点,当datanode2节点接收到这个ack确认包并且自己也写完之后会返回一个ack确认包给datanode1节点,当datanode1节点接收到这个ack确认包并且自己也写完之后会返回给FSDataOutputStream对象一个ack确认包,此时表示第一个块的三个副本写完了。剩余文件块也按照这个方式进行写入。
3.当所有的文件块都写完之后,客户端会调用FSDataOutputStream对象的close方法告知namenode文件已经全部写入完成。
HDFS读流程【面试】
hdfs读写流程对用户操作是无感知的
1.客户端调用程序入口FileSystem.open(filrpath)的open方法和namenode进行RPC通信,namenode会以FSDataInputStream对象返回要读文件部分或全部的块列表(要读取的块具体在哪些datanode上面)
2.客户端通过调用FSDataInputStream对象的read方法,去与第一个块最近的datanode进行读取,读取完成会检查是否读取成功,如果读取成功就关闭与datanode的通信,如果读取失败会记录所读机器的datanode节点和块信息(标记该节点已经挂了),下次就不会从该节点读取信息,并且从第二个节点读取信息,如果读取成功就关闭通道,如果读取失败就从第三个节点读取信息,当第一个块读取成功后,然后会去与第二个块最近的datanode进行读取,以此类推。当块列表读取完成后,文件还没有全部读取时会继续调用FileSystem从namenode获取下一个批次的块列表,以此类推。
3.当全部读取完毕后,客户端会调用FSDataInputStream对象的close方法关闭输入流。
HDFS副本放置策略【面试】【机架】
读写操作会选择datanode节点进行读取
第一副本:放置在客户端上传的datanode节点
当客户端不在datanode节点时,就会随机选择一个datanode节点
第二个副本:放置在与第一个副本不同机架上的某个datanode节点
第三个副本:放置在与第二个副本相同机架上的不同datanode节点
如果副本数设置超过三个,其他副本就随机放置在不同的datanode节点
HDFS安全模式
1.什么时候会发生安全模式?
(1)启动hadoop
(2)HDFS故障【集群文件块大量丢失,说明集群不稳的,会进入安全模式】
(3)我们认为主动进入安全模式:业务场景有关 集群维护
2.进入安全模式对业务有什么影响?
读文件:可以
写文件:不可以
3.相关命令:hdfs dfsadmin -safemode <enter进入安全模式 | leave退出安全模式 | get查看安全模式状态 | wait | forceExit>
Linux kill 杀死进程号 -num pid
kill -9 发送信号给进程 告诉进程 你被终结了 请立刻退出
kill -15 发送信号给进程 告诉进程 你需要被关闭 请自行停止运行并退出
mysql :
查询进程 show processlist;
杀死进程 kill id;
创建数据库时 比如hive 创建一个hive用户
创建数据库流程:
create database dl2262;
grant all privileges on dl2262.* to dl2262 identified by ‘123456’;
flush privileges;
如何查看HDFS块是否丢失?hdfs fsck /
HDFS块丢失该怎么解决?
(1)不用解决 hdfs高容错 多副本情况下 datanode–6h
(2)手动进行恢复丢失的数据块
hdfs debug recoverLease -path [-retries num]
MapReduce数据分析计算
以并行方式(多个task)处理大量数据可以部署在廉价的机器上以容错的方式运行
hdfs容错体现在副本数上,mp容错体现在task上,重试机制,当一个task挂掉时,再重新启动一个task完成刚才挂掉task的任务
适用于离线数据计算,不适用于流式数据计算
mapreduce先从hdfs上获取数据然后经过map阶段和reduce阶段对数据进行处理,最后输出
mapreduce流程:
(input)<k1,v1> -> map-><k2,v2> -> reduce-><k3,v3> (output)<k3,v3>
整个阶段都是对kv进行开发,每个阶段输出都是kv,并且kv数据类型必须要被序列化
把磁盘上的文件读取到内存中的过程叫序列化 把内存上的文件写到磁盘的过程叫反序列化
kv数据类型都要实现了Writable接口,此外key的数据类型还要实现WritableComparable接口
key要实现序列化+排序 value只要实现序列化即可
String:Text
数值:intWritable longWritable flaotWritable doubleWritable等待
Text和intWritable longWritable flaotWritable doubleWritable等待均实现了WritableComparable
以mapreduce为例分析wordcount
input: 文件里面的数据 一行一行的读取 a,a,a,a k是行号
b,b,b,b v是每行数据
map:一一映射 y=f(x) 按照分隔符进行拆分 每个单词赋值为1
(a,1) (a,1) (a,1) (a,1) (b,1) (b,1) (b,1) (b,1)
k表示的是单词 v表示的是次数
reduce:规约、聚合
把相同的key“拉到一起”这个过程就是shuffle “做一些事情”
“拉到一起” shuffle(a,<1,1,1,1>) (b,<1,1,1,1>)
“做一些事情” (a,4) (b,4)
wc案例:mapreduce模板
1.mapper:map阶段代码
2.reducer:reduce阶段代码
3.dirver:定义如何启动map和reduce
map阶段:mapper<KEYIN,VALUEIN,KEYOUT,VALUEOUT>
KEYIN:输入数据key的数据类型
VALUEIN:输入数据value的数据类型
KEYOUT:输出数据key的数据类型
VALUEOUT:输出数据value的数据类型
map阶段的输出作为reduce阶段的输入
// 将待处理或处理好的kv存进集合中
package com.bigdata;
import java.util.HashMap;
import java.util.Map;
public class Context {
Map cacheMap = new HashMap();
public void write(Object key,Object value){
cacheMap.put(key, value);
}
public Object get(Object key){
return cacheMap.get(key);
}
}
package mapreduce;
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.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;
/**
* driver阶段
*/
public class WCAPP {
public static void main(String[] args) throws Exception {
if (args.length < 2){
System.err.println("Usage:WCAPP <in> <out>");
System.exit(2);
}
String input = args[0];
String output = args[1];
//String input = "src/data/wc.data";
//String output = "out";
Configuration conf = new Configuration();
FileUtils.deletePath(conf,output);
// 设置作业名称
Job job = Job.getInstance(conf, "WCAPP");
// 设置map、reduce代码执行的主类在哪
job.setJarByClass(WCAPP.class);
job.setMapperClass(MyMapper.class);
job.setReducerClass(MyReducer.class);
// 指定output的kv类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// 设置数据源路径和数据输出路径
FileInputFormat.addInputPath(job,new Path(input));
FileOutputFormat.setOutputPath(job,new Path(output));
// 提交mr作业到yarn上
// 完成了是0退出 不完成是1报异常
System.exit(job.waitForCompletion(true) ?0:1);
}
}
/**
*map阶段
*/
class MyMapper extends Mapper<Object, Text, Text, IntWritable> {
@Override
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String[] words = value.toString().split("\t");
for (String word : words) {
context.write(new Text(word), new IntWritable(1));
}
}
}
/**
* reduce阶段
*/
class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable value : values) {
sum += Integer.parseInt(value.toString());
}
context.write(key, new IntWritable(sum));
}
}
文件加载时切片
数据: 文件FileInputFormat db(数据库)DBInputFormat
mr里面核心类:
input:InputFormat =》如何加载数据
1.getSplits:获取数据的切片数量
2.createRecordReader 读取数据内容
map:Mapper
map
reduce:Reducer
reducer
output:OutputFormat =》指定数据输出
Map task 个数由什么东西决定的?由切片数量决定
数据输入input :
如果是文件:
FileInputFormat:(FileInputFormat默认是TextInputFormat)
TextInputFormat:<LongWritable, Text>
key:读取文件内容的位置
value:一行一行的内容
1.isSplitable =》 判断你的文件是否可以被切分
1.可以被切分
分片数量 等同于 map task的数据
补充: map task数量由数据input的切片数量决定的
2.不可以被切分
切片数量就是1 对应一个map task
一个文件加载的时候会形成几个切片 ?前提 文件可以被切分
split 切片数量取决于splitsize大小
生产上:splitsize等于blocksize=128M
1.文件能被切分
1.文件大小 小于128M
就有一个切片 =》 一个map task
2.文件大小 大于 128M
fileszie/splitsize = num 切片数
filesize剩余的部分和splitsize的百分之十进行比较
如果剩余部分比splitsize的百分之十大 那么就会额外开启一个切片
如果剩余部分比splitsize的百分之十小 那么就不会开启一个新切片 剩余部分会放在上一个切片里进行处理
2.不能切分的文件
某些压缩文件
数据输入input=160M => 2个
128M =》1
32M > 128M*10% => 1
idea 本地环境:splitsize=32M
Reduce task 个数由什么东西决定的?
1.mr 用户自己决定 job.setNumReduceTasks(num)
2.默认reduce task个数是 1
3.reduce task 个数 =》 最终文件输出的时候 就有多少个文件
分区Partition job.setPartitionerClass(xxx.class)
”把相同的key拉倒一起“
key按照某个规则进行划分到 一起
某个规则: 默认 hash
需求:
基于phone结果数据,进行分文件存储
13开头的结果数据 存储到 一个文件中
15开头的结果数据 存储到 一个文件中
其他结果数据 存储到 一个文件中
1.reduce task 3
2.分区需要自定义
总结:
1.reduce task个数 > partitions 会有空文件
2.reduce task个数 < partitions 会报错
3.reduce task个数 = partitions 没问题
4.reduce task个数1 就是默认值 最终会有一个文件产生
map task是几,Mapper里的setup()和clear up()就执行几次
// 判断输出文件路径是否存在,存在则递归删除
package mapreduce;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class FileUtils {
public static void deletePath(Configuration conf,String outpath) throws Exception{
FileSystem fs = FileSystem.get(conf);
Path path = new Path(outpath);
if(fs.exists(path)){
fs.delete(path,true);
}
}
}
package mapreduce02;
import mapreduce.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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 PhoneAPP {
public static void main(String[] args) throws Exception {
String input = "src/data/phone.txt";
String output = "out/phone";
Configuration conf = new Configuration();
FileUtils.deletePath(conf,output);
Job job = Job.getInstance(conf, "PhoneAPP");
job.setJarByClass(PhoneAPP.class);
job.setMapperClass(MyMapper.class);
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
job.setNumReduceTasks(3);
job.setPartitionerClass(PhonePartitioner.class);
FileInputFormat.addInputPath(job,new Path(input));
FileOutputFormat.setOutputPath(job,new Path(output));
System.exit(job.waitForCompletion(true)?0:1);
}
}
class MyMapper
extends Mapper<Object, Text, Text, Text> {
@Override
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String[] split = value.toString().split("\t");
String phone = split[0];
String up = split[split.length - 3];
String down = split[split.length - 2];
context.write(new Text(phone),new Text(up + "\t" + down));
}
}
class MyReducer extends Reducer<Text, Text,Text,Text>{
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
long sum_up = 0;
long sum_down = 0;
long all = 0;
for (Text value : values) {
String[] split = value.toString().split("\t");
String up = split[0];
String down = split[1];
sum_up+=Long.parseLong(up);
sum_down+=Long.parseLong(down);
}
all=sum_up+sum_down;
context.write(key,new Text(sum_up+"\t"+sum_down+"\t"+all));
}
}
// 设置分区,让特定的输出数据输出特定的文件里
package mapreduce02;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
public class PhonePartitioner extends Partitioner<Text, Text> {
@Override
public int getPartition(Text key, Text value, int numPartitions) {
String phone = key.toString();
if (phone.startsWith("13")){
return 0;
} else if (phone.startsWith("15")){
return 1;
} else{
return 2;
}
}
}
mql底层用mr怎么实现
1.group by => mr
map =>
reduce =>
2.distinct =>group by
3.order by
4. join
// 1.group by
package mapreduce03;
import mapreduce.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
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 GroupAPP {
public static void main(String[] args) throws Exception {
String input = "src/data/groupby.txt";
String output = "out/groupby";
Configuration conf = new Configuration();
FileUtils.deletePath(conf,output);
Job job = Job.getInstance(conf, "GroupAPP");
job.setJarByClass(GroupAPP.class);
job.setMapperClass(MyMapper.class);
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(NullWritable.class);
FileInputFormat.addInputPath(job,new Path(input));
FileOutputFormat.setOutputPath(job,new Path(output));
System.exit(job.waitForCompletion(true)?0:1);
}
}
class MyMapper
extends Mapper<Object, Text, Text, NullWritable> {
@Override
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String[] names = value.toString().split(",");
for (String name : names) {
context.write(new Text(name),NullWritable.get());
}
}
}
class MyReducer
extends Reducer<Text, NullWritable,Text,NullWritable> {
@Override
protected void reduce(Text key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
context.write(key,NullWritable.get());
}
}
// 2.order by
package mapreduce03;
import mapreduce.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
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 OrderByAPP {
public static void main(String[] args) throws Exception {
String input = "out/phone/part-r-00000";
String output = "out/orderby";
Configuration conf = new Configuration();
FileUtils.deletePath(conf,output);
Job job = Job.getInstance(conf, "OrderByAPP");
job.setJarByClass(OrderByAPP.class);
job.setMapperClass(MyMapper01.class);
job.setReducerClass(MyReducer01.class);
job.setMapOutputKeyClass(MyIntWriteAble.class);
job.setMapOutputValueClass(Text.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(NullWritable.class);
FileInputFormat.addInputPath(job,new Path(input));
FileOutputFormat.setOutputPath(job,new Path(output));
System.exit(job.waitForCompletion(true)?0:1);
}
}
class MyMapper01 extends Mapper<Object, Text, MyIntWriteAble, Text> {
@Override
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String[] split = value.toString().split("\t");
String all = split[3];
String phone = split[0];
String up = split[1];
String down = split[2];
context.write(new MyIntWriteAble(Integer.parseInt(all)),new Text(phone+"\t"+up+"\t"+down));
}
}
class MyReducer01 extends Reducer<MyIntWriteAble, Text,Text, NullWritable> {
@Override
protected void reduce(MyIntWriteAble key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
for (Text value : values) {
context.write(new Text(value+"\t"+key),NullWritable.get());
}
}
}
// 重写IntWritable来进行排序
package mapreduce03;
import org.apache.hadoop.io.IntWritable;
public class MyIntWriteAble extends IntWritable {
public MyIntWriteAble() {
}
public MyIntWriteAble(int value) {
super(value);
}
@Override
public int compareTo(IntWritable o) {
return super.compareTo(o);
}
}
// 3.join
package mapreduce03;
import mapreduce.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
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.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
import java.util.ArrayList;
public class JoinAPP {
public static void main(String[] args) throws Exception {
String input="";
String output="";
Configuration conf = new Configuration();
//0.todo... 删除目标路径
FileUtils.deletePath(conf,output);
//1.设置 作业名称
Job job = Job.getInstance(conf, "JoinAPP");
//2.设置map reduce 执行代码的主类
job.setJarByClass(JoinAPP.class);
job.setMapperClass(MyMapper02.class);
job.setReducerClass(MyReducer02.class);
//3.指定 oupput kv类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
//4. 设置数据源路径 数据输出路径
FileInputFormat.addInputPath(job, new Path(input));
FileOutputFormat.setOutputPath(job, new Path(output));
//5. 提交mr yarn
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
class MyMapper02 extends Mapper<Object, Text, Text, Text> {
String tablename;
@Override
protected void setup(Context context) throws IOException, InterruptedException {
FileSplit inputSplit =(FileSplit)context.getInputSplit();
tablename = inputSplit.getPath().getName();
}
@Override
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
if (tablename.startsWith("dept")){
String[] split = value.toString().split(",");
String deptno = split[0];
String dname = split[1];
String loc = split[2];
context.write(new Text(deptno),new Text(dname+"\t"+loc+"dept"));
} else {
String[] split = value.toString().split(",");
String deptno = split[split.length - 1];
String ename = split[1];
String job = split[2];
context.write(new Text(deptno),new Text(ename+"\t"+job+"emp"));
}
}
}
class MyReducer02 extends Reducer<Text, Text,Text,Text> {
String deptTableColumns;
ArrayList<String> empData = new ArrayList();
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
for (Text value : values) {
String[] split = value.toString().split("\t");
String table = split[split.length - 1];
if (table.equals("dept")){
deptTableColumns=split[0]+"\t"+split[1];
} else {
empData.add(split[0]+"\t"+split[1]);
}
}
for (String emp : empData) {
context.write(new Text(emp),new Text(key+"\t"+deptTableColumns));
}
}
}
Yarn架构:主从架构
1.resourcemanager:负责资源的分配
2.nodemanager:负责资源的供给与隔离
资源调度?resourcemanager将nodemanager上资源分配给task
资源隔离?nodemanager按照要求给task提供资源,保证提供的资源具有独占性
每个container之间相互隔离的
资源:nodemanager指挥分配的资源:container[一定比列cpu和内存]
一个container对应一个task
yarn的架构设计【面试】
1.客户端给ResourceManager的ApplicationsManager发送请求去运行jar包 包含一个作业(app master)
2.ApplicationsManager通知nodemanager分配一个container去运行app master
3.app master向ApplicationsManager去注册作业(完成之后yarn web ui 可以看到作业的运行情况)
4.app master向Resource Scheduler申请资源去运行我的代码
5-6.nodeManager收到Resource Scheduler的通知,会去开启container资源去运行map task和reduce task
7.task会向app master汇报代码的运行情况
8.当代码运行完成后,app master会给ApplicationsManager通知我的作业运行完成了,请求释放资源(container资源)
9.ApplicationsManager收到请求之后,通知客户端,你的代码运行完成了
input: map task个数会决定container 申请的个数
reduce : reduce task个数会决定container 申请的个数
container 申请的个数 =map task个数+reduce task个数
调度器
1.FIFO scheduler : 先进先出 单队列
2.Capacity scheduler 容量调度器:(离线任务,实时任务,临时查询任务)
多队列、每个队列之间 互不影响 先进先出
3.Fair scheduler 公平调度器
多队列、 每个队列之间 job 有影响 不是先进先出 队列间按job的优先级决定
默认调度器:Capacity scheduler 容量调度器 3.x yarn-site.xml
2.x默认是公平调度器
yarn.resourcemanager.scheduler.class =》 CapacityScheduler
yarn web : 可以查看queue Scheduler
yarn资源调优
针对集群中 每个节点 对container进行配置
刀片服务器配置: 128G 16物理core
1.刀片服务器 装完系统 消耗1G
2.系统预留 20%内存 [包含 装完系统 消耗1G]128G*%+
为什么系统要预留20%内存?
1.给未来部署组件预留内存空间
2.防止 全部使用 会导致 系统夯住 oom机制(资源不足时会杀死进程)【linux系统】
目前位置 大数据服务资源: 102G
hadoop:Datanode 进程内存 2G
nodemanager 进程内存 4G
102G -2g -4G = 96G => yarn资源 container 一共 96G
container资源划分 : 1.内存 2.cpu 【core】
1.container内存:
总:yarn.nodemanager.resource.memory-mb 96G 【默认值是8G】
最小:yarn.scheduler.minimum-allocation-mb 1g 极限情况下 会有96个container
最大:yarn.scheduler.maximum-allocation-mb 96G 最大不超过32G 极限情况下 会有1个container
注意: container内存会自动增加 默认1g递增
2.container cpu 【虚拟核 vcore =》 设计初衷 考虑不同节点的cpu性能不一样】
第一机器: 强悍 pcore:vcore=1:2 【1core 当成2core使用】
第二机器: 不强悍 pcore:vcore=1:1
16个物理核 : 16vcore
1.虚拟核 和物理核的比例 : yarn.nodemanager.resource.pcores-vcores-multiplier 2
yarn core =》 vcore 162 = 32 vcore
总: yarn.nodemanager.resource.cpu-vcores 32 【默认是8vcore】
最小:yarn.scheduler.minimum-allocation-vcores 1 极限情况下 只有32个container
最大:yarn.scheduler.maximum-allocation-vcores 32 极限情况下 只有1个container
实际开发角度: 1.mem 为主
2.cpu :cloudera 公司推荐 一个contaier 的vcore 最大值最好不要超过5 4
16个物理核 : 16vcore
1.虚拟核 和物理核的比例 :yarn.nodemanager.resource.pcores-vcores-multiplier 2
yarn core =》 vcore 162 = 32 vcore
总:yarn.nodemanager.resource.cpu-vcores 32 【默认是8vcore】
最小:yarn.scheduler.minimum-allocation-vcores 1 极限情况下 只有32个container
最大:yarn.scheduler.maximum-allocation-vcores 4 极限情况下 只有8个container
反推内存:
- vcore 4 container 8个
总:yarn.nodemanager.resource.memory-mb 96G 【默认值是8G】
最小:yarn.scheduler.minimum-allocation-mb 1g 极限情况下 会有96个container
最大:yarn.scheduler.maximum-allocation-mb 12G 极限情况下 会有8个container
注意: container内存会自动增加 默认1g递增
container: - 8-32 个container => task 8-32个
2.实际上以内存为主
总:yarn.nodemanager.resource.memory-mb 96G 【默认值是8G】
最小:yarn.scheduler.minimum-allocation-mb 2G
最大:yarn.scheduler.maximum-allocation-mb 20G
20G => container ? 5个container
2g => container ? 48个container
修改yarn资源
比如单个机器4G 2core
1.内存
总:yarn.nodemanager.resource.memory-mb 4G 【默认值是8G】
最小:yarn.scheduler.minimum-allocation-mb 1g 极限情况下 会有4个container
最大:yarn.scheduler.maximum-allocation-mb 2G 极限情况下 会有2个container
2.cpu 2
1.虚拟核 和物理核的比例 : yarn.nodemanager.resource.pcores-vcores-multiplier 2
总: yarn.nodemanager.resource.cpu-vcores 4 【默认是8vcore】
最小:yarn.scheduler.minimum-allocation-vcores 1 极限情况下 只有4个container
最大:yarn.scheduler.maximum-allocation-vcores 2 极限情况下 只有2个container
vim yarn-site.xml:
内存:
yarn.nodemanager.resource.memory-mb
4096
yarn.scheduler.minimum-allocation-mb
1024
yarn.scheduler.maximum-allocation-mb
2048
cpu:
yarn.nodemanager.resource.pcores-vcores-multiplier
2
yarn.nodemanager.resource.cpu-vcores
4
yarn.scheduler.minimum-allocation-vcores
1
yarn.scheduler.maximum-allocation-vcores
2
xsync yarn-site.xml
mapreduce:map task reduce task 默认申请的container:cpu 内存 分别是多少?
map task:
1.内存 :mapreduce.map.memory.mb 默认是1024
一个map task 申请的内存资源是 1024M
如果map task 实际使用的资源量超过该值 task将会被强制杀死
2.cpu mapreduce.map.cpu.vcores 默认是1
reduce task :
1.内存 mapreduce.reduce.memory.mb 默认是1024
2.cpu mapreduce.reduce.memory.mb 默认是1
jvm调优
mr: 进程级别 运行一个作业会开启很多进程进程,这个进程和yarn和hdfs没关系,它开启的是maptask相关进程可能多个,reduce task相关进程可能多个 =》 jvm (java运行时数据区)
map task :mapreduce.map.java.opts jvm相关调优参数即可
reduce task :mapreduce.reduce.java.opts jvm相关调优参数即可
jvm参数调优:
1.提交mr作业时可以指定 ?当前job生效
hadoop jar xxx.jar xxx参数 指定job运行时资源的参数
2.集群环境里面可以配置:? 全局生效
mapreude-site.xml
3.最常见的 code =》 当前job生效
code Configration设置参数
存储文件进行调优【压缩】
1.为什么要使用压缩?
1.节省空间 (1file hdfs 3个副本)
2.节省时间(数据发送时间): 网络io 和磁盘io 会减少
1.mapreduce 过程中 map端输出采用压缩
1.经过网络传输的数据量会变少 【减少了网络io】
2.磁盘上数据压缩后,读取到内存的数据量也会变少 【减少磁盘io】
2.使用场景?
1.采用压缩,对机器的cpu要求比较高
2.存储数据的空间不够
3.凡事都有两面性?
1.采用压缩 1.空间 和读取数据和网络传输时间会减少
2.cpu消耗比较大 cpu利用率高 =》 会导致整个mr作业处理数据时间会稍微长一些(发送数据时间的减少不能弥补压缩的时间)
4.如何使用压缩?
1.存储角度
上传到hdfs上的文件直接压缩,压缩普通文件
2.计算角度 mr spark flink
1.input mr 加载压缩文件不用考虑,因为文件已经压缩完了,压缩完的文件通过inputformat(切片)直接对文件进行读取 切片会对压缩格式进行校验 =》 源码里面会判断是否是压缩文件,如果是压缩文件进行强转
2.map out 配置参数即可
3.reduce out 配置参数即可
5.常见的压缩格式
gzip .gz
bzip2 .bz2
LZO .lzo
Snappy .snappy
LZ4 .lzo4
1.压缩比
Bzip2:30% GZIP snappy、lzo:50%
解压速度:
snappy、lzo > GZIP > Bzip2
2.压缩文件是否可以被切分?
1.假设一个 5G文件 不能被切分split意味着 只能使用一个map task去处理 效率不高
map task 处理的数据量是5G
2.假设一个 5G文件 能被切片splits 假如被切分为10个分片,10map task 去并行处理
5*1024 /10 = 一个map task 处理的数据量M
3. 能否被切分 决定了 你的 一个map task处理的数据量有多少,在资源充足的情况下,多并行度处理要比单并行度处理效率高些
3.压缩后的文件是否支持分割?
gzip 不可分割
bzip2 可分割
lzo 带索引的可以分割 (默认是不支持分割的)
snappy 不可分割的
4.mapreduce 每个阶段该如何采用这些算法?
compressed input =>decompressed input=> maps(一个切片对应一个map task) =>map out compressed =>decompressed map out=> reduce => reduce out compressed=> output
input:建议使用压缩,支持分片
1.Bzip2
支持分割 多个map task 进行出
map out:解压速度(网络传输),不用考虑是否能切片,切片是读取数据时要考虑的
1. shuffle 过程 要选择一个压/解 速度快的压缩格式
2.snappy 、lzo
reduce out :高压缩比的目的是节省空间,因为reduce输出的数据将来还有可能作为下一个map的输入,还要考虑能否切片的问题
1.高的压缩比 + 支持分片 =》 节省空间
2.bzip2 、lzo带索引的
reduce out 数据 作为下一个map 的输入咋办?
建议使用bzip2【如果采用压缩的话】
如何进行配置?
1.某个作业 =》 code代码里面添加 【某个job生效】
2.集群所有作业 =》 在配置文件里面加 【全局生效】
mapreduce 压缩配置:input阶段不需要配置
1.配置压缩的codec
2.map reduce 输出配置:
1.先打开压缩的开关
2.配置codec 即可
hadoop集群hdfs 支持哪些压缩?gzip(默认)、snappy、bzip2、lz4
1.命令 hadoop checknative
2.core-site.xml可以配置:配置支持的压缩有哪些
io.compression.codecs 控制
mapred-site.xml里面可以配置:
1.先打开压缩的开关
2.map reduce 输出 压缩算法
补充:
原生 hadoop 默认不支持 lzo的压缩
vim core-site.xml 配置支持的压缩有哪些
bzip2、gzip、snappy
io.compression.codecs
org.apache.hadoop.io.compress.BZip2Codec,
org.apache.hadoop.io.compress.SnappyCodec,
org.apache.hadoop.io.compress.GzipCodec,
org.apache.hadoop.io.compress.DefaultCodec
vim mapred-site.xml:
1.先打开压缩的开关
2.map reduce 输出 压缩算法
reduce: 开关
mapreduce.output.fileoutputformat.compress
mapreduce.output.fileoutputformat.compress
true
mapreduce.output.fileoutputformat.compress.codec
mapreduce.output.fileoutputformat.compress.codec
org.apache.hadoop.io.compress.BZip2Codec这里面的压缩格式可以换
hadoop fs -text 压缩文件 可以查看压缩文件 -text是以mr的方式进行查看文件内容,一定有input阶段,input阶段对压缩文件没有要求,任何压缩文件都可以看到
各个DN节点数据平衡
sbin目录下start-balancer.sh
eg:DN1 存储空间 90%
DN2 存储空间 60%
DN3 存储空间 80%
数据平衡的默认阈值:threshold = 10.0
每个节点的磁盘使用率 - 平均的磁盘使用率 <10%
avg=90 + 80 +60 /3 = 76%
DN1 存储空间 90% -76% = 14% 说明这个节点数据多 往别的节点迁移数据 出
DN2 存储空间 60% -76% = -12% 说明这个节点数据少 别的节点迁移数据 进
DN3 存储空间 80% -76% = 4%
生产上 从现在开始 start-balancer.sh -threshold 10 每天要定时做的
放到业务低谷期去做 数据平衡操作 ,不要在业务高峰期做
数据平衡 数据传输 带宽有关
调优参数 :平衡的网络带宽 w
(3.x)dfs.datanode.balance.bandwidthPerSec 默认是100M 【2.x 默认是10M】
单个DN节点 多块磁盘的数据平衡 nodename有副本数,不要做raid
做raid:备份,把磁盘分成两份,一份读一份写,但是内容是一样的
查看磁盘挂载df -h
1.dn 配置多个磁盘
dfs.datanode.data.dir
/data01,/data02,/data03磁盘
2.为什么要使用多块物理磁盘?
1.存储
2.因为多个磁盘的io也是叠加的
每块磁盘 磁盘io 每秒 100M
三块磁盘 1s 能 300M文件内容
一块磁盘 1s 100M
3./data01,/data02,/data03
做多个磁盘数据均衡
dfs.disk.balancer.enabled true 【3.x有这个功能 cdh 2.x 也有】 apache 2.x 没有这个功能
得通过命令去解决磁盘数据均衡?
hdfs diskbalancer
1.步骤
hdfs diskbalancer -plan bigdata32 => 生成一个 bigdata32.plan.json 文件
hdfs diskbalancer -execute bigdata32.plan.json =》 执行disk 数据均衡计划文件
hdfs diskbalancer -query bigdata32
生产上 当你发现 磁盘写入不均衡 可以做一下 【一般 一个月 半个月 做一次即可】