1. 依赖

  1. <!-- server -->
  2. <dependency>
  3. <groupId>org.apache.hbase</groupId>
  4. <artifactId>hbase-server</artifactId>
  5. <version>1.3.1</version>
  6. </dependency>
  7. <!-- client -->
  8. <dependency>
  9. <groupId>org.apache.hbase</groupId>
  10. <artifactId>hbase-client</artifactId>
  11. <version>1.3.1</version>
  12. </dependency>

2. 基础API

核心对象:DDL操作 HBaseAdmin ,DML操作 HTable

2.1 获取Configuration

  1. public static Configuration configuration;
  2. static {
  3. configuration = HBaseConfiguration.create();
  4. // 设置zk地址
  5. configuration.set("hbase.zookeeper.quorum","hadoop101,hadoop102,hadoop103");
  6. configuration.set("hbase.zookeeper.property.clientPort","2181");
  7. }

2.2 判断表是否存在

  1. /**
  2. * 1. 判断表是否存在
  3. *
  4. * @param tableName 表名
  5. * @return
  6. * @throws IOException
  7. */
  8. public static boolean isTableExist(String tableName) throws IOException {
  9. //在 HBase 中管理、访问表需要先创建 HBaseAdmin 对象
  10. // 方式一:
  11. // Connection connection = ConnectionFactory.createConnection(configuration);
  12. // HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
  13. // 方式二:
  14. HBaseAdmin admin = new HBaseAdmin(configuration);
  15. boolean isExist = admin.tableExists(tableName);
  16. return isExist;
  17. }

2.3 创建表

  1. /**
  2. * 2. 创建表
  3. *
  4. * @param tableName 表名
  5. * @param columnFamily 多个列族
  6. */
  7. public static void createTable(String tableName, String... columnFamily) throws IOException {
  8. HBaseAdmin admin = new HBaseAdmin(configuration);
  9. // 首先判断表是否存在
  10. if (isTableExist(tableName)) {
  11. System.out.println("表" + tableName + "已存在");
  12. System.exit(0);
  13. }else {
  14. // 创建表属性对象,表名需要转字节
  15. HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
  16. // 创建多个列族
  17. for (String cf : columnFamily) {
  18. hTableDescriptor.addFamily(new HColumnDescriptor(cf));
  19. }
  20. // 根据对表的配置,创建表
  21. admin.createTable(hTableDescriptor);
  22. System.out.println("表" + tableName + "创建成功!");
  23. }
  24. }

2.4 删除表

  1. /**
  2. * 3. 删除表
  3. *
  4. * @param tableName 表名
  5. * @throws IOException
  6. */
  7. public static void dropTable(String tableName) throws IOException {
  8. HBaseAdmin admin = new HBaseAdmin(configuration);
  9. if (isTableExist(tableName)) {
  10. // 先disable才能删除掉
  11. admin.disableTable(tableName);
  12. admin.deleteTable(TableName.valueOf(tableName));
  13. System.out.println("表" + tableName + "删除成功!");
  14. } else {
  15. System.out.println("表" + tableName + "不存在!");
  16. }
  17. }

2.5 插入数据

  1. /**
  2. * 4. 插入数据
  3. *
  4. * @param tableName 表名
  5. * @param rowKey rowkey
  6. * @param columnFamily 列族
  7. * @param column 列
  8. * @param value 值
  9. */
  10. public static void addRowData(String tableName, String rowKey, String columnFamily, String column, String value) throws IOException {
  11. // 创建HTable对象
  12. HTable hTable = new HTable(configuration, tableName);
  13. // 通过rowkey构建Put对象
  14. Put put = new Put(Bytes.toBytes(rowKey));
  15. // 向Put对象中组装数据
  16. put.add(Bytes.toBytes(columnFamily), Bytes.toBytes(column), Bytes.toBytes(value));
  17. hTable.put(put);
  18. hTable.close();
  19. System.out.println("插入数据成功");
  20. }

2.6 删除多行数据

  1. /**
  2. * 5. 删除多行数据
  3. *
  4. * @param tableName 表名
  5. * @param rows rowKey数组
  6. * @throws IOException
  7. */
  8. public static void deleteMultiRow(String tableName, String... rows) throws IOException {
  9. HTable hTable = new HTable(configuration, tableName);
  10. ArrayList<Delete> deleteList = new ArrayList<>();
  11. for (String row : rows) {
  12. Delete delete = new Delete(Bytes.toBytes(row));
  13. deleteList.add(delete);
  14. }
  15. hTable.delete(deleteList);
  16. hTable.close();
  17. System.out.println("多行数据删除成功");
  18. }

2.7 查询所有数据

  1. /**
  2. * 6. 获取所有数据
  3. *
  4. * @param tableName 表名
  5. * @throws IOException
  6. */
  7. public static void scanRows(String tableName) throws IOException {
  8. HTable hTable = new HTable(configuration, tableName);
  9. // 获取用于扫表的scan对象
  10. Scan scan = new Scan();
  11. // 使用HTable得到resultScanner实现类的对象,实际是个迭代器
  12. ResultScanner scanner = hTable.getScanner(scan);
  13. for (Result result : scanner) {
  14. // 获取所有的单元格
  15. Cell[] cells = result.rawCells();
  16. for (Cell cell : cells) {
  17. // 获取rowkey
  18. System.out.println("行键rowkey:" + Bytes.toString(CellUtil.cloneRow(cell)));
  19. // 获取列族
  20. System.out.println("列族:" + Bytes.toString(CellUtil.cloneFamily(cell)));
  21. // 获取列
  22. System.out.println("列:" + Bytes.toString(CellUtil.cloneQualifier(cell)));
  23. System.out.println("值:" + Bytes.toString(CellUtil.cloneValue(cell)));
  24. }
  25. }
  26. }

2.8 获取某一行数据

  1. /**
  2. * 7. 获取某一行数据
  3. *
  4. * @param tableName 表名
  5. * @param rowKey rowKey
  6. * @throws IOException
  7. */
  8. public static void getRow(String tableName, String rowKey) throws IOException {
  9. HTable hTbale = new HTable(configuration, tableName);
  10. Get get = new Get(Bytes.toBytes(rowKey));
  11. // 显示所有版本
  12. get.setMaxVersions();
  13. // 显示指定时间戳的版本
  14. // get.setTimeStamp()
  15. Result result = hTbale.get(get);
  16. for (Cell cell : result.rawCells()) {
  17. // 获取rowkey
  18. System.out.println("行键rowkey:" + Bytes.toString(CellUtil.cloneRow(cell)));
  19. // 获取列族
  20. System.out.println("列族:" + Bytes.toString(CellUtil.cloneFamily(cell)));
  21. // 获取列
  22. System.out.println("列:" + Bytes.toString(CellUtil.cloneQualifier(cell)));
  23. System.out.println("值:" + Bytes.toString(CellUtil.cloneValue(cell)));
  24. }
  25. }

2.9 获取某一单元格数据

  1. /**
  2. * 8. 获取某一行指定“列族:列”的数据,即获取某一个单元格的数据
  3. *
  4. * @param tableName 表名
  5. * @param rowKey rowKey
  6. * @param columnFamily 列族
  7. * @param column 列
  8. */
  9. public static void getCell(String tableName, String rowKey, String columnFamily, String column) throws IOException {
  10. HTable hTable = new HTable(configuration, tableName);
  11. Get get = new Get(Bytes.toBytes(rowKey));
  12. // 添加“列族:列”信息
  13. get.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(column));
  14. Result result = hTable.get(get);
  15. for (Cell cell : result.rawCells()) {
  16. // 获取rowkey
  17. System.out.println("行键rowkey:" + Bytes.toString(CellUtil.cloneRow(cell)));
  18. // 获取列族
  19. System.out.println("列族:" + Bytes.toString(CellUtil.cloneFamily(cell)));
  20. // 获取列
  21. System.out.println("列:" + Bytes.toString(CellUtil.cloneQualifier(cell)));
  22. System.out.println("值:" + Bytes.toString(CellUtil.cloneValue(cell)));
  23. }
  24. }

2.10 过滤器

2.10.1 常用过滤器如下

image.png

2.10.2 实例代码

详细参考:七、HBase谷粒微博案例 4.6获取某个用户所有微博详情

  1. Scan scan = new Scan();
  2. // 构建过滤器
  3. // 表示过滤出 RowKey 中有uid_"-"的子串的数据
  4. RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator(uid + "_"));
  5. scan.setFilter(rowFilter);
  6. // 4. 获取数据
  7. ResultScanner resultScanner = table.getScanner(scan);

3. 整合MapReduce

3.1 环境准备

3.1.1 查看HBase的官方MapReduce任务的执行需要哪些jar

  1. # maredcp --- MapReduce的jar包classpath路径
  2. $ bin/hbase mapredcp

3.1.2 环境变量的导入

3.1.2.1 临时生效(执行环境变量导入)

$ export HBASE_HOME=/opt/module/hbase
$ export HADOOP_HOME=/opt/module/hadoop-2.7.2
$ export HADOOP_CLASSPATH=`${HBASE_HOME}/bin/hbase mapredcp`

3.1.2.2 永久生效

  • 先在/etc/profile配置

    export HBASE_HOME=/opt/module/hbase
    export PATH=$PATH:$HBASE_HOME/bin
    export HADOOP_HOME=/opt/module/hadoop-2.7.2
    
  • 然后在 hadoop-env.sh 中增加如下配置(注意:在 for 循环之后配,目的是导入hadoop中增加对hbase的支持的jar包)

    export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:/opt/module/hbase/lib/*
    
  • 分发 /etc/profilehadoop-env.sh

    3.1.3 重启hbase、hadoop(关闭顺序、启动倒序)

    3.2 官方案例

    3.2.1 案例一

    统计 stu 表中有多少行数据

# 注意:在/opt/module/hbase下执行,否则找不到lib/hbase-server-1.3.1.jar
$ /opt/module/hadoop-2.7.2/bin/yarn jar lib/hbase-server-1.3.1.jar rowcounter stu

3.2.2 案例二

使用 MapReduce 将本地CSV数据导入到 HBase

  • TSV数据如下

    **.tsv** 文件是以 **\t** 进行分割的文本文件 **.csv** 文件是以 **,** 进行分割的文本文件

1001 Apple Red
1002 Pear Yellow
1003 Pineapple Yellow
  • 创建 Hbase 表(程序不会自动创建表)

    create 'fruit','info'
    
  • 在 HDFS 中创建 input_fruit 文件夹并上传 fruit.tsv 文件

    $ /opt/module/hadoop-2.7.2/bin/hdfs dfs -mkdir /input_fruit/
    $ /opt/module/hadoop-2.7.2/bin/hdfs dfs -put fruit.tsv /input_fruit/
    
  • 执行 MapReduce到HBase的fruit表中

    /opt/module/hadoop-2.7.2/bin/yarn jar lib/hbase-server-1.3.1.jar importtsv -Dimporttsv.columns=HBASE_ROW_KEY,info:name,info:color fruit hdfs://hadoop102:9000/input_fruit
    
  • 使用 scan 命令查看导入后的结果

image.png

3.3 自定义HBase-MapReduce

3.3.1 案例一(HDFS-MR-HBase)

目标:实现将HDFS中的数据写入到HBase表中 读取HDFS中的tsv文件数据写入HBase使用 MapReduce 将本地CSV数据导入到 HBase

3.3.1.1 CSV数据(数据同官方案例二)

**.tsv** 文件是以 **\t** 进行分割的文本文件

1001 Apple Red
1002 Pear Yellow
1003 Pineapple Yellow

3.3.1.2 创建 Hbase 表(程序不会自动创建表)

create 'fruit1','info'

3.3.1.3 Mapper类

package com.jshawn.hbase.mr1;

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

import java.io.IOException;

/**
 * 需求:HDFS-MR-HBASE案例
 *      mapper中不做任何操作,在reducer中再做
 * @author JShawn 2021/6/18 18:53
 */
public class FruitMapper extends Mapper<LongWritable, Text, LongWritable, Text> {

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        // 直接写出
        context.write(key,value);
    }
}

3.3.1.4 Reducer类

不是继承Reducer,现在要写入HBase,所以继承TableReducer,而TableReducer实际就是Reducer

package com.jshawn.hbase.mr1;

import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;

import java.io.IOException;

/**
 * 需求:HDFS-MR-HBASE案例
 *      Reducer类:
 *          注意:不是继承Reducer,现在要写入HBase,所以继承TableReducer,而TableReducer实际就是Reducer
 *                但Reducer泛型有四个,TableReducer的泛型是三个,其中在源码中第四个参数已经定好了Mutation
 *                而Mutation的子类就是Append,Delete,Put,Increment四种HBase的对象
 * @author JShawn 2021/6/18 18:55
 */
public class FruitReducer extends TableReducer<LongWritable, Text, NullWritable> {

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

        // 1. 遍历values
        for (Text value : values) {

            // 2. 获取每一行数据
            String[] fields = value.toString().split("\t");

            // 3. 构建Put对象,这里不能提成成员变量,因为Put对象无空参构造,有参构造至少需要RowKey
            Put put = new Put(Bytes.toBytes(fields[0]));

            // 4. 给Put对象赋值
            // 数据:  1001 Apple Red
            put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("name"),Bytes.toBytes(fields[1]));
            put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("color"),Bytes.toBytes(fields[2]));

            // 5. 写出
            context.write(NullWritable.get(),put);
        }
    }
}

3.3.1.5 Driver类

package com.jshawn.hbase.mr1;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

/**
 * 需求:HDFS-MR-HBASE案例
 *      Driver类
 * @author JShawn 2021/6/18 19:06
 */
public class FruitDriver implements Tool {

    private Configuration configuration = null;

    @Override
    public int run(String[] args) throws Exception {
        // 1. 获取Job对象
        Job job = Job.getInstance(configuration);

        // 2. 设置类路径
        job.setJarByClass(FruitDriver.class);

        // 3. 设置Mapper和Mapper输出的KV类型
        job.setMapperClass(FruitMapper.class);
        job.setMapOutputKeyClass(LongWritable.class);
        job.setMapOutputValueClass(Text.class);

        // 4. 设置Reducer类,由于这里Reducer是和HBase打交道的Reducer,所以使用工具类TableMapReduceUtil
        // 4.1 设置输出路径(就是Hbase的表名,输出到哪张表),reducer类,job
        TableMapReduceUtil.initTableReducerJob(args[1], FruitReducer.class, job);

        // 5. 设置输入参数(就是HDFS的文件路径)
        FileInputFormat.setInputPaths(job,new Path(args[0]));

        //7. 提交任务
        boolean result = job.waitForCompletion(true);

        return result ? 0 : 1;
    }

    @Override
    public void setConf(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public Configuration getConf() {
        return configuration;
    }

    public static void main(String[] args) {

        try {
            Configuration configuration = new Configuration();
            int run = ToolRunner.run(configuration, new FruitDriver(), args);
            System.exit(run);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.3.1.6 测试

  • 打包上传至/opt/module/hbase,执行如下命令

    $ /opt/module/hadoop-2.7.2/bin/yarn jar hbase-learning-1.0-SNAPSHOT.jar com.jshawn.hbase.mr1.FruitDriver /input_fruit/fruit.tsv fruit1
    
  • 运行结果

image.png

3.3.2 案例二(HBase-HBase)

目标:数据清洗 将 fruit 表中的一部分数据(name列),通过 MR 迁入到 fruit2 表中。

3.3.2.1 源表数据(即案例一结果表)

image.png

3.3.2.2 创建HBase表

create 'fruit2','info'

3.3.2.3 Mapper类

package com.jshawn.hbase.mr2;

import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.IOException;

/**
 * HBase-HBase
 *      需求:HBase一张表的部分数据,提取到HBase另一张表中
 *      Mapper类:用于读取 源 fruit 表中的数据
 * @author JShawn 2021/6/18 20:03
 */
public class Fruit2Mapper extends TableMapper<ImmutableBytesWritable, Put> {

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

        // 1. 将 fruit 的 name 和 color 提取出来,相当于将每一行数据读取出来放入到 Put对象中。
        Put put = new Put(key.get());

        // 2. 遍历添加column行
        for (Cell cell : value.rawCells()) {
            // 添加/克隆列族:info
            if ("info".equals(Bytes.toString(CellUtil.cloneFamily(cell)))) {
                // 添加/克隆列“name”
                if ("name".equals(Bytes.toString(CellUtil.cloneQualifier(cell)))) {
                    // 将该列cell加入到put对象中
                    put.add(cell);
                }
            }
        }

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

3.3.2.4 Reducer类

package com.jshawn.hbase.mr2;

import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.io.NullWritable;

import java.io.IOException;

/**
 * HBase-HBase
 *      Reducer类:用于将读取到的 fruit 表中的数据写入到 fruit2 表中
 * @author JShawn 2021/6/18 22:04
 */
public class Fruit2Reducer extends TableReducer<ImmutableBytesWritable, Put, NullWritable> {

    @Override
    protected void reduce(ImmutableBytesWritable key, Iterable<Put> values, Context context) throws IOException, InterruptedException {
        //读出来的每一行数据写入到 fruit2 表中
        for(Put put: values){
            context.write(NullWritable.get(), put);
        }
    }
}

3.3.2.5 Driver类

package com.jshawn.hbase.mr2;

import com.jshawn.hbase.mr1.FruitReducer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Put;
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.mapreduce.Job;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

/**
 * Driver类:用于组装job信息
 *
 * @author JShawn 2021/6/18 22:07
 */
public class Fruit2Driver implements Tool {

    private Configuration configuration = null;

    @Override
    public int run(String[] strings) throws Exception {

        // 1. 获取configuration
        Configuration configuration = this.getConf();

        // 2. 创建Job对象
        Job job = Job.getInstance(configuration, this.getClass().getSimpleName());

        // 3. 配置job
        job.setJarByClass(Fruit2Driver.class);

        // 4. 可选,配置scan
        Scan scan = new Scan();
        scan.setCacheBlocks(false);
        scan.setCaching(50);

        // 5. 设置Mapper,注意导入的是MapReduce包下的,不是mapred包下的,后者是老版本
        TableMapReduceUtil.initTableMapperJob(
                "fruit",   //  设置输入源的表名
                scan,   // 设置scan扫描控制器(过滤,缓存等等)
                Fruit2Mapper.class, //  设置Mapper类
                ImmutableBytesWritable.class,   //  设置mapper输出key类型
                Put.class,  //  设置mapper输出value类型
                job         //  设置给哪个job
        );

        // 6. 设置Reducer,
        TableMapReduceUtil.initTableReducerJob(
                "fruit2",
                Fruit2Reducer.class,
                job
        );

        // 7. 可选,设置Reducer数量,最少1个
        job.setNumReduceTasks(1);

        // 8. 执行
        boolean result = job.waitForCompletion(true);

        return result ? 0 : 1;

    }

    @Override
    public void setConf(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public Configuration getConf() {
        return configuration;
    }

    public static void main(String[] args) {
        // 环境运行
//        Configuration configuration = new Configuration();

        // 本地运行,默认create()方法中会读取本地hbase-site.xml文件获取环境信息用于程序的执行,因此本地必须从环境拉取hbase-site.xml文件
        Configuration configuration = HBaseConfiguration.create();
        try {
            int status = ToolRunner.run(configuration, new Fruit2Driver(), args);
            System.exit(status);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.3.2.7 本地hbase-site.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
/**
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-->
<configuration>
    <!-- 指定hbase数据在hadoop中存储目录 -->
    <property>
        <name>hbase.rootdir</name>
        <value>hdfs://hadoop101:9000/HBase</value>
    </property>
    <!-- 分布式 -->
    <property>
        <name>hbase.cluster.distributed</name>
        <value>true</value>
    </property>
    <!-- 0.98 后的新变动,之前版本没有.port,默认端口为 60000 -->
    <!-- master端口号16000;还可以配置webUI端口号,默认是16010 -->
    <property>
        <name>hbase.master.port</name>
        <value>16000</value>
    </property>
    <!-- zk地址 -->
    <property>
        <name>hbase.zookeeper.quorum</name>
        <value>hadoop101,hadoop102,hadoop103</value>
    </property>
    <!-- zk的功能目录,zk在磁盘上数据存储的目录 version 2 -->
    <property>
        <name>hbase.zookeeper.property.dataDir</name>
        <value>/opt/module/zookeeper-3.4.10/zkData</value>
    </property>
    <!-- 连接zk的超时时间 -->
    <property>
        <name>zookeeper.session.timeout</name>
        <value>1200000</value>
    </property>
    <!-- client端与zk发送心跳的时间间隔,线上配置:6000(6s),默认值:6000 -->
    <property>
        <name>hbase.zookeeper.property.tickTime</name>
        <value>6000</value>
    </property>
</configuration>

3.3.2.8 测试

image.png

4. 整合Hive

4.1 HBase 与 Hive 的对比

描述 用途 存储 特点
Hive 数据仓库
Hive 的本质其实就相当于将 HDFS 中已经存储的文件在 Mysql 中做了一个双射关系,以方便使用 HQL 去管理查询。
用于数据分析、清洗
Hive 适用于离线的数据分析和清洗,延迟较高。
基于 HDFS、MapReduce
Hive 存储的数据依旧在 DataNode上,编写的 HQL 语句终将是转换为 MapReduce 代码执行。
HBase 数据库
是一种面向列族存储的非关系型数据库。
用于存储结构化和非结构化的数据
适用于单表非关系型数据的存储,不适合做关联查询,类似 JOIN 等操作。
基于 HDFS
数据持久化存储的体现形式是 HFile,存放于 DataNode 中,被 ResionServer 以 region 的形式进行管理。
延迟较低,接入在线业务使用

4.2 HBase 与 Hive 集成使用

注意:HBase 与 Hive 的集成在最新的两个版本中无法兼容。所以,只能重新编译: hive-hbase-handler-1.2.2.jar ,如果公司使用的是CDH等,则不用考虑兼容性问题,这里提供已兼容编译好的jar hive-hbase-handler-1.2.1.jar

4.2.1 环境准备

因为会在操作 Hive 的同时对 HBase 也会产生影响,所以 Hive 需要持有操作HBase 的 Jar,那么需要拷贝 Hive 所依赖的 Jar 包(或者使用软连接的形式)。

# 1. 首先保证/etc/profile中的Hive和Hbase环境变量已配
# HBASE
export HBASE_HOME=/opt/module/hbase
export PATH=$PATH:$HBASE_HOME/bin
# HIVE
export HIVE_HOME=/opt/module/hive
export PATH=$PATH:$HIVE_HOME/bin

# 2. 做软链接(或者拷贝jar包)让hive可以操作hbase
ln -s $HBASE_HOME/lib/hbase-common-1.3.1.jar $HIVE_HOME/lib/hbase-common-1.3.1.jar
ln -s $HBASE_HOME/lib/hbase-server-1.3.1.jar $HIVE_HOME/lib/hbase-server-1.3.1.jar
ln -s $HBASE_HOME/lib/hbase-client-1.3.1.jar $HIVE_HOME/lib/hbase-client-1.3.1.jar
ln -s $HBASE_HOME/lib/hbase-protocol-1.3.1.jar $HIVE_HOME/lib/hbase-protocol-1.3.1.jar
ln -s $HBASE_HOME/lib/hbase-it-1.3.1.jar $HIVE_HOME/lib/hbase-it-1.3.1.jar
ln -s $HBASE_HOME/lib/htrace-core-3.1.0-incubating.jar $HIVE_HOME/lib/htrace-core-3.1.0-incubating.jar
ln -s $HBASE_HOME/lib/hbase-hadoop2-compat-1.3.1.jar $HIVE_HOME/lib/hbase-hadoop2-compat-1.3.1.jar
ln -s $HBASE_HOME/lib/hbase-hadoop-compat-1.3.1.jar $HIVE_HOME/lib/hbase-hadoop-compat-1.3.1.jar
  • 同时在 hive-site.xml 中增加 zookeeper 的属性
    <!--集成HBase需要增加zookeeper的信息-->
    <property>
    <name>hive.zookeeper.quorum</name>
    <value>hadoop101,hadoop102,hadoop103</value>
    <description>The list of ZooKeeper servers to talk to. This is only needed for read/write locks.</description>
    </property>
    <property>
    <name>hive.zookeeper.client.port</name>
    <value>2181</value>
    <description>The port of ZooKeeper servers to talk to. This is only needed for read/write locks.</description>
    </property>
    

    4.2.2 案例一

    ```sql — 1. 在Hive中创建表同时关联HBase,完成之后,可以分别进入 Hive 和 HBase 查看,都生成了对应的表 CREATE TABLE hive_hbase_emp_table( empno int, ename string, job string, mgr int, hiredate string, sal double, comm double, deptno int) STORED BY ‘org.apache.hadoop.hive.hbase.HBaseStorageHandler’ WITH SERDEPROPERTIES (“hbase.columns.mapping” = “:key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:comm,info:deptno”) TBLPROPERTIES (“hbase.table.name” = “hbase_emp_table”);

— 2.在 Hive 中创建临时中间表,用于 load 文件中的数据,不能将数据直接 load 进 Hive 所关联 HBase 的那张表中,因为load实际是put操作,需要走MR才行 CREATE TABLE emp( empno int, ename string, job string, mgr int, hiredate string, sal double, comm double, deptno int) row format delimited fields terminated by ‘\t’;

— 3. 向 Hive 中间表中 load 数据 hive> load data local inpath ‘/opt/module/hive/data/emp.txt’ into table emp;

— 4. 通过 insert 命令将中间表中的数据导入到 Hive 关联 Hbase 的那张表中 hive> insert into table hive_hbase_emp_table select * from emp;

— 5. 查看 Hive 以及关联的 HBase 表中是否已经成功的同步插入了数据 hive> select * from hive_hbase_emp_table; Hbase> scan ‘hbase_emp_table’

<a name="GTgmp"></a>
### 4.2.3 案例二
> 案例一是同时建表,而现在是在HBase中已经有表了,需要在Hive中创建一张表去关联HBase中那张表
> 在 HBase 中已经存储了某一张表 hbase_emp_table,然后在 Hive 中创建一个外部表来关联 HBase 中的 hbase_emp_table 这张表,使之可以借助 Hive 来分析 HBase 这张表中的数据。

```sql
-- 1. 使用EXTERNAL关键字创建外部表关联
CREATE EXTERNAL TABLE relevance_hbase_emp(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:comm,info:deptno")
TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table");

-- 2. 关联后就可以使用 Hive 函数进行一些分析操作了
hive (default)> select * from relevance_hbase_emp;