头歌实践教学平台:大数据存储2023(五)

五、当HBase遇上MapReduce

第1关:HBase的MapReduce快速入门

任务描述
本关任务:编写一个HBase的MapReduce配置程序。

相关知识
为了完成本关任务,你需要掌握:

快速入门
HBase的MapReduce配置
HBase的Map类 和Reduce类

快速入门
要运行HBase的MapReduce很简单,HBase内部已经自带了MapReduce任务。

首先登录到服务器上,然后确保当前的用户下有HADOOP_HOME和HBASE_HOME 这两个环境变量,并启动Hadoop、HBase。

默认情况下,部署到MapReduce集群的MapReduce作业是不能访问HBase的配置和类的,

要为MapReduce作业提供他们所需的访问权限,推荐的方法是使用 HADOOP_CLASSPATH让HBase添加它的依赖jar ,  HADOOP_CLASSPATH=${HBASE_HOME}/bin/hbase classpath。
我们先来看看hbase-mapreduce-<版本号>.jar包中包含了哪些MapReduce Job:

# HADOOP_CLASSPATH=`${HBASE_HOME}/bin/hbase classpath`   ${HADOOP_HOME}/bin/hadoop jar ${HBASE_HOME}/lib/hbase-mapreduce-2.0.0.jar
An example program must be given as the first argument.
Valid program names are:
  CellCounter: Count cells in HBase table.
  WALPlayer: Replay WAL files.
  completebulkload: Complete a bulk data load.
  copytable: Export a table from local cluster to peer cluster.
  export: Write table data to HDFS.
  exportsnapshot: Export the specific snapshot to a given FileSystem.
  import: Import data written by Export.
  importtsv: Import data in TSV format.
  rowcounter: Count rows in HBase table.
  verifyrep: Compare data from tables in two different clusters. It doesn't work for incrementColumnValues'd cells since timestamp is changed after appending to WAL.
可以看到里面有个叫rowcounter,我们这次就使用它了,rowcounter做的事情很简单,就是统计当前表有多少行,当你调用hbase-mapreduce-<版本号>.jar,并使用rowcounter为第一个参数的时候,就会使用这个MapReduce 的job,第二个参数就是你想统计的目标表,所以这条命令的格式为:

$ HADOOP_CLASSPATH=`${HBASE_HOME}/bin/hbase classpath`  ${HADOOP_HOME}/bin/hadoop jar ${HBASE_HOME}/hbase-mapreduce-<版本号>.jar rowcounter <目标表>
想要统计的表名为CAR_INFOS,所以我们在这个例子中使用的命令为:

HADOOP_CLASSPATH=`${HBASE_HOME}/bin/hbase classpath`   ${HADOOP_HOME}/bin/hadoop jar ${HBASE_HOME}/lib/hbase-mapreduce-2.0.0.jar   rowcounter CAR_INFOS
运行后会输出很多日志信息,在信息的末尾我们会看到这样一句话:

  org.apache.hadoop.hbase.mapreduce.RowCounter$RowCounterMapper$Counters
                ROWS=1004
        File Input Format Counters 
                Bytes Read=0
        File Output Format Counters 
                Bytes Written=0
这个ROWS=1004就是结果,意思是mytable统计出来的行数是1004。

HBase 的 MapReduce 配置
从运行HBase的MapReduce命令可以看出,HBase的MapReduce仍然使用的是Hadoop提供的原生MapReduce,使用hadoop命令来启动jar包。
那HBase的MapReduce和原生的具体有何区别呢?

我们首先来看看HBase的MapReduce的配置信息。

  Configuration conf = HBaseConfiguration.create(getConf());
        String tablename = args[0];
        String targetTable = args[1];
        Job job = Job.getInstance(conf);
        Scan scan = new Scan();
 TableMapReduceUtil.initTableMapperJob(tablename,scan,MyMapper.class, Text.class, IntWritable.class,job);
        TableMapReduceUtil.initTableReducerJob(targetTable,MyReducer.class,job);
  }
可以看出,原本繁琐配置不见了,Map节点使用initTableMapJob 方法进行配置,方法里依次传入输出表、HBase过滤器、mapper类、mapper输出key、mapper输入value、job类。Reducer节点的相关配置,则通过initTableReducerJob方法进行配置,方法依次传入 输出表、reducer类、job类,就此HBase的MapReducer配置就完成了。

HBase 的 Map 类 和 Reduce 类
使用HBase的MapReducer,则不能使用原生的Map类和Reducer类, HBase定义了继承Map类的TableMapper和 继承Reducer类的 TableReducer。如下:

public abst\fract class TableMapper<KEYOUT, VALUEOUT>
extends Mapper<ImmutableBytesWritable, Result, KEYOUT, VALUEOUT> {
}
public abst\fract class TableReducer<KEYIN, VALUEIN, KEYOUT>
extends Reducer<KEYIN, VALUEIN, KEYOUT, Mutation> {
}
对于Mapper类,我们使用TableMapper时,不再需要设定四个泛型类型,只需要确定输出到Reducer的key和Value的类型,而起始偏移量、及文本内容变为HBase特有的rowkey和行Result。

对于Reduce类,我们使用TableReducer时 ,最后写入的Value的类型不再自己设定,而变为HBase特有的Mutation,由Mutation存入最后的数据。

编程要求
请仔细阅读右侧代码,根据方法内的提示,在Begin - End区域内进行代码补充,在run()方法增加MapReduce配置信息。
String[] args数组,第一个参数为输入表, 第二个参数为输出表。

测试说明
补充完代码后,点击测评,平台会对你编写的代码进行测试,当你的结果与预期输出一致时,即为通过。

测试输入:t_comment t_word_count;
预期输出:

word:I
word_info:count 9
word:be
word_info:count 4
word:is
word_info:count 4
word:the
word_info:count 4
word:to
word_info:count 8
word:with
word_info:count 4
word:you
word_info:count 7
开始你的任务吧,祝你成功!

package com.processdata;

import java.io.IOException;
import java.util.List;
import java.util.Scanner;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.apdplat.word.WordSegmenter;
import org.apdplat.word.segmentation.Word;
import com.util.HBaseUtil;
import com.vdurmont.emoji.EmojiParser;

/**
 * 词频统计
 *
 */
public class WorldCountMapReduce extends Configured implements Tool {

    private static class MyMapper extends TableMapper<Text, IntWritable> {
        private static byte[] family = "comment_info".getBytes();
        private static byte[] column = "content".getBytes();

        @Override
        protected void map(ImmutableBytesWritable rowKey, Result result, Context context) {
            try {
                byte[] value = result.getValue(family, column);
                String content = new String(value, "utf-8");
                String[] split = content.split(" ");
                for (String str : split) {
                    Text text = new Text(str);
                    IntWritable v = new IntWritable(1);
                    context.write(text, v);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    private static class MyReducer extends TableReducer<Text, IntWritable, ImmutableBytesWritable> {
        private static byte[] family = "word_info".getBytes();
        private static byte[] column = "count".getBytes();

        @Override
        public void reduce(Text key, Iterable<IntWritable> values, Context context) {

            int sum = 0;
            for (IntWritable value : values) {
                sum += value.get();
            }
            Put put = new Put(Bytes.toBytes(key.toString()));
            put.addColumn(family, column, Bytes.toBytes(sum));
            try {
                context.write(null, put);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    @Override
    public int run(String[] args) throws Exception {
        // 配置Job
        /********** Begin *********/
  
        // 创建Conf对象
        Configuration conf = HBaseConfiguration.create(getConf());
  
        String tablename = args[0]; // 表名
        String targetTable = args[1]; // 目标表
  
        // 获取到Job对象
        Job job = Job.getInstance(conf);

        // 创建Scan对象
        Scan scan = new Scan();
  
        // 通过Hbase工具类提交数据
        TableMapReduceUtil.initTableMapperJob(tablename, scan, MyMapper.class, Text.class, IntWritable.class, job);
        TableMapReduceUtil.initTableReducerJob(targetTable, MyReducer.class, job);
  
        // 开始提交数据
        job.waitForCompletion(true);
        return 0;
        /********** End *********/
    }
}

第2关:HBase的MapReduce使用

任务描述
本关任务:写一个HBase的MapReduce任务。

相关知识
为了完成本关任务,你需要掌握:

编写自己的MapReduce。
编写自己的MapReduce
HBase结合MapReduce可以实现从HBase输入表到HBase输出表、从文件到HBase输出表、从HBase输入表到文件的几种场景。

下面我们来完成一个从HBase输入表到HBase输出表的需求:从HBase表里统计列族comment_info中列名content的字符串单词的出现次数,并把统计结果存到HBase列族为word_info,列名为count的另一个表里。

我们分析一下需求,统计的是字符串中单词的个数,则key应该为Text类型,value 为IntWritable类型,输入表名为args[0]。Map节点配置如下:

 String tablename = args[0];
 String targetTable = args[1];
 Scan scan = new Scan();
 Job job = Job.getInstance(conf);
 TableMapReduceUtil.initTableMapperJob(tablename,scan,MyMapper.class, Text.class, IntWritable.class,job);
Map节点的目的是切分字符串,获取单词名称,把单词名称当做key值,单词数1当成value值,写到context.write(key,value)方法中,Map方法如下:

public  class MyMapper extends TableMapper<Text, IntWritable> {
        private static byte[] family = "comment_info".getBytes();
         private static byte[] column = "content".getBytes();
        
        @Override
        protected void map(ImmutableBytesWritable rowKey, Result result, Context context)
                throws IOException, InterruptedException {
         byte[] value = result.getValue(family, column);
            String content = new String(value,"utf-8");
            String[] split = content.split(",");
             for(String str : split) {
                 Text text = new Text(str);
                 IntWritable v = new IntWritable(1);
                 context.write(text,v);
               }
         }
        }
    }


接下来我们配置及使用 Reduce 方法,输出表名为args[1], Reduce节点配置如下

 TableMapReduceUtil.initTableReducerJob(args[1],MyReducer.class,job);
由于Reduce节点需要把统计结果放到Mutation中,而Mutation是个抽象类,一般都会使用继承它的Put类,进行数据写入,Reduce节点的使用方法如下:

 public  class MyReducer extends TableReducer<Text, IntWritable, ImmutableBytesWritable> {
        private static byte[] family =  "word_info".getBytes();
        private static byte[] column = "count".getBytes();
        
        @Override
        public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {         
              int sum = 0;
                for (IntWritable value : values) {
                    sum += value.get();
                }
                Put put = new Put(Bytes.toBytes(key.toString()));
                put.addColumn(family,column,Bytes.toBytes(sum));
                context.write(null,put);
        }


最后提交任务,并阻塞等待job执行完成:

job.waitForCompletion();
至此你就做完了你的第一个HBase的MapReduce任务啦!

编程要求
根据提示,在右侧Begin - End区域内补充代码,完成一个从文件到HBase输出表的需求:使用MapReduce统计单词个数。
String[] args数组,第一个参数为输入文件, 第二个参数为输出表
输入文件内容如下:

There will be no regret and sorrow if you fight with all your strength
give a stranger one of your smiles. It might be the only sunshine he sees all day
Victory belongs to the most persevering
The world is his who enjoys it
want it more that anything
Love is the greatest refreshment in life
The first step is as good as half over
My heart is with you
Cease to struggle and you cease to live
He knows most who speaks least
Until you make peace with who you are, you’ll never be content with what you have
One needs 3 things to be truly happy living in the world: some thing to do, some one to love, some thing to hope for
I lied when I said I didn’t like you. I lied when I said I didn’t care. I lie every time I try to tell myself I will never fall for you
统计文件内容里出现的单词个数,并把统计结果保存到列族为word_info,单词个数为count的字段,单词名称为rowkey的输出表里。

测试说明
补充完代码后,点击测评,平台会对你编写的代码进行测试,当你的结果与预期输出一致时,即为通过。

测试输入:t_comment t_word_count;
预期输出:

word:I
word_info:count 9
word:be
word_info:count 4
word:is
word_info:count 4
word:the
word_info:count 4
word:to
word_info:count 8
word:with
word_info:count 4
word:you
word_info:count 7
开始你的任务吧,祝你成功!

package com.processdata;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.util.Tool;

/**
 * 词频统计
 *
 */
public class WorldCountMapReduce2 extends Configured implements Tool {

    private static class MyMapper extends Mapper<Object, Text, Text, IntWritable> {

        @Override
        public void map(Object object, Text value, Context context) throws IOException, InterruptedException {
            /********** Begin *********/
            // 根据题意,我们需要根据空格对指定数据进行拆分
            String[] split = value.toString().split(" ");
            // 循环数组,对值进行分类
            for (String str : split) {
                Text text = new Text(str.getBytes());
                IntWritable v = new IntWritable(1);
                context.write(text, v);
            }
            /********** End *********/
        }
    }

    private static class MyReducer extends TableReducer<Text, IntWritable, ImmutableBytesWritable> {
        private static byte[] family = "word_info".getBytes();
        private static byte[] column = "count".getBytes();

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

            /********** Begin *********/
            int sum = 0; // 用于统计

             //  循环Map中分类的值,求和
            for (IntWritable value : values) {
                sum += value.get();
            }

             //  将key和value进行聚和
            Put put = new Put(Bytes.toBytes(key.toString()));
            put.addColumn(family, column, Bytes.toBytes(sum));

             //  通过文件方式将其输出
            context.write(null, put);
            /********** End *********/
        }

    }

    @Override
    public int run(String[] args) throws Exception {
         // 配置Job
        /********** Begin *********/
         // 配置
        Configuration conf = HBaseConfiguration.create(getConf());
        String file = args[0]; //  输入文件
        String targetTable = args[1]; //  输出表
        Job job = Job.getInstance(conf);
         //  Map的Key的输入类型
        job.setMapOutputKeyClass(Text.class);
         //  Map的Value的输入类型
        job.setMapOutputValueClass(IntWritable.class);
         //  需要执行的MapReduce类
        job.setJarByClass(WorldCountMapReduce2.class);
         //  文件输入格式
        FileInputFormat.addInputPath(job, new Path(file));
         //  设置Mapper类
        job.setMapperClass(MyMapper.class);
         //  开始执行任务
        TableMapReduceUtil.initTableReducerJob(targetTable, MyReducer.class, job);
        job.waitForCompletion(true);
        return 0;
        /********** End *********/
    }
}

有任何问题都可以随时关注私信!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值