MapReduce深入操作

本文介绍如何在Hadoop中实现自定义数据类型以处理复杂的业务数据,通过实例演示了自定义RecordWritable类的过程,并展示了如何利用MapReduce对购物统计数据进行省份、用户及商品分类层面的消费分析。
摘要由CSDN通过智能技术生成

一、自定义数据类型
在设计的系统开发过程之中,有可能要参与分析的文件会有很多,并且文件的组成结构也可能会非常的复杂,所以来讲在整个的Hadoop里面可以针对于用户的需求实现自定义类型。 现在假如说有以下一个信息文件,文件的组成格式(购物统计):

用户名[0]、省份[1]、城市[2]、购买日期时间[3]、商品名称[4]、商品分类[5]、商品子分类[6]、商品价格[7]、商品购买价格[8]

希望可以通过一种数据类型能够描述出以下几种关系:
1、保留有省份的花销(商品原始价格、成交价格、折扣的价格);
2、保留有用户的花销(商品原始价格、成交价格、折扣的价格);
3、保留有商品分类的花销(商品原始价格、成交价格、折扣的价格);


在整个的MapReduce之中并不会提供有任何的一种类型来描述这种花销的结构,那么这样的话就需要定义一个自己的数据类型,而所有的数据类型一定要实现一个接口:org.apache.hadoop.io.Writable;

/**
* 实现了自定义的记录数据价格的结构
* @author mldn
*/
public class RecordWritable implements Writable {
    private double price ; // 商品的原始价格
    private double deal ; // 商品的成交价格
    private double discount ; // 商品的折扣价格
    public RecordWritable() {
        // RecordWritable类需要在读取的时候执行数据反序列化操作;
    }
    // RecordWritable类要在数据创建的时候接收内容
    public RecordWritable(double price, double deal, double discount) {
        super();
        this.price = price;
        this.deal = deal;
        this.discount = discount;
    }
    @Override
    public void write(DataOutput output) throws IOException {
        output.writeDouble(this.price);
        output.writeDouble(this.deal);
        output.writeDouble(this.discount);
    }
    @Override
    public void readFields(DataInput input) throws IOException {
        this.price = input.readDouble() ;
        this.deal = input.readDouble() ;
        this.discount = input.readDouble() ;
    }
    @Override
    public String toString() {
        return "RecordWritable [price=" + price + ", deal=" + deal + ", discount=" + discount + "]";
    }
    public double getPrice() {
        return price;
    }
    public double getDeal() {
        return deal;
    }
    public double getDiscount() {
        return discount;
    }
}

最终的结果一定要对数据进行MapReduce 操作统计,所以在整个的统计里面,现在希望可以实现这样的操作统计:
· 可以实现根据省份数据得到的统计结果;
定义Map 数据处理

/**
* 针对于每一行发送的数据进行拆分处理,将每一行的数据拆分为key与value的形式<br>
* 输入类型:关注的是value的内容;<br>
* 输出类型:单词名称=数量(1)<br>
* @author mldn
*/
private static class RecordMapper extends Mapper<Object, Text, Text, RecordWritable> {
    @Override
    protected void map(Object key, Text value, Mapper<Object, Text, Text, RecordWritable>.Context context) throws       IOException, InterruptedException {
        // 读取每一行的数据,Map是根据换行作为读取的分割符;
        String str = value.toString(); // 取的每一行的内容
        // 所有的单词要求按照空格进行拆分
        String result[] = str.split(","); // 按照“,”拆分
        // 以后如果要根据不同的方式来进行统计,则此处可以取得不同的内容
        String province = result[1] ; // 取得省份的信息内容
        double price = Double.parseDouble(result[7]) ; // 取得原始价格
        double deal = Double.parseDouble(result[8]) ; // 成交价格
        RecordWritable rw = new RecordWritable(price, deal, price-deal) ;
        context.write(new Text(province), rw); // 将数据取出
    }
}

定义Reduce 处理类

private static class RecordReducer extends Reducer<Text, RecordWritable, Text, RecordWritable> {
    @Override
    protected void reduce(Text key, Iterable<RecordWritable> values,Reducer<Text, RecordWritable, Text, RecordWritable>.Context context)
        // 计算出消费的总价格数据
        double priceSum = 0.0 ;
        double dealSum = 0.0 ;
        double discountSum = 0.0 ;
        for (RecordWritable rw : values) {
            priceSum += rw.getPrice() ;
            dealSum += rw.getDeal() ;
            discountSum += rw.getDiscount() ;
        }
        RecordWritable rw = new RecordWritable(priceSum, dealSum, discountSum) ;
        context.write(key, rw);
    }
}

定义相关的作业进行处理

public class Record {
    private static final String OUTPUT_PATH = "hdfs://192.168.122.132:9000/output-" ;
    // 具体的输入的路径由用户自己来输入,而我们来定义一个属于自己的输出路径,格式“output-20201010”
    public static void main(String[] args) throws Exception {
        if (args.length != 1) { // 现在输入的参数个数不足,那么则应该退出程序
        System.out.println("本程序的执行需要两个参数:HDFS输入路径");
        System.exit(1); // 程序退出
        }
        Configuration conf = new Configuration() ; // 此时需要进行HDFS操作
        String paths [] = new GenericOptionsParser(conf,args).getRemainingArgs() ; //将输入的两个路径解析为HDFS的路径
        // 需要定义一个描述作业的操作类
        Job job = Job.getInstance(conf, "hadoop") ;
        job.setJarByClass(Record.class); // 定义本次作业执行的类的名称
        job.setMapperClass(RecordMapper.class); // 定义本次作业完成所需要的Mapper程序类
        job.setMapOutputKeyClass(Text.class); // 定义Map输出数据的Key的类型
        job.setMapOutputValueClass(RecordWritable.class); // 定义Map输出数据的Value的类型
        job.setReducerClass(RecordReducer.class); // 定义要使用的Reducer程序处理类
        job.setOutputKeyClass(Text.class); // 最终输出统计结果的key的类型
        job.setOutputValueClass(RecordWritable.class); // 最终输出统计结果的value的类型
        // 所有的数据都在HDFS上进行操作,那么就必须使用HDFS提供的程序进行数据的输入配置
        FileInputFormat.addInputPath(job, new Path(paths[0])); // 定义输入的HDFS路径
        // 输出路径可以自己进行定义
        FileOutputFormat.setOutputPath(job,
        new Path(OUTPUT_PATH + new SimpleDateFormat("yyyyMMdd").format(new
        Date())));// 定义统计结果的输出路径
        System.exit(job.waitForCompletion(true) ? 0 : 1); // 程序执行完毕后要进行退出
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值