第5章MapReDuce
5.1 数据倾斜的原因:
key 分布不均匀 业务数据本身的欠缺性 建表设计方法不对 有些 SQL 难免会有一下数据倾斜不可避免 表现的形式: 任务完成进度卡死在99%,或者进度完成度在100%但是查看任务监控,发现还是有少量(1个或几个)reduce 子任务未完成。因为其处理的数据量和其他 reduce 差异过大。单一reduce 的记录数与平均记录数差异过大,通常可能达到3倍甚至更多。 最长时长远大于平均时长。
解决方案: 参数调整: hive.map.aggr=true: Map 端部分聚合,相当于 Combiner hive.groupby.skewindata=true: 有数据倾斜的时候进行负载均衡,当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。 2:参数调节: 如何 Join: 关于驱动表的选取,选用 join key 分布最均匀的表作为驱动表 做好列裁剪和 filter 操作,以达到两表做 join 的时候,数据量相对变小的效果 大小表 Join: 使用 map join 让小的维度表(1000条以下的记录条数) 先进内存。在 map 端完成 reduce. 大表 Join 大表: 把空值的 key 变成一个字符串加上随机数,把倾斜的数据分到不同的 reduce 上,由于 null值关联不上,处理后并不影响最终结果 count distinct 大量相同特殊值 count distinct 时,将值为空的情况单独处理,如果是计算 count distinct,可以不用处理,直接过滤,在最后结果中加1。如果还有其他计算,需要进行 group by,可以先将值为空的记录单独处理,再和其他计算结果进行 union。 group by 维度过小: 采用 sum() group by 的方式来替换 count(distinct) 完成计算。 特殊情况特殊处理: 在业务逻辑优化效果的不大情况下,有些时候是可以将倾斜的数据单独拿出来处理。最后 union 回去。 如果确认业务需要这样倾斜的逻辑,考虑以下的优化方案: 总结: 1、对于 join,在判断小表不大于1 G 的情况下,使用 map join 2、对于 group by 或 distinct,设定 hive.groupby.skewindata=true 3、尽量使用上述的 SQL 语句调节进行优化
5.2 编写 mapreduce 的方式:
java 编写-常用 Hadoop Streaming:使用 unix 标准的输入和输出流作为 hadooop 和应用程序之间的接口,支持像Ruby,python 等不同的编程语言编写 map 和 reduce Hadoop Pipes 是 hadoop 提供的 C++ 的接口的名称
5.3 请简述 mapreduce 中的 combine 和 partition 的作用
答:combiner 是发生在 map 的最后一个阶段,其原理也是一个小型的 reducer,主要作用是减少输出到 reduce 的数据量,提高网络传输瓶颈,提高 reducer 的执行效率。 partition 的主要作用将 map 阶段产生的所有 k,v 对分配给不同的 reducer task 处理,可以将 reduce 阶段的处理负载进行分摊。
5.4 用 mapreduce 怎么处理数据倾斜问题
本质:让各个分区的数据均匀分布,并且根据自己的业务特点设置合适的 partition 策略,具体的设置方法可以上网查询一下,这里就不过多的介绍了。如果事先不知道业务数据的分布规律,只能利用随机抽样之后生成 partition 策略后再做处理
5.5 我们在开发分布式计算 job 的时候,是否可以去掉 reduce 阶段
答:可以,如果不涉及到有关数据的计算的话还是可以省才去 mapreduce 阶段的
5.6 mapreduce 的作业调度模式
答: 公平调度器:为每个任务分配资源的方法,按照作业的优先级高低,再按照到达时间的先后选择被执行的作业
5.7 flush 的过程
答:flush 是在内存的基础上进行的,首先写入文件的时候,会先将文件写到内存中,当内存写满的时候,一次性的将文件全部都写到硬盘中去保存,并清空缓存中的文件,
5.8 MapReduce 优化经验
答:1.设置合理的 map 和 reduce 的个数。合理设置块的大小,要注意一个任务对应一个 map 2避免数据倾斜,合理分配数据对应的 key,尽量对 sql 进行优化 3 combine 函数 4 对数据进行压缩处理,必要的时候对数据进行拆分。 5小文件处理优化:事先合并成大文件,combineTextInputformat,在 hdfs 上用 mapreduce 将小文件合并成 SequenceFile 大文件(key: 文件名,value:文件内容),并且要定期在非工作时间做一次大合并,但是要提前估算好工作量,因为大合并期间所有任务是没办法执行的。 6参数优化,具体什么参数比较多大家可以自行百度。
5.9 请列举出曾经修改过的 /etc/ 下面的文件,并说明修改要解决什么问题?
答:/etc/profile 这个文件,主要是用来配置环境变量。让 hadoop 命令可以在任意目录下面执行。但是每个开发人员都有自己的目录设置习惯,这个需要根据自己的习惯具体来回答。 /ect/sudoers /etc/hosts /etc/sysconfig/network /etc/inittab
5.10 mapreduce 的大致流程(运行一个hadoop的任务流程)
答:主要分为八个步骤
•导入数据对需分析的数据进行分片,片的大小默认与 datanode 块大小相同。
•启动相应数量的 maptask 进程
•每个数据片由一个 mapper 进行分析,mapper 按照需求将数据拆分为一个个 key/value 格式的数据。
•调用自定义的 map 函数,并将 k1v1 传给 map,一个任务对应一个 map
•收集 map 的输出,进行分区和排序,这块要注意优化。
•reduce task 任务启动,并从 map 端拉取数据
•reduce task 调用自定义的 reduce 函数进行处理
•调用 outputformat 的 recordwriter 将结果数据输出
5.11 举一个例子说明 mapreduce 是怎么运行的。
自带的实例 Wordcount,但是最好是自己准备一个写熟了的例子。
5.12 给你一个1G的数据,分别有id,name,mark,source四个字段,按照mark分组,id排序,手写一个MapReduce?其中有几个Mapper?
1)MapReduce实现
方案一:在map端对mark和id进行排序
@Override
publicint compareTo(SortBean o) {
intresult;
if (this.mark > o.getMark()) {
result = 1;
}elseif(this.mark o.getId()? -1:1;
}
returnresult;
}
方案二:在map端对mark排序,在reduce端对id分组。
@Override
public int compareTo(GroupBean o) {
int result;
result = this.mark > o.mark ? -1 : 1;
return result;
}
@Override
public int compare(WritableComparable a, WritableComparable b) {
GroupBean aBean = (GroupBean) a;
GroupBean bBean = (GroupBean) b;
int result;
if (aBean.getId() > bBean.getId()) {
result = 1;
} else if (aBean.getId() < bBean.getId()) {
result = -1;
} else {
result = 0;
}
return result;
}
2)几个mapper
(1)1024m/128m=8块
MapReduce怎么解决数据均衡问题 如何确定分区号
如果没有自定义的 partitioning,则默认的 partition 算法,即根据每一条数据的 key的 hashcode 值摸运算(%)reduce 的数量,得到的数字就是“分区号“。
5.13 mr环形数组怎么设置,最大能设置多大
shuffle性能优化的关键参数,应在yarn启动之前就配置好(mapred-default.xml)
配置参数 参数说明
mapreduce.task.io.sort.mb 100 shuffle的环形缓冲区大小,默认100m
mapreduce.map.sort.spill.percent 0.8 环形缓冲区溢出的阈值,默认80%
5.14手写MapReduce的WordCount(导入的包最好能记住)
需求:在一堆给定的文本文件中统计输出每一个单词出现的总次数
(1)定义一个mapper类
//首先要定义四个泛型的类型
//keyin: LongWritable valuein: Text
//keyout: Text valueout:IntWritable
public class WordCountMapper extends Mapper
//map方法的生命周期: 框架每传一行数据就被调用一次
//key : 这一行的起始点在文件中的偏移量
//value: 这一行的内容
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//拿到一行数据转换为string
String line = value.toString();
//将这一行切分出各个单词
String[] words = line.split("");
//遍历数组,输出<单词,1>
for(String word:words){
context.write(new Text(word), new IntWritable(1));
}
}
}
(2)定义一个reducer类
//生命周期:框架每传递进来一个kv 组,reduce方法被调用一次
@Override
protected void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException {
//定义一个计数器
int count = 0;
//遍历这一组kv的所有v,累加到count中
for(IntWritable value:values){
count += value.get();
}
context.write(key, new IntWritable(count));
}
}
(3)定义一个主类,用来描述job并提交job
public class WordCountRunner {
//把业务逻辑相关的信息(哪个是mapper,哪个是reducer,要处理的数据在哪里,输出的结果放哪里……)描述成一个job对象
//把这个描述好的job提交给集群去运行
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job wcjob = Job.getInstance(conf);
//指定我这个job所在的jar包
// wcjob.setJar("/home/hadoop/wordcount.jar");
wcjob.setJarByClass(WordCountRunner.class);
wcjob.setMapperClass(WordCountMapper.class);
wcjob.setReducerClass(WordCountReducer.class);
//设置我们的业务逻辑Mapper类的输出key和value的数据类型
wcjob.setMapOutputKeyClass(Text.class);
wcjob.setMapOutputValueClass(IntWritable.class);
//设置我们的业务逻辑Reducer类的输出key和value的数据类型
wcjob.setOutputKeyClass(Text.class);
wcjob.setOutputValueClass(IntWritable.class);
//指定要处理的数据所在的位置
FileInputFormat.setInputPaths(wcjob, "hdfs://hdp-server01:9000/wordcount/data/big.txt");
//指定处理完成之后的结果所保存的位置
FileOutputFormat.setOutputPath(wcjob, new Path("hdfs://hdp-server01:9000/wordcount/output/"));
//向yarn集群提交这个job
boolean res = wcjob.waitForCompletion(true);
System.exit(res?0:1);
}
5.15 一句话介绍MapReduce
分布式计算模型
1)首先map task会从本地文件系统读取数据,转换成key-value形式的键值对集合,使用的是hadoop内置的数据类型(longwritable、text)
2)将键值对集合输入mapper进行业务处理过程,将其转换成需要的key-value在输出
3)之后会进行一个partition分区操作,默认使用的是hashpartitioner,自定义分区:重写getpartition方法
4)之后会对key进行进行sort排序,grouping分组操作将相同key的value合并分组输出
5)之后进行一个combiner归约操作,其实就是一个本地段的reduce预处理,以减小后面shufle和reducer的工作量
6)reduce task会通过网络将各个数据收集进行reduce处理
7)最后将数据保存或者显示,结束整个job
5.16 MapReduce数据倾斜和内存溢出怎么办
1)什么是数据倾斜
答:大量数据涌入到某一节点,导致此节点负载过重,此时就产生了数据倾斜。
某一个区域的数据量要远远大于其他区域。
2)处理数据倾斜的4种方案
(1)重新设计key
(2)自定义分区:基于输出键的背景知识进行自定义分区。例如,如果map输出键的单词来源于一本书。且其中某几个专业词汇较多。那么就可以自定义分区将这这些专业词汇发送给固定的一部分reduce实例。而将其他的都发送给剩余的reduce实例。
(3)Combine:使用Combine可以大量地减小数据倾斜。在可能的情况下,combine的目的就是聚合并精简数据。
(4)采用Map Join,尽量避免Reduce Join。
3)内存溢出解决办法:
问题原因: Hadoop运行时使用的虚拟内存不足, hadoop物理内存默认与主机的内存一致,hadoop虚拟内存默认是hadoop物理内存的2.1倍。
(1)hadoop物理内存的大小可以在mapred-site.xml设置:
mapreduce.map.memory.mb
2048
mapreduce.reduce.memory.mb
2048
(3)或修改hadoop虚拟内存与hadoop物理内存的比率值可以在yarn-site.xml设置:
yarn.nodemanager.vmem-pmem-ratio
3
5.17 遇到垃圾日志怎么办
(假如说有人恶意制造日志,怎么处理。我说的黑名单机制,那个面试官貌似还挺满意)