大数据学习——MapReduce

目录

1 MapReduce概述

1.1 MapReduce定义

1.2 MapReduce优缺点

1.3 MapReduce核心思想

1.4 WordCount

1.4.1计算流程

1.4.2环境准备

创建maven工程,添加如下依赖

配置log4j.properties

1.4.3 编写程序

(1)Mapper类

(2)Reducer类

(3)Driver

1.4.4 测试

本地测试

集群测试

2 Hadoop序列化

2.1 自定义bean对象实现序列化接口(Writable)

2.2 序列化案例

2.2.1 数据准备

2.2.2 计算流程

2.2.3编写程序

3 MapReduce框架原理

3.1 InputFormat数据输入

3.1.1 MapTask并行度决定机制

3.1.2 FileInputFormat切片流程

3.1.3 FileInputFormat切片机制

3.2 OutputFormat数据输出

3.3 Shuffle机制

3.3.1自定义Partition分区

3.3.4 WritableComparable排序

3.3.5 Combiner合并

3.4 Join应用

3.5 ETL数据清洗 

3.5.1 日志清洗案例:

3.6 MapReduce详解

3.6.1 MapTask

3.6.2 ReduceTask

4 Hadoop数据压缩

4.1 概述

4.2 压缩算法对比

4.3 压缩参数配置

4.4 压缩案例


1 MapReduce概述

1.1 MapReduce定义

MapReduce是一个分布式运算程序的编程框架,核心功能是将用户编写的业务逻辑代码自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上。

1.2 MapReduce优缺点

优点

1)MapReduce易于编程

它简单的实现一些接口,就可以完成一个分布式程序,也就是说写一个分布式程序,跟写一个简单的串行程序是一模一样的。

2)良好的扩展性

计算资源不足时可以通过简单的增加机器来扩展计算能力。

3)高容错性

如果其中一台机器挂了,它可以把上面的计算任务自动转移到另外一个节点上运行,不至于这个任务运行失败

4)适合PB级以上海量数据的离线处理

缺点

1)不擅长实时计算

MapReduce无法像MySQL一样,在毫秒或者秒级内返回结果。

2)不擅长流式计算

流式计算的输入数据是动态的,而MapReduce的输入数据集是静态的,不能动态变化。这是因为MapReduce自身的设计特点决定了数据源必须是静态的。

3)不擅长DAG(有向无环图)计算

多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。这种情况下,每个MapReduce作业的中间结果都会落盘产生大量IO。

1.3 MapReduce核心思想

分布式的运算程序往往需要分成至少2个阶段。

        第一个阶段的MapTask并发实例,完全并行运行,互不相干。

      第二个阶段的ReduceTask并发实例互不相干,但是他们的数据依赖于上一个阶段的所有MapTask并发实例的输出。

3MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行运行。

1.4 WordCount

1.4.1计算流程

1.4.2环境准备

创建maven工程,添加如下依赖

<dependencies>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>3.1.3</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.30</version>
    </dependency>
</dependencies>

配置log4j.properties

log4j.rootLogger=INFO, stdout  
log4j.appender.stdout=org.apache.log4j.ConsoleAppender  
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout  
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n  
log4j.appender.logfile=org.apache.log4j.FileAppender  
log4j.appender.logfile.File=target/spring.log  
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout  
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

1.4.3 编写程序

mapreduce程序主要通过继承Mapper和Reducer类并重写对应方法构成,其中Mapper和Reducer端分别负责数据读取,加工和输出,Driver端负责程序配置,如联系M端R端以及运行环境,输出类型和数据输入输出位置等。

(1)Mapper类

package com.atguigu.mapreduce.wordcount;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
	
	Text k = new Text();
	IntWritable v = new IntWritable(1);
	
	@Override
	protected void map(LongWritable key, Text value, Context context)	throws IOException, InterruptedException {
		
		// 1 获取一行
		String line = value.toString();
		
		// 2 切割
		String[] words = line.split(" ");
		
		// 3 输出
		for (String word : words) {
			
			k.set(word);
			context.write(k, v);
		}
	}
}

context实现了与Hadoop框架的交互,该类可用于读写数据,记录日志等操作。在这段代码中,传入Context对象可以将数据写入Hadoop集群中的分布式文件系统中,或者读取其他节点上的数据进行处理。

(2)Reducer类

package com.atguigu.mapreduce.wordcount;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable>{

int sum;
IntWritable v = new IntWritable();

	@Override
	protected void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException {
		
		// 1 累加求和
		sum = 0;
		for (IntWritable count : values) {
			sum += count.get();
		}
		
		// 2 输出
         v.set(sum);
		context.write(key,v);
	}
}

(3)Driver

        1 获取配置信息和获取job对象

        2 关联本Driver程序,Mapper和Reducer的jar

        3 设置Mapper阶段和最终输出的kv类型

        4 设置输入和输出路径并提交job

package com.atguigu.mapreduce.wordcount;
import java.io.IOException;
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;

public class WordCountDriver {

	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

		// 1 获取配置信息以及获取job对象
		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf);

		// 2 关联本Driver程序,Mapper和Reducer的jar
		job.setJarByClass(WordCountDriver.class);
		job.setMapperClass(WordCountMapper.class);
		job.setReducerClass(WordCountReducer.class);

		// 3 设置Mapper阶段和最终输出kv类型
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(IntWritable.class);
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);
		
		// 4 设置输入和输出路径并提交job(本地测试时输入和输出路径为磁盘中绝对路径)
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		FileOutputFormat.setOutputPath(job, new Path(args[1]));
		boolean result = job.waitForCompletion(true);
		System.exit(result ? 0 : 1);
	}
}

1.4.4 测试

本地测试

配置HADOOP_HOME环境变量和Windows运行依赖,参照p7276_尚硅谷_Hadoop_MapReduce_WordCount案例Driver_哔哩哔哩_bilibili

集群测试

配置打包插件依赖

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.6.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

上传到Hadoop集群的hadoop根路径,启动集群:

[atguigu@hadoop102 hadoop-3.1.3]sbin/start-dfs.sh

[atguigu@hadoop102 hadoop-3.1.3]sbin/start-yarn.sh

执行程序:

[atguigu@hadoop102 hadoop-3.1.3]

hadoop jar wc.jar com.atguigu.mapreduce.wordcount.WordCountDriver /user/atguigu/input /user/atguigu/output

1.5 MapReduce的高级特性

序列化、排序、分区、合并。

1.序列化(Serializable):

(1)核心接口是:Writable,如果一个类实现了Writable接口,该类的对象可以作为key和value。

(2)Java中的序列化是实现Serializable。

(3)序列化的注意事项:

1)、反序列化时,需要调用空参构造函数,所以必须有空参构造。2)、重写序列化方法。3)、重写反序列化方法(顺序必须和序列化的顺序一样)。4)、想要把结果显式在文件中,需要重写toString方法。5)、如果要自定义bean放在key中传输,则需要实现Comparable接口。

2.排序(sort):

(1)MapReduce自带的排序,默认是按照key排序。

(2)自定义排序:需要实现writablecomparable接口,重写compareTo。

(3)二次排序:一般使用SortComparator和GroupComparator,先是调用SortComparator,如果满足则调用GroupComparator函数,只要两个key相同,就会把它们的value放到一个value迭代器中,如果没有满足则调用key自己实现的compareTo方法。

3.分区(partitioner):

(1)分区是根据MR的输出<key,value>进行分区的。默认情况下,MR的输出只有一个分区,一个分区就是一个文件。

(2)自定义分区:继承Partitioner,重写getPartition这个方法。

(3)如果没有定义partitioner,那数据在被送达Reducer前是按照每一条数据的key的hashcode进行分区的。

4.合并(Combiner):

(1)Combiner是一种特殊的Reducer,它是对每一个mapTask的输出进行局部汇总,以减少网络传输量。

(2)好处是:合并在Mapper端执行一次合并,用于减少Mapper输出到Reducer的数量,可以调高效率。

(3)谨慎使用Combiner,Combiner的输出的k,v应该和Reducer的输入的k,v相对应。不能用的Combiner的例子,例如:求平均值。

(4)Combiner和Reducer的区别在于运行的位置:Combiner是在每一个mapTask所在的节点运行。Reducer是接收全局所有Mapper的输出结果

2 Hadoop序列化

Java的序列化是一个重量级序列化框架(Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息,Header,继承体系等)开销太大。因此大数据框架普遍采用自己的序列化机制。

2.1 自定义bean对象实现序列化接口(Writable)

具体实现bean对象序列化步骤如下

(1)实现Writable接口

(2)反序列化时,需要反射调用空参构造函数,所以必须有空参构造

(3)重写序列化和反序列化方法,同时要求顺序一致

(4)如果需要将自定义的bean放在key中传输,则还需要实现Comparable接口,因为MapReduce框中的Shuffle过程要求对key必须能排序。

2.2 序列化案例

2.2.1 数据准备

输入数据格式:

7      13560436666      120.196.100.99            1116           954                    200

id      手机号码            网络ip                         上行流量  下行流量     网络状态码

期望输出数据格式

13560436666              1116                 954                            2070

手机号码                上行流量        下行流量                   总流量

自建数据或者参照p79尚硅谷大数据Hadoop教程(Hadoop 3.x安装搭建到集群调优)_哔哩哔哩_bilibilip

2.2.2 计算流程

2.2.3编写程序

(1)流量统计的Bean对象

package com.atguigu.mapreduce.writable;

import org.apache.hadoop.io.Writable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

//1 继承Writable接口
public class FlowBean implements Writable {

    private long upFlow; //上行流量
    private long downFlow; //下行流量
    private long sumFlow; //总流量

    //2 提供无参构造
    public FlowBean() {
    }

    //3 提供三个参数的getter和setter方法
    public long getUpFlow() {
        return upFlow;
    }

    public void setUpFlow(long upFlow) {
        this.upFlow = upFlow;
    }

    public long getDownFlow() {
        return downFlow;
    }

    public void setDownFlow(long downFlow) {
        this.downFlow = downFlow;
    }

    public long getSumFlow() {
        return sumFlow;
    }

    public void setSumFlow(long sumFlow) {
        this.sumFlow = sumFlow;
    }

    public void setSumFlow() {
        this.sumFlow = this.upFlow + this.downFlow;
    }

    //4 实现序列化和反序列化方法,注意顺序一定要保持一致
    @Override
    public void write(DataOutput dataOutput) throws IOException {
        dataOutput.writeLong(upFlow);
        dataOutput.writeLong(downFlow);
        dataOutput.writeLong(sumFlow);
    }

    @Override
    public void readFields(DataInput dataInput) throws IOException {
        this.upFlow = dataInput.readLong();
        this.downFlow = dataInput.readLong();
        this.sumFlow = dataInput.readLong();
    }

    //5 重写ToString
    @Override
    public String toString() {
        return upFlow + "\t" + downFlow + "\t" + sumFlow;
    }
}

write和readFields方法具体实现了序列化和反序列化(将对象转换为字节流),注意保持顺序一致。

(2)Mapper类

package com.atguigu.mapreduce.writable;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;

public class FlowMapper extends Mapper<LongWritable, Text, Text, FlowBean> {
    private Text outK = new Text();
    private FlowBean outV = new FlowBean();

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

        //1 获取一行数据,转成字符串
        String line = value.toString();

        //2 切割数据
        String[] split = line.split("\t");

        //3 抓取我们需要的数据:手机号,上行流量,下行流量
        String phone = split[1];
        String up = split[split.length - 3];
        String down = split[split.length - 2];

        //4 封装outK outV
        outK.set(phone);
        outV.setUpFlow(Long.parseLong(up));
        outV.setDownFlow(Long.parseLong(down));
        outV.setSumFlow();

        //5 写出outK outV
        context.write(outK, outV);
    }
}

(3)Reducer类

package com.atguigu.mapreduce.writable;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;

public class FlowReducer extends Reducer<Text, FlowBean, Text, FlowBean> {
    private FlowBean outV = new FlowBean();
    @Override
    protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {

        long totalUp = 0;
        long totalDown = 0;

        //1 遍历values,将其中的上行流量,下行流量分别累加
        for (FlowBean flowBean : values) {
            totalUp += flowBean.getUpFlow();
            totalDown += flowBean.getDownFlow();
        }

        //2 封装outKV
        outV.setUpFlow(totalUp);
        outV.setDownFlow(totalDown);
        outV.setSumFlow();

        //3 写出outK outV
        context.write(key,outV);
    }
}

(4)Driver类

package com.atguigu.mapreduce.writable;

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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;

public class FlowDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

        job.setJarByClass(FlowDriver.class);
        job.setMapperClass(FlowMapper.class);
        job.setReducerClass(FlowReducer.class);
        
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(FlowBean.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);
        
        FileInputFormat.setInputPaths(job, new Path("D:\\inputflow"));
        FileOutputFormat.setOutputPath(job, new Path("D:\\flowoutput"));
        boolean b = job.waitForCompletion(true);
        System.exit(b ? 0 : 1);
    }
}

3 MapReduce框架原理

其中(7)-(16)为shuffle阶段

1MapTask收集我们的map()方法输出的kv对,放到内存缓冲区中

2)从内存缓冲区不断溢出本地磁盘文件,可能会溢出多个文件

3)多个溢出文件会被合并成大的溢出文件

4)在溢出过程及合并的过程中,都要调用Partitioner进行分区和针对key进行排序

5ReduceTask根据自己的分区号,去各个MapTask机器上取相应的结果分区数据

6ReduceTask会抓取到同一个分区的来自不同MapTask的结果文件,ReduceTask会将这些文件再进行合并(归并排序)

7)合并成大文件后,Shuffle的过程也就结束了,后面进入ReduceTask的逻辑运算过程(从文件中取出一个一个的键值对Group,调用用户自定义的reduce()方法)

注意:

1Shuffle中的缓冲区大小会影响到MapReduce程序的执行效率,原则上说,缓冲区越大,磁盘io的次数越少,执行速度就越快。

2)缓冲区的大小可以通过参数调整,参数:mapreduce.task.io.sort.mb默认100M

3.1 InputFormat数据输入

3.1.1 MapTask并行度决定机制

数据块:BlockHDFS物理上把数据分成一块一块。数据块是HDFS存储数据单位

数据切片:数据切片只是在逻辑上对输入进行分片,并不会在磁盘上将其切分成片进行存储。数据切片是MapReduce程序计算输入数据的单位,一个切片会对应启动一个MapTask

3.1.2 FileInputFormat切片流程

3.1.3 FileInputFormat切片机制

3.2 OutputFormat数据输出

自定义OutputFormat:过滤输入的log日志,包含atguigu的网站输出到e:/atguigu.log,不包含atguigu的网站输出到e:/other.log。数据准备参照p106尚硅谷大数据Hadoop教程(Hadoop 3.x安装搭建到集群调优)_哔哩哔哩_bilibili

编写程序

(1)LogMapper类

package com.atguigu.mapreduce.outputformat;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

public class LogMapper extends Mapper<LongWritable, Text,Text, NullWritable> {
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //不做任何处理,直接写出一行log数据
        context.write(value,NullWritable.get());
    }
}

(2) LogReducer类

package com.atguigu.mapreduce.outputformat;

import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

public class LogReducer extends Reducer<Text, NullWritable,Text, NullWritable> {
    @Override
    protected void reduce(Text key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
        // 防止有相同的数据,迭代写出
        for (NullWritable value : values) {
            context.write(key,NullWritable.get());
        }
    }
}

(3)LogDriver类

package com.atguigu.mapreduce.outputformat;

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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

public class LogDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

        job.setJarByClass(LogDriver.class);
        job.setMapperClass(LogMapper.class);
        job.setReducerClass(LogReducer.class);

        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(NullWritable.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(NullWritable.class);

        //设置自定义的outputformat
        job.setOutputFormatClass(LogOutputFormat.class);

        FileInputFormat.setInputPaths(job, new Path("D:\\input"));
        //虽然我们自定义了outputformat,但是因为我们的outputformat继承自fileoutputformat
        //而fileoutputformat要输出一个_SUCCESS文件,所以在这还得指定一个输出目录
        FileOutputFormat.setOutputPath(job, new Path("D:\\logoutput"));

        boolean b = job.waitForCompletion(true);
        System.exit(b ? 0 : 1);
    }
}

(4)LogOutputFormat类,调用LogRecordWriter

package com.atguigu.mapreduce.outputformat;

import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.RecordWriter;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

public class LogOutputFormat extends FileOutputFormat<Text, NullWritable> {
    @Override
    public RecordWriter<Text, NullWritable> getRecordWriter(TaskAttemptContext job) throws IOException, InterruptedException {
        //创建一个自定义的RecordWriter返回
        LogRecordWriter logRecordWriter = new LogRecordWriter(job);
        return logRecordWriter;
    }
}

(5)LogRecordWriter类,继承RecordWriter

package com.atguigu.mapreduce.outputformat;

import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.RecordWriter;
import org.apache.hadoop.mapreduce.TaskAttemptContext;

import java.io.IOException;

public class LogRecordWriter extends RecordWriter<Text, NullWritable> {

    private FSDataOutputStream atguiguOut;
    private FSDataOutputStream otherOut;

    public LogRecordWriter(TaskAttemptContext job) {
        try {
            //获取文件系统对象
            FileSystem fs = FileSystem.get(job.getConfiguration());
            //用文件系统对象创建两个输出流对应不同的目录
            atguiguOut = fs.create(new Path("d:/hadoop/atguigu.log"));
            otherOut = fs.create(new Path("d:/hadoop/other.log"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void write(Text key, NullWritable value) throws IOException, InterruptedException {
        String log = key.toString();
        //根据一行的log数据是否包含atguigu,判断两条输出流输出的内容
        if (log.contains("atguigu")) {
            atguiguOut.writeBytes(log + "\n");
        } else {
            otherOut.writeBytes(log + "\n");
        }
    }

    @Override
    public void close(TaskAttemptContext context) throws IOException, InterruptedException {
        //关流
        IOUtils.closeStream(atguiguOut);
        IOUtils.closeStream(otherOut);
    }
}

3.3 Shuffle机制

3.3.1自定义Partition分区

默认分区通过key的hashCode对ReduceTask个数取模得到。

 将统计结果按照手机归属地不同省份输出到不同文件中(分区),手机号136、137、138、139开头都分别放到一个独立的4个文件中,其他开头的放到一个文件中。数据参照p97尚硅谷大数据Hadoop教程(Hadoop 3.x安装搭建到集群调优)_哔哩哔哩_bilibili

 

 本案例基于2.2.3,增加一个分区类

package com.atguigu.mapreduce.partitioner;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;

public class ProvincePartitioner extends Partitioner<Text, FlowBean> {

    @Override
    public int getPartition(Text text, FlowBean flowBean, int numPartitions) {
        //获取手机号前三位prePhone
        String phone = text.toString();
        String prePhone = phone.substring(0, 3);

        //定义一个分区号变量partition,根据prePhone设置分区号
        int partition;

        if("136".equals(prePhone)){
            partition = 0;
        }else if("137".equals(prePhone)){
            partition = 1;
        }else if("138".equals(prePhone)){
            partition = 2;
        }else if("139".equals(prePhone)){
            partition = 3;
        }else {
            partition = 4;
        }

        //最后返回分区号partition
        return partition;
    }
}

调整驱动类

package com.atguigu.mapreduce.partitioner;
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;

public class FlowDriver {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

        job.setJarByClass(FlowDriver.class);
        job.setMapperClass(FlowMapper.class);
        job.setReducerClass(FlowReducer.class);

        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(FlowBean.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);

        //指定自定义分区器
        job.setPartitionerClass(ProvincePartitioner.class);

        //同时指定相应数量的ReduceTask
        job.setNumReduceTasks(5);

        //设置输入输出路径
        FileInputFormat.setInputPaths(job, new Path("D:\\inputflow"));
        FileOutputFormat.setOutputPath(job, new Path("D\\partitionout"));
        boolean b = job.waitForCompletion(true);
        System.exit(b ? 0 : 1);
    }
}

3.3.4 WritableComparable排序

MapTaskhe ReduceTask均会对数据按照Key排序,默认按照字典顺序,通过快排实现。对于MapTask,他将处理结果暂时放入环形缓冲区,当环形缓冲区使用率达到阈值时,缓冲区内的数据会进行一次快排,并将排序完成的数据写入词牌。数据处理完成后对磁盘上的所有问题进行归并排序。对于ReduceTask,所有数据拷贝完毕后会统一对内存和磁盘上的所有数据进行一次归并排序。相同key的数据要发往同一个ReduceTask,在MapTask结束后排序可以提高shuffle阶段效率。

bean对象做为key传输,需要实现WritableComparable接口重写compareTo方法,就可以实现排序。

该案例基于2.2.3产生的结果再次对总流量进行倒序排序。

数据参照p99尚硅谷大数据Hadoop教程(Hadoop 3.x安装搭建到集群调优)_哔哩哔哩_bilibili

(1)FlowBean对象在在原需求基础上增加了比较功能

package com.atguigu.mapreduce.writablecompable;

import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

public class FlowBean implements WritableComparable<FlowBean> {

    private long upFlow; //上行流量
    private long downFlow; //下行流量
    private long sumFlow; //总流量

    //提供无参构造
    public FlowBean() {
    }

    //生成三个属性的getter和setter方法
    public long getUpFlow() {
        return upFlow;
    }

    public void setUpFlow(long upFlow) {
        this.upFlow = upFlow;
    }
    public long getDownFlow() {
        return downFlow;
    }

    public void setDownFlow(long downFlow) {
        this.downFlow = downFlow;
    }

    public long getSumFlow() {
        return sumFlow;
    }

    public void setSumFlow(long sumFlow) {
        this.sumFlow = sumFlow;
    }

    public void setSumFlow() {
        this.sumFlow = this.upFlow + this.downFlow;
    }

    //实现序列化和反序列化方法,注意顺序一定要一致
    @Override
    public void write(DataOutput out) throws IOException {
        out.writeLong(this.upFlow);
        out.writeLong(this.downFlow);
        out.writeLong(this.sumFlow);

    }

    @Override
    public void readFields(DataInput in) throws IOException {
        this.upFlow = in.readLong();
        this.downFlow = in.readLong();
        this.sumFlow = in.readLong();
    }

    //重写ToString,最后要输出FlowBean
    @Override
    public String toString() {
        return upFlow + "\t" + downFlow + "\t" + sumFlow;
    }

    @Override
    public int compareTo(FlowBean o) {

        //按照总流量比较,倒序排列
        if(this.sumFlow > o.sumFlow){
            return -1;
        }else if(this.sumFlow < o.sumFlow){
            return 1;
        }else {
            return 0;
        }
    }
}

(2)Mapper类

package com.atguigu.mapreduce.writablecompable;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;

public class FlowMapper extends Mapper<LongWritable, Text, FlowBean, Text> {
    private FlowBean outK = new FlowBean();
    private Text outV = new Text();

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

        //1 获取一行数据
        String line = value.toString();

        //2 按照"\t",切割数据
        String[] split = line.split("\t");

        //3 封装outK outV
        outK.setUpFlow(Long.parseLong(split[1]));
        outK.setDownFlow(Long.parseLong(split[2]));
        outK.setSumFlow();
        outV.set(split[0]);

        //4 写出outK outV
        context.write(outK,outV);
    }
}

(3)Reducer类

package com.atguigu.mapreduce.writablecompable;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;

public class FlowReducer extends Reducer<FlowBean, Text, Text, FlowBean> {
    @Override
    protected void reduce(FlowBean key, Iterable<Text> values, Context context) throws IOException, InterruptedException {

        //遍历values集合,循环写出,避免总流量相同的情况
        for (Text value : values) {
            //调换KV位置,反向写出
            context.write(value,key);
        }
    }
}

(4)Driver类

package com.atguigu.mapreduce.writablecompable;

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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;

public class FlowDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        //1 获取job对象
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

        //2 关联本Driver类
        job.setJarByClass(FlowDriver.class);

        //3 关联Mapper和Reducer
        job.setMapperClass(FlowMapper.class);
        job.setReducerClass(FlowReducer.class);

        //4 设置Map端输出数据的KV类型
        job.setMapOutputKeyClass(FlowBean.class);
        job.setMapOutputValueClass(Text.class);

        //5 设置程序最终输出的KV类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);

        //6 设置输入输出路径
        FileInputFormat.setInputPaths(job, new Path("D:\\inputflow2"));
        FileOutputFormat.setOutputPath(job, new Path("D:\\comparout"));

        //7 提交Job
        boolean b = job.waitForCompletion(true);
        System.exit(b ? 0 : 1);
    }
}

3.3.5 Combiner合并

 自定义Combiner实现步骤

1)自定义一个Combiner继承Reducer,重写Reduce方法

public class WordCountCombiner extends Reducer<Text, IntWritable, Text, IntWritable> {

    private IntWritable outV = new IntWritable();

    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {

        int sum = 0;
        for (IntWritable value : values) {
            sum += value.get();
        }
     
        outV.set(sum);
     
        context.write(key,outV);
    }
}

2)在Job驱动类中设置:

job.setCombinerClass(WordCountCombiner.class);

实现自定义Combiner合并

统计过程中对每一个MapTask的输出进行局部汇总,以减小网络传输量即采用Combiner功能。期望:Combine输入数据多,输出时经过合并,输出数据降低。数据参照p104尚硅谷大数据Hadoop教程(Hadoop 3.x安装搭建到集群调优)_哔哩哔哩_bilibili

方案一:增加一个WordCountCombiner类继承Reducer

package com.atguigu.mapreduce.combiner;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;

public class WordCountCombiner extends Reducer<Text, IntWritable, Text, IntWritable> {

private IntWritable outV = new IntWritable();

    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {

        int sum = 0;
        for (IntWritable value : values) {
            sum += value.get();
        }

        //封装outKV
        outV.set(sum);
        //写出outKV
        context.write(key,outV);
    }
}

在WordcountDriver驱动类中指定Combiner

// 指定需要使用combiner,以及用哪个类作为combiner的逻辑
job.setCombinerClass(WordCountCombiner.class);

方案二:将WordcountReducer作为Combiner在WordcountDriver驱动类中指定

// 指定需要使用Combiner,以及用哪个类作为Combiner的逻辑
job.setCombinerClass(WordCountReducer.class);

3.4 Join应用

Join主要分为Map Join和Reduce Join两种。其中Reduce Join在Reduce阶段完成合并操作,Reduce端处理压力大,容易产生数据倾斜。Map Join可以将一张表提前加载至内存,适用于一张表十分小、一张表很大的场景。

3.5 ETL数据清洗 

Extract-Transform-Load,清理的过程往往只需要运行Mapper程序,不需要运行Reduce程序。

3.5.1 日志清洗案例:

1)需求:去除日志中字段个数小于等于11的日志。

数据参照p121

尚硅谷大数据Hadoop教程(Hadoop 3.x安装搭建到集群调优)_哔哩哔哩_bilibili

2)实现代码

(1)WebLogMapper类

package com.atguigu.mapreduce.weblog;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class WebLogMapper extends Mapper<LongWritable, Text, Text, NullWritable>{
	
	@Override
	protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
		
		// 1 获取1行数据
		String line = value.toString();
		
		// 2 解析日志
		boolean result = parseLog(line,context);
		
		// 3 日志不合法退出
		if (!result) {
			return;
		}
		
		// 4 日志合法就直接写出
		context.write(value, NullWritable.get());
	}

	// 2 封装解析日志的方法
	private boolean parseLog(String line, Context context) {

		// 1 截取
		String[] fields = line.split(" ");
		
		// 2 日志长度大于11的为合法
		if (fields.length > 11) {
			return true;
		}else {
			return false;
		}
	}
}

(2)WebLogDriver类

package com.atguigu.mapreduce.weblog;
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WebLogDriver {
	public static void main(String[] args) throws Exception {

// 输入输出路径需要根据自己电脑上实际的输入输出路径设置
        args = new String[] { "D:/input/inputlog", "D:/output1" };

		// 1 获取job信息
		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf);

		// 2 加载jar包
		job.setJarByClass(LogDriver.class);

		// 3 关联map
		job.setMapperClass(WebLogMapper.class);

		// 4 设置最终输出类型
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(NullWritable.class);

		// 设置reducetask个数为0
		job.setNumReduceTasks(0);

		// 5 设置输入和输出路径
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		FileOutputFormat.setOutputPath(job, new Path(args[1]));

		// 6 提交
         boolean b = job.waitForCompletion(true);
         System.exit(b ? 0 : 1);
	}
}

3.6 MapReduce详解

3.6.1 MapTask

1Read阶段:MapTask通过InputFormat获得的RecordReader,从输入InputSplit中解析出一个个key/value,其中的key默认为偏移量。

2Map阶段:该节点主要是将解析出的key/value交给用户编写map()函数处理,并产生一系列新的key/value

3Collect收集阶段:数据处理完成后,一般会调用OutputCollector.collect()输出结果。在该函数内部,它会将生成的key/value分区(调用Partitioner),并写入一个环形内存缓冲区中

4Spill阶段:溢写,当环形缓冲区满后,MapReduce会将数据写到本地磁盘上,生成一个临时文件。需要注意的是,将数据写入本地磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行合并、压缩等操作。

溢写阶段详情:

        步骤1:利用快速排序算法对缓存区内的数据进行排序,排序方式是,先按照分区编号Partition进行排序,然后按照key进行排序。这样,经过排序后,数据以分区为单位聚集在一起,且同一分区内所有数据按照key有序。

        步骤2:按照分区编号由小到大依次将每个分区中的数据写入任务工作目录下的临时文件output/spillN.outN表示当前溢写次数)中。如果用户设置了Combiner,则写入文件之前,对每个分区中的数据进行一次聚集操作。

        步骤3:将分区数据的元信息写到内存索引数据结构SpillRecord中,其中每个分区的元信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小。如果当前内存索引大小超过1MB,则将内存索引写到文件output/spillN.out.index中。

5Merge阶段:所有数据处理完成后,MapTask对所有临时文件进行一次合并,以确保最终只会生成一个数据文件。

        当所有数据处理完后,MapTask会将所有临时文件合并成一个大文件,并保存到文件output/file.out中,同时生成相应的索引文件output/file.out.index

        在进行文件合并过程中,MapTask以分区为单位进行合并。对于某个分区,它将采用多轮递归合并的方式。每轮合并mapreduce.task.io.sort.factor(默认10)个文件,并将产生的文件重新加入待合并列表中,对文件排序后,重复以上过程,直到最终得到一个大文件。

        让每个MapTask最终只生成一个数据文件,可避免同时打开大量文件和同时读取大量小文件产生的随机读取带来的开销。

3.6.2 ReduceTask

(1)Copy阶段:ReduceTask从各个MapTask上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。

(2)Sort阶段:在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。按照MapReduce语义,用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起,Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此,ReduceTask只需对所有数据进行一次归并排序即可

(3)Reduce阶段:reduce()函数将计算结果写到HDFS上

4 Hadoop数据压缩

4.1 概述

1)压缩的优缺点

优点:以减少磁盘IO、减少磁盘存储空间。

缺点:增加CPU开销。

2)压缩原则

运算密集型的Job少用压缩。IO密集型的Job多用压缩

4.2 压缩算法对比

压缩格式

Hadoop自带?

算法

文件扩展名

是否可切片

换成压缩格式后,原来的程序是否需要修改

DEFLATE

是,直接使用

DEFLATE

.deflate

和文本处理一样,不需要修改

Gzip

是,直接使用

DEFLATE

.gz

和文本处理一样,不需要修改

bzip2

是,直接使用

bzip2

.bz2

和文本处理一样,不需要修改

LZO

否,需要安装

LZO

.lzo

需要建索引,还需要指定输入格式

Snappy

是,直接使用

Snappy

.snappy

和文本处理一样,不需要修改

4.3 压缩参数配置

参数

默认值

阶段

io.compression.codecs  

(在core-site.xml中配置)

无,这个需要在命令行输入hadoop checknative查看

输入压缩

mapreduce.map.output.compress(在mapred-site.xml中配置)

false

mapper输出

mapreduce.map.output.compress.codec(在mapred-site.xml中配置)

org.apache.hadoop.io.compress.DefaultCodec

mapper输出

mapreduce.output.fileoutputformat.compress(在mapred-site.xml中配置)

false

reducer输出

mapreduce.output.fileoutputformat.compress.codec(在mapred-site.xml中配置)

org.apache.hadoop.io.compress.DefaultCodec

reducer输出

4.4 压缩案例

(1)Map输出端采用压缩

修改Driver类,加入

        // 开启map端输出压缩

    conf.setBoolean("mapreduce.map.output.compress", true);

        // 设置map端输出压缩方式

    conf.setClass("mapreduce.map.output.compress.codec", BZip2Codec.class,CompressionCodec.class);

package com.atguigu.mapreduce.compress;
import java.io.IOException;
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.io.compress.BZip2Codec;	
import org.apache.hadoop.io.compress.CompressionCodec;
import org.apache.hadoop.io.compress.GzipCodec;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WordCountDriver {

	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

		Configuration conf = new Configuration();

		// 开启map端输出压缩
		conf.setBoolean("mapreduce.map.output.compress", true);

		// 设置map端输出压缩方式
		conf.setClass("mapreduce.map.output.compress.codec", BZip2Codec.class,CompressionCodec.class);

		Job job = Job.getInstance(conf);

		job.setJarByClass(WordCountDriver.class);

		job.setMapperClass(WordCountMapper.class);
		job.setReducerClass(WordCountReducer.class);

		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(IntWritable.class);

		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);

		FileInputFormat.setInputPaths(job, new Path(args[0]));
		FileOutputFormat.setOutputPath(job, new Path(args[1]));

		boolean result = job.waitForCompletion(true);

		System.exit(result ? 0 : 1);
	}
}

(2)Reduce输出端采用压缩

修改Driver类,加入

// 设置reduce端输出压缩开启

      FileOutputFormat.setCompressOutput(job, true);

// 设置压缩的方式

        FileOutputFormat.setOutputCompressorClass(job, BZip2Codec.class);

package com.atguigu.mapreduce.compress;
import java.io.IOException;
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.io.compress.BZip2Codec;
import org.apache.hadoop.io.compress.DefaultCodec;
import org.apache.hadoop.io.compress.GzipCodec;
import org.apache.hadoop.io.compress.Lz4Codec;
import org.apache.hadoop.io.compress.SnappyCodec;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WordCountDriver {

	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
		
		Configuration conf = new Configuration();
		
		Job job = Job.getInstance(conf);
		
		job.setJarByClass(WordCountDriver.class);
		
		job.setMapperClass(WordCountMapper.class);
		job.setReducerClass(WordCountReducer.class);
		
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(IntWritable.class);
		
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);
		
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		FileOutputFormat.setOutputPath(job, new Path(args[1]));
		
		// 设置reduce端输出压缩开启
		FileOutputFormat.setCompressOutput(job, true);
		// 设置压缩的方式
	    FileOutputFormat.setOutputCompressorClass(job, BZip2Codec.class); 
//	    FileOutputFormat.setOutputCompressorClass(job, GzipCodec.class); 
//	    FileOutputFormat.setOutputCompressorClass(job, DefaultCodec.class); 
	    
		boolean result = job.waitForCompletion(true);
		
		System.exit(result?0:1);
	}
}

  • 9
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
⼤数据测试 ⼤数据测试 什么是⼤数据 什么是⼤数据 ⼤数据是指⽆法在⼀定时间范围内⽤传统的计算机技术进⾏处理的海量数据集。 对于⼤数据的测试则需要不同的⼯具、技术、框架来进⾏处理。 ⼤数据的体量⼤、多样化和⾼速处理所涉及的数据⽣成、存储、检索和分析使得⼤数据⼯程师需要掌握极其⾼的技术功底。 需要你学习掌握更多的⼤数据技术、Hadoop、Mapreduce等等技术。 ⼤数据测试策略 ⼤数据测试策略 ⼤数据应⽤程序的测试更多的是去验证其数据处理⽽不是验证其单⼀的功能特⾊。 当然在⼤数据测试时,功能测试和性能测试是同样很关键的。 对于⼤数据测试⼯程师⽽⾔,如何⾼效正确的验证经过⼤数据⼯具/框架成功处理过的⾄少百万兆字节的数据将会是⼀个巨⼤的挑战。 因为⼤数据⾼效的处理测试速度,它要求测软件⼯程师具备⾼⽔平的测试技术才能应对⼤数据测试。 我们来看下⼤数据处理的三个特性: ⼤批量 实时性 可交互 另外,数据质量也同样是⼤数据测试的⼀个重要维度。 因此在进⾏应⽤程序测试之前,必须确保数据质量,并且考虑把数据质量作为数据库测试的⼀部分。涉及数据的各种特性的检验,例如⼀致性、准确性、重复 性、连贯性、有效性及完整性等等。 ⼤数据应⽤测试步骤 ⼤数据应⽤测试步骤 下⾯我们⼀起看看⼤数据应⽤的测试过程是怎么样的。 整体⽽⾔,⼤数据测试⼤体可以分为三⼤步骤: 步骤⼀,数据预处理验证 在进⾏⼤数据测试时,⾸先要预hadoop前验证数据的准确性等等。 1. 我们数据来源可能是关系数据库、⽇志系统、社交⽹络等等,所以我们应该确保数据能正确的加载到系统中 2. 我们要验证加载的数据和源数据是⼀致的 3. 我们要确保正确的提取和加载数据⾄hdfs中 步骤⼆,Map Reduce验证 在进⾏⼤数据测试时,第⼆个关键步骤是"Map Reduce"验证。在本阶段,我们主要验证每⼀个处理节点的业务逻辑是否正确, 并验证在多个运⾏后,确保: 1. Map Reduce过程⼯作正常 2. 数据聚合、分离规则已经实现 3. 数据key-value关系已正确⽣成 4. 验证经过map reduce后数据的准确性等特性 步骤三,结果验证 在本阶段主要验证在经过⼤数据⼯具/框架处理后,⽣成的最终数据的成果。 主要验证: 1. 验证数据转换规则是否正确应⽤ 2. 验证数据的完整性和是否成功持久化到⽬标系统 3. 验证⽆数据损坏 架构测试 架构测试 Hadoop处理海量数据是⾮常的消耗资源的,良好的架构是确保⼤数据项⽬成功的基础。糟糕的涉及会导致性能急剧的下降,进⽽使得系统⽆法满⾜我们的需 要,因此我们需要,或是说⾄少在Hadoop环境下进⾏性能测试、故障恢复测试,以应改进效率和应对可能的最糟糕的情况。 性能测试是⼀个复杂的⼯作,它贯穿整个测试周期,需要关注内存、CPU、⽹络等等指标。 故障恢复测试则是验证数据处理过程中可能出现的故障,为做好意外的恢复做好相应的应对措施。 性能测试 性能测试 ⼤数据性能测试主要包含以下⼏个部分: 数据提取、存储效率 在本阶段,我们主要验证⼤数据应⽤从源数据中提取、加载数据的效率。 ⼀是验证单位时间内数据的提取、加 载效率。 ⼆是验证数据持久化⾄mongodb等库的效率等等 数据处理 在本阶段,我们验证map reduce任务的执⾏效率,重点关注的是数据处理的效率。当然这个过程可能也会涉及到数据的持久化相关指标,例如存储⾄HDFS读 写效率等等,同样也会涉及在内存中处理效率,即我们的处理算法效率等等 ⼦组件性能 ⼤数据处理,⼀般都会需要综合利⽤各种组件来辅助处理,所以我们也是需要关注这些辅助组件的性能 性能测试策略 性能测试策略 ⼤数据应⽤性能测试涉及海量的结构化和⾮结构化的数据,与我们平时所⾯对的业务系统有所不同,所以我们需要针对⼤数据应⽤制定特定的测试策略,以应对 海量的数据。 根据上图性能测试执⾏过程⼀般是这样的: 1. 在性能测试前需要先初始化⼤数据集群环境 2. 梳理和设计⼤数据性能测试场景 3. 准备⼤数据性能测试脚本 4. 执⾏并分析测试结果(如果指标异常,则调优相应的组件并重新测试) 5. 优化配置 性能测试基础准备 性能测试基础准备 在⼤数据性能测试时,需要准备相关的基础⼯作,如下: 数据准备,我们需要在不同的节点准备什么量级数据? ⽇志预估,在测试过程中,可能会⽣成多⼤的⽇志,⽇志的可能增量是什么样的? 并发,在测试时,可能会有多少线程并发读和写? 超时设置,应对设置怎样的连接超时?查询超时?写超时等等? JVM参数,如何设置最优的jvm参数,heap size、GC机制等等 Map Reduce,我们应该选择什么样的sort、merge等算法? 消息队列,消息队列长度会怎么样?等等 必备的测试环境 必备的测试环境 ⼤数据测试不同
⼤数据概述 ⼤数据概述 什么是⼤数据? ⼤数据(big data)是指⽆法在⼀定时间范围内⽤常规软件⼯具进⾏捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策⼒、洞 察发现⼒和流程优化能⼒的海量、⾼增长率和多样化的信息资产。 ------百度百科 ⼀,⼤数据时代: 1)第⼀次信息化浪潮使计算机开始普及,第⼆次信息化浪潮⼈类全⾯进⼊互联⽹时代,第三次信息化浪潮解决了信息爆炸问题,⼤数据时代来。 2)存储设备容量不断增加,cpu处理能⼒⼤幅度提升,⽹络带宽不断增加为⼤数据时代提供技术⽀持。 3)数据产⽣⽅式从1.运营式阶段>2.⽤户原创内容阶段>3.感知式阶段 的变⾰促成了⼤数据时代的到来。 4)⼤数据发展阶段:   1.萌芽阶段:20世纪90年代⾄21世纪初,随着数据挖掘理论和数据库技术的逐步成熟,⼀批商业智能⼯具和知识管理技术开始被应⽤,如数据 仓库、专家系统、知识管理系统等   2.成熟阶段:21世纪前10年,web2.0应⽤发展,⾮结构化数据⼤量产⽣,传统处理⽅法难以应对,带动了⼤数据技术的快速突破,⼤数据解 决⽅案逐渐⾛向成熟,形成了并⾏计算与分布式系统两⼤核⼼技术,⾕歌的GFS和MapReduce等⼤数据技术受到追捧,Hadoop平台开始⼤⾏其 道。   3.⼤规模应⽤期:2010年以后,⼤数据应⽤渗透各⾏业,数据驱动决策,信息社会智能化程度⼤幅度提⾼ ⼆,⼤数据概念:"4个V" 1)数据量⼤(volume) 2)数据类型繁多(variety) 3)处理速度快(velocity) 4)价值密度低(value) 三,⼤数据影响: 1)⼤数据对科学研究的影响:   1.实验科学>2.理论科学>3.计算科学>4.数据密集型科学 2)⼤数据对思维⽅式的影响:   1.全样⽽⾮抽样   2.效率⽽⾮精确   3.相关⽽⾮因果 3)⼤数据对社会发展的影响:   1.⼤数据决策成为⼀种新的决策⽅式   2.⼤数据应⽤促进信息技术与各⾏业的深度融合   3.⼤数据开发推动新技术和新应⽤的不断涌现 四,⼤数据的应⽤:   ⼤数据⽆处不在,包括⾦融、汽车、餐饮、电信、能源、体育和娱乐等在内的社会各⾏各业都已经融⼊了⼤数据的印记 五,⼤数据关键技术: 技术层 ⾯ 功能 数据采 集与预 处理 利⽤ETL⼯具将分布的、异构数据源中的数据,如关系数据,平⾯数据⽂件等,抽取到临时中间层后进⾏清洗、转换、集成,最后加载 到数据仓库或数据集市中,成为联机分析处理、数据挖掘的基础;也可以利⽤⽇志采集⼯具(如Flume、kafka等)把实时采集的数据作 为流计算系统的输⼊,进⾏实时处理分析  数据存 储和管 理 利⽤分布式⽂件系统、数据仓库、关系数据库、nosql数据库、云数据库等,实现对结构化、半结构化和⾮结构化和⾮结构化海量数据的 存储和管理 数据处 理与分 利⽤分布式并⾏编程模型和计算框架,结合机器学习和数据挖掘算法,实现对海量数据的处理和分析;对分析结果进⾏可视化呈现,帮 助⼈们更好地理解数据、分析数据 析 数据安 全和隐 私保护 在从⼤数据中挖掘潜在的巨⼤商业价值和学术价值的同时,构建隐私数据保护体系和数据安全体系,有效保护个⼈隐私和数据安全 六,⼤数据计算模式: ⼤数据计算 模式 解决问题 代表产品 批处理计算 针对⼤规模数据的批量处理 MapReduce、Spark等 流计算 针对流数据的实时计算 Storm、S4、Flume、Streams、Puma、DStream、SuperMario、银河流 数据处理平台等 图计算 针对⼤规模图结构数据的处 理 Pregel、GraphX、Giraph、PowerGraph、Hama、GoldenOrb等 查询分析计 算 ⼤规模数据的存储管理和查 询分析 Dremel、Hive、Cassandra、Impala等 七,⼤数据产业:   ⼤数据产业是指⼀切与⽀撑⼤数据组织管理和价值发现相关的企业经济活动的集合。   ⼤数据产业包括IT基础设施层、数据源层、数据管理层、数据分析层、数据平台层和数据应⽤层。 ⼋,⼤数据与云计算、物联⽹: ⼀)云计算: 1)云计算概念:云计算实现了通过⽹络提供可伸缩的、廉价的分布式计算能⼒,⽤户只需要在具备⽹络接⼊条件的地⽅,就可以随时随地地获得 所需的各种IT资源 2)元计算的关键技术:虚拟化、分布式存储、分布式计算、多租户等。 3)云计算数据中⼼:是⼀整套复杂的设施,包括⼑⽚服务器、宽带⽹络连接、环境控制设备、监控设备⼀级耕种安全装置等。数据中⼼是云计算 的重要载体,为云计算提供计算、存储、宽带等各种硬件资源,为各种平台和应⽤提供运⾏⽀撑环境。 4)云计算的应⽤:在电⼦政务、医疗、卫⽣、教育、企业等领域的应⽤不断深化,对提⾼政府服务⽔平促进产业转型升级和培育发展

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值