HDFS
扩展
数据平衡
-
当HDFS集群中,存储空间不足的时候,那么此时可能会选择新添磁盘或者新添节点,那么添加完之后,考虑做数据在磁盘或者节点之间的平衡
-
节点内(磁盘)的平衡
-
假设第一台服务器新添了一块磁盘,那么此时需要在第一台服务器上生成均衡计划
hdfs diskbalancer -plan hadoop01 # 执行完成之后,会在当前目录下生成一个均衡文件hadoop01.plan.json
-
执行均衡计划
hdfs diskbalancer -execute hadoop01.plan.json
-
查看均衡任务的执行情况
hdfs diskbalancer -query hadoop01
-
取消均衡计划
hdfs diskbalancer -cancel hadoop01.plan.json
-
-
节点间的平衡
-
开启均衡任务
# start-balancer.sh -threhold n # threhold-阈值,n表示节点之间的使用差 # 10表示当各个节点之间,磁盘利用率相差如果超过10%,那么就需要进行均衡 start-balancer.sh -threhold 10
-
关闭均衡
stop-balancer.sh
-
集群间的数据拷贝
-
如果需要将数据从一个HDFS集群拷贝到另一个HDFS集群,那么可以执行
hadoop distcp hdfs://hadoop01:9000/a.txt hdfs://hadoop101:9000/a.txt
-
实际过程中,较少发生。一般是在进行集群的合并的时候,才会考虑集群之间的数据拷贝
Federation HDFS(了解)
- 当前HDFS架构存在一个小小的问题
- 集群中只有一个NameNode,那么就意味着所有的请求都需要经过这唯一的NameNode进行处理,NameNode的处理效率一定程度决定了HDFS的效率
- 因为只有1个NameNode,所以导致所有的元数据都需要记录到NameNode上。实际开发中,一台服务器如果被设置为了NameNode,那么这台服务器需要大约腾出来50GB(大约是2亿~3亿条元数据)左右的内存给NameNode使用
- 考虑到上述问题,HDFS提供了一种缓解方案:Federation HDFS(联邦HDFS)。将路径拆分到不同的节点上,当客户端产生请求的时候,根据客户端的请求路径不同,那么将请求分发到不同的节点上处理,此时利用了多个NameNode来共同承担原来一个NameNode的责任,这种情况称之为联邦HDFS
- 联邦HDFS的特点
- 利用多台服务器来处理请求,那么可以一定程度上提高集群的并发量(能够接受的请求数量),也能够更充分的利用集群的磁盘的IO资源
- 这种方式的特点在于,HDFS上的虚拟路径相对固定,因此联邦HDFS更适合于业务相对稳定的场景
- 在联邦HDFS中,要求所有的NameNode的BlockPoolID相同
MapReduce
概述
- MapReduce是Doug根据的Google的<The Google MapReduce>来仿照实现的
- MapReduce将整个计算过程拆分为了两个大阶段:Map(映射)阶段和Reduce(规约)阶段
入门案例
-
案例:统计这个文件中每一个字符出现的次数
-
MapReduce刚开始的时候,会先对要处理的文件进行切片(Split)。
- Split是逻辑切分,是在划分任务量
- Block是物理切分,是在确定存储单位
-
实际过程中,考虑到数据要跨节点传输,为了减少跨节点的数量,所以Split=Block/n。在MapReduce中,默认Split和Block等大
-
Mapper类
package com.fesco.charcount; 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 // 在MapReduce中,要求传输的数据能够被序列化 // 而MapReduce使用的不是Java的原生序列化机制,而是采用了新的序列化机制 // KEYIN - 输入的键的类型,是这一行的字节偏移量 // VALUEIN - 输入的值的类型,是这一行数据 // KEYOUT - 输出的键的类型。当前案例中,输出的键是字符 // VALUEOUT - 输出的值的类型。当前案例中,输出的值是次数 public class CharCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> { private final IntWritable once = new IntWritable(1); // MapTask的处理逻辑是放在map方法中 // 切片中的每一行都会调用一次map方法进行处理 // key:键。每一行的字节偏移量 // value:值。这一行数据 // context:环境参数 @Override protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException { // 将这一行中的字符拆分出来 char[] cs = value.toString().toCharArray(); // for (char c : cs) { context.write(new Text(String.valueOf(c)), once); } } }
-
Reducer类
package com.fesco.charcount; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; // KEYIN,VALUEIN - 输入的键和值。Reducer的数据是Mapper产生的,那么就意味着Mapper的输出类型就是Reducer的输入类型 // KEYOUT,VALUEOUT - 输出的键和值。当前案例中,最终输出的字符和对应的次数 public class CharCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> { // key:键。当前案例中,键是字符 // values:值。Reduce开始的时候会将相同的键对应的值放到一起,这个过程称之为分组。分组结束之后,会将值放入迭代器中,每一个键调用一次reduce方法 @Override protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException { // key:h // values:1,1,1,1,1,1.... // 定义变量记录和 int sum = 0; // 遍历values for (IntWritable value : values) { sum += value.get(); } context.write(key, new IntWritable(sum)); } }
-
Driver类(入口类)
package com.fesco.charcount; 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 CharCountDriver { public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException { // 环境参数 Configuration conf = new Configuration(); // 申请任务 Job job = Job.getInstance(conf); // 指定入口类 job.setJarByClass(CharCountDriver.class); // 指定Mapper类 job.setMapperClass(CharCountMapper.class); // 指定Reducer类 job.setReducerClass(CharCountReducer.class); // 指定Mapper的输出类型 job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(IntWritable.class); // 指定Reducer的输出类型 job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); // 指定输入路径 FileInputFormat.addInputPath(job, new Path("hdfs://10.16.3.181:9000/txt/characters.txt")); // 指定输出路径,要求输出路径必须不存在 FileOutputFormat.setOutputPath(job, new Path("hdfs://10.16.3.181:9000/result/char_count")); // 提交任务 job.waitForCompletion(true); } }
指定输出路径,要求输出路径必须不存在
FileOutputFormat.setOutputPath(job, new Path(“hdfs://10.16.3.181:9000/result/char_count”));
// 提交任务
job.waitForCompletion(true);
}
}