大数据平台搭建(四):hive中的表及UDF

前言

     本章介绍hive内部表、外部表、分区表和UDF等。

1.hive表介绍

    1.Hive虽说是数据仓库,其实可以认为就是一个mysql数据库,hive中的表名对应的是hdfs上的文件目录名,表内容就是对应目录下的文件。
    2.hive表的hdfs路径
     在hive-site.xml中,由参数hive.metastore.warehouse.dir指定,我的是/hivedata/warehouse。
     

2. 应用场景

     1.每天将收集到的网站日志定期流入HDFS文本文件,一天一个目录;
     2.在Hive中建立内部分区表,通过添加分区的方式,将每天HDFS上的原始日志映射到表的分区中;
     3.在外部表(原始日志表)的基础上做大量的统计分析,用到的中间表、结果表使用内部表存储,数据通过SELECT+INSERT。

3.内部表

     1.vim student.txt,并hadoop fs -put student.txt 上传到hdfs,举例(用’\t’间隔):

这里写图片描述

     2.创建内部表指定分隔符:
     create table student (id bigint,name string,age double,sex string) row format delimited fields terminated by ‘\t’;
     3.从hdfs load数据到表中:
     load data local inpath ‘hdfs://mycluster/test/student.txt’ into table student
     4.查询数据:

大数据平台搭建(四):hive中的表及UDF_第1张图片
     注意:此时student.txt 被移走了!

4.外部表

     1.创建外部表,关键字:external location
       create external table student_ext (id bigint,name string,age double,sex string) row format delimited fields terminated by ‘\t’ location ‘/test/data/’。
    2. /test/data/目录下所有数据都会被加载进来,但是hive表目录下没有新目录创建。

大数据平台搭建(四):hive中的表及UDF_第2张图片
大数据平台搭建(四):hive中的表及UDF_第3张图片

5.外部表和内部表区别

     1.DROP时不同:内部表DROP时候会删除HDFS上的数据; 外部表DROP时候不会删除(类似于挂载的其他路径)HDFS上的数据;
    2.适用场景不同:外部表需要定期将外部数据映射到表中,内部表一般作为中间表和结果表,比如外部表统计后的结果用内部表存储。
    3.外部表要使用关键字 external 和 location(创建表可以不指定,增加分区需要)。

6.分区表

    1.分区表其实就是数据量太大,将数据分不同目录来存储,统计时查询比较快。个人觉得分区表跟内部表和外部表没有什么关系,一定区分开来。
    2.创建分区表
     create table people (id bigint, name string) partitioned by (nation string) row format delimited fields terminated by ‘\t’;
    3.load数据到分区表(会移走源文件)
     load data inpath ‘hdfs://mycluster/test/data/china.txt’ into table people partition(nation=’China’)

大数据平台搭建(四):hive中的表及UDF_第4张图片

      load的方式会在hive中生成对应的分区目录,且目录下有数据。

大数据平台搭建(四):hive中的表及UDF_第5张图片

    4.直接增加分区,hive目录下不会产生分区目录。
     alter table people5 add partition(nation=’China’) location ‘/China’;

大数据平台搭建(四):hive中的表及UDF_第6张图片

    5.说的再多不如自己一试,没接触过的可能觉得有点晕,自己试一下就知道了!

7.UDF

    hive中,除了提供丰富的内置函数外,还允许用户自己定义UDF函数。
    开发自定义UDF函数有三种方式:
     1.继承org.apache.hadoop.hive.ql.exec.UDF;
     2.继承org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
     3.通过脚本的方式,比如python UDF、scala UDF等。
     简单的数据类型(比如String、Integer等)可以用UDF,复杂的数据类型(Array、Map、Struct)可以使用GenericUDF,或者通过脚本实现函数体。
    比如,我们有个需求:消费2000元以上的评级为A,其他的评级为B,先来看下表中数据如下:

大数据平台搭建(四):hive中的表及UDF_第7张图片

    UDF实现
      1.eclipse创建maven项目hive,引入依赖:
"https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4.0.0

  com.cnepay
  hive
  0.0.1-SNAPSHOT
  jar

  hive
  http://maven.apache.org

  
    UTF-8
  

  
    
        org.apache.hive
        hive-exec
        1.1.0
    
    
    
        org.apache.hadoop
        hadoop-common
        2.6.0
        
    
    
        jdk.tools
        jdk.tools
        1.6
        system
        ${JAVA_HOME}/lib/tools.jar
    


    
      junit
      junit
      3.8.1
      test
    
  

      2.java代码
package com.cnepay.hive;

import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;

public class CountFee extends UDF{
    private Text t = new Text();

    public Text evaluate(Double fee){

        if(fee >= 2000.0){
            t.set("A");
        }
        else{
            t.set("B");
        }

        return t;
    }
}
      3.将项目打成jar,并上传到Linux服务器

大数据平台搭建(四):hive中的表及UDF_第8张图片

      4.进入hive或beeline命令行,
        a.将jar包加入到hive的classpath: add jar /home/hadoop/countFee.jar;
        b.创建临时函数:create temporary function getFee as ‘com.cnepay.hive.CountFee’;

大数据平台搭建(四):hive中的表及UDF_第9张图片

     GenericUDF实现
     1.继承org.apache.hadoop.hive.ql.udf.generic.GenericUDF之后,必须需要重写3个重要的方法:
      public ObjectInspector initialize(ObjectInspector[] arguments)
      //必选,该方法用于函数初始化操作,并定义函数的返回值类型;
      public Object evaluate(DeferredObject[] args){}
      //必选,函数处理的核心方法,用途和UDF中的evaluate一样;
      public String getDisplayString(String[] children)
      //必选,显示函数的帮助信息
     2.java代码,关注下代码中注释
package com.cnepay.hive;

import java.util.ArrayList;

import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.io.Text;

public class WxqGenericUDF extends GenericUDF{

    private Object[] ret = new Object[2];

    /**
     * 作用:
     * 1.检查输入类型是否正确;
     * 2.初始化读取文件等操作;
     * 3.定义函数的返回值类型是对象;
     * 
     */
    @Override
    public ObjectInspector initialize(ObjectInspector[] arguments)
            throws UDFArgumentException {
        //检查输入参数个数
        if(arguments.length != 2){
            throw new UDFArgumentException(" The function 'WxqGenericUDF' accepts 2 argument !");
        }

        //存储struct对象的属性名称
        ArrayList structFieldNames = new ArrayList<>();
        //存储struct对象的属性值的类型
        ArrayList structFieldObjectInspectors = new ArrayList<>();

        //创建两个属性,name 和 level
        structFieldNames.add("name");
        structFieldNames.add("level");

        //定义name和level值的类型为string
        structFieldObjectInspectors.add(PrimitiveObjectInspectorFactory.writableStringObjectInspector);
        structFieldObjectInspectors.add(PrimitiveObjectInspectorFactory.writableStringObjectInspector);

        //定义函数的返回类型是struct对象
        StructObjectInspector soi = ObjectInspectorFactory.getStandardStructObjectInspector(structFieldNames, structFieldObjectInspectors);

        return soi;
    }

    /**
     * 遍历每条记录
     */
    @Override
    public Object evaluate(DeferredObject[] arguments) throws HiveException {
        String name = arguments[0].get().toString();
        Double fee = Double.parseDouble(arguments[1].get().toString());

        //编写具体业务逻辑,封装返回对象。
        ret[0] = new Text(name);
        if(fee >= 2000.0){
            ret[1] = new Text("A");
        }
        else{
            ret[1] = new Text("B");
        }


        return ret;
    }

    @Override
    public String getDisplayString(String[] children) {
        // TODO Auto-generated method stub
        return "Usage: WxqGenericUDF(String str1, Double d)";
    }



}

     3.由于我们的数据类型要封装成struct,所以先建一张复合数据类型为struct的表:
     这里写图片描述
     4.打包,上传,创建临时函数等步骤同UDF一样。

大数据平台搭建(四):hive中的表及UDF_第10张图片

     5.查看表中数据如下:

大数据平台搭建(四):hive中的表及UDF_第11张图片

     脚本实现(个人推荐)
      本例采用python,centos自带python,可以通过which python查看,所以不用担心python环境。那么为什么采用python脚本呢?简单啊,编程效率高啊,前面2种方式又得编译又得打包等,麻烦死 。
       1.vim getFee.py,编写python脚本如下:

大数据平台搭建(四):hive中的表及UDF_第12张图片

       2.测试脚本,cat py.txt | python getFee.py。注意:这里输入的是本地文件py.txt的内容,实际是把表的查询结果作为输入,所以这里的测试只能表示你写的脚本基本语法和逻辑没问题,不代表查表没问题。这个取决于python脚本的输入。

大数据平台搭建(四):hive中的表及UDF_第13张图片

       3.将脚本加入到分布式缓存,add file /home/hadoop/py/getFee.py;

这里写图片描述

       4.查询,select TRANSFORM(name,fee) USING ‘python getFee.py’ as (name,level) from t0415;

大数据平台搭建(四):hive中的表及UDF_第14张图片

      5.解析hql
      TRANSFORM(name,fee) :这里面的字段必须为表中字段,是查询结果的输出,python脚本的输入。python中遍历的每行(line)的值就是对应的这俩值,一定要注意这点,我就是刚开始不知道研究了大半天。
      USING ‘python getFee.py’:用哪个脚本来执行。
      as (name,level) :执行结果的表头,对应python中print的字段。

总结:

     本文主要讲了hive中的表,以及开发UDF的3种方式。这些也是日常开发中经常接触到的,比较重要,如果是初学者还是要多加练习和研究。

你可能感兴趣的:(大数据平台搭建(四):hive中的表及UDF)