Hive核心概念剖析及示例

来源:互联网 发布:北京中云数据有限公司 编辑:程序博客网 时间:2024/06/07 15:10

一、Hive文件存储格式和记录格式

Hive中的文件格式和记录格式

Hive存储数据时底层一般使用的都是Hadoop的HDFS文件系统,Hive数据存在的形式为HDFS文件。
我们可以使用Hive load命令和insert ··· directory···select···fromSQL实现:

  1. 把文件中的数据导入到Hive数据表;
  2. 把Hive表中感兴趣的字段数据转储到指定的数据文件;

那么在这些过程中hive是怎么操作的呢?

补充:
1、load伪命令:load data local inpath ‘/yourDir’ overwrite into table ‘yourTable’ partion(column=’value’);

2、insert 伪SQL:insert overwrite local directory ‘/yourDir’ select * from yourTable where yourCondition;

首先,在Hive看来,Hive不会强制要求将数据转换成特定的文件格式后才能使用:

(1)Hive利用Hadoop的inputStream api从不同的数据源中读取数据,例如:文本格式文件、sequence格式文件,甚至用户自定义格式文件;
(2)同样的,Hive利用Hadoop的outputFormat api也可以将数据保存为不同的格式文件。

其次,在Hive看来,对于文件格式,只需要关注两个方面:

(1)文件是怎么分割成行(记录)

Hive默认使用文件文件保存数据,文本文件使用\n作为默认的行分隔符。

当用户没有使用默认的文本文件格式时,用户需要告诉Hive使用的InputFormat和OutputFormat是什么。事实上,用户需要指定对于输入和输出格式实现的Java类的名称。InputFormat中定义了如何读取划分,以及如何将划分分割为记录;而OutputFormat中定义了将这些划分写回到文件或控制台输出中。

实际上,InputFormat和OutputFormat就是Hadoop io中用于读写文件的inputstream和outputstream。

(2)行(记录)是怎么分割成字段(列)

Hive使用^A作为文本文件中默认的字段分隔符。

Hive使用SerDe(序列化/反序列化的缩写)作为对输入记录(反序列化)进行分割以及写记录(序列化)的模板。

一个SerDe包含了将一条记录的非结构化字节转化成Hive可以使用的一条记录的过程。

在Hive内部,Hive引擎使用定义的InputFormat来读取一行数据记录,这行记录之后被传递给SerDe.deserialize()方法进行处理。有些SerDe需要指定特定的属性才能完整的实例化某个对象。

所有这些信息用户在创建表的时候都可以在表定义语句中进行制定。创建完成后,用户可以向平时一样查询表,而无需关心底层格式。

示例1

这里使用一个自定义的SerDe、输入格式和输出格式的完整例子做个示例:

CREATE TABLE yourTablePARTITIONED BY(ds string)ROW FORMAT SERDE 'com.linkedin.haivvreo.AvroSerDe'WITH SERDEPROPERTIES('schema.url' = ‘http://schema_provider/kst.avsc’)STORED AS INPUTFORMAT 'com.linkedin.haivvreo.AvroInputFormat'OUTPUTFORMAT 'com.linkedin.haivvreo.AvroOutputFormat'

ROW FORMAT SERDE指定了使用的SerDe,Hive提供了WITH SERDEPROPERTIES功能,允许用户传递配置信息给SerDe,Hive本身并不知晓这些属性的含义,需要SerDe去决定这些属性所代表的含义。

STORED AS INPUTFORMAT······ OUTPUTFORMAT······子句分别指定了用于输入格式和输出格式的Java类。

需要注意的是,用户必须同时对输入格式和输出格式都进行指定。

示例2

在Hive中,某些语法是其它语法的快捷语法,例如:

CREATE TABLE yourtableSTORED AS TEXTFILE;

语法STORED AS的替代方式同时指定INPUTFORMAT和OUTPUTFORMAT:

  1. 指定INPUTFORMAT为’org.apache.hadoop.mapred.TextInputFormat’;
  2. 指定OUTPUTFORMAT为’org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat’;

示例3

创建表时指定记录格式,用户可以指定列分隔符及集合元素间的分隔符:

CREATE TABLE yourtable(······)ROW FORMAT DELIMITEDFIELDS TERMINATED BY '\001'COLLECTION ITEMS TERMINATED BY '\002'MAP KEYS TERMINATED BY '\002'LINES TERMINATED BY '\n'STORED AS TEXTFILE;

定义完这些分隔符后,用户就可以读取保存特定记录格式的文本文件。

这种强大的可定制功能使得可以很容易的使用Hive来处理那些由其它工具和程序产生的文件。

二、Hive与MapRedece

Apache HCatalog项目:以Hadoop MapReduce的方式操作Hive中海量数据

HCatalog官方文档

HCatalog提供了一个HCatInputFormat类来共MapReduce用户从Hive的数据仓库中读取数据。其允许用户只读取需要的表分区和字段,同时以一种方便的列表格式(HCatRecord)来使用记录,这样就不需要用户来进行划分了。

HCatalog提供了一个HCatOutputFormat类来写数据到文件。当HCatOutputFormat写数据时,输出的键不重要,而值得类型必须是HCatRecord。

在执行MapReduce任务时,Hive本身是不会生成Java MapReduce算法程序的。相反,Hive通过一个表示“job执行计划”的XML文件驱动执行内置的,原生的mapper和reducer模块。换句话说,这些通用的模块函数类似于微型的语言翻译程序,而这个驱动计算的“语言”是以XML形式编码的。

Hive通过和JobTracker通信来初始化MapReduce任务,而不必部署在JobTracker所在的管理节点上执行。

这里是一个官网使用HCatalog进行读写的示例:

public class GroupByAge extends Configured implements Tool {    public static class Map extends            Mapper<WritableComparable, HCatRecord, IntWritable, IntWritable> {        int age;        @Override        protected void map(                WritableComparable key,                HCatRecord value,                org.apache.hadoop.mapreduce.Mapper<WritableComparable, HCatRecord,                        IntWritable, IntWritable>.Context context)                throws IOException, InterruptedException {            age = (Integer) value.get(1);            context.write(new IntWritable(age), new IntWritable(1));        }    }    public static class Reduce extends Reducer<IntWritable, IntWritable,    WritableComparable, HCatRecord> {      @Override      protected void reduce(              IntWritable key,              java.lang.Iterable<IntWritable> values,              org.apache.hadoop.mapreduce.Reducer<IntWritable, IntWritable,                      WritableComparable, HCatRecord>.Context context)              throws IOException, InterruptedException {          int sum = 0;          Iterator<IntWritable> iter = values.iterator();          while (iter.hasNext()) {              sum++;              iter.next();          }          HCatRecord record = new DefaultHCatRecord(2);          record.set(0, key.get());          record.set(1, sum);          context.write(null, record);        }    }    public int run(String[] args) throws Exception {        Configuration conf = getConf();        args = new GenericOptionsParser(conf, args).getRemainingArgs();        String inputTableName = args[0];        String outputTableName = args[1];        String dbName = null;        Job job = new Job(conf, "GroupByAge");        HCatInputFormat.setInput(job, InputJobInfo.create(dbName,                inputTableName, null));        // initialize HCatOutputFormat        job.setInputFormatClass(HCatInputFormat.class);        job.setJarByClass(GroupByAge.class);        job.setMapperClass(Map.class);        job.setReducerClass(Reduce.class);        job.setMapOutputKeyClass(IntWritable.class);        job.setMapOutputValueClass(IntWritable.class);        job.setOutputKeyClass(WritableComparable.class);        job.setOutputValueClass(DefaultHCatRecord.class);        HCatOutputFormat.setOutput(job, OutputJobInfo.create(dbName,                outputTableName, null));        HCatSchema s = HCatOutputFormat.getTableSchema(job);        System.err.println("INFO: output schema explicitly set for writing:"                + s);        HCatOutputFormat.setSchema(job, s);        job.setOutputFormatClass(HCatOutputFormat.class);        return (job.waitForCompletion(true) ? 0 : 1);    }    public static void main(String[] args) throws Exception {        int exitCode = ToolRunner.run(new GroupByAge(), args);        System.exit(exitCode);    }}

HCatInputFormat通过和Hive的元数据存储进行通信来 获取要读取的表和分区的信息。这包括获取表的模式,也包括获取每个分区的模式。对于每个分区,还要确定对应的用于读取该分区数据的实际的InputFormat和SerDe。这些会被收集在一起,然后和所有的分区的数据划分一起,返回一个InputSplits对象列表。

同样,对于每个底层的InputFormat都会有对应的RecordReader用于对划分进行解码。然后HCatRecordReader会通过和分区对应的SerDe将来自底层的RecordReader的值转化成HCatRecord。这个过程中包含有为每个分区补充对应缺少的列,或者过滤掉不需要的列。

HCatOutputFormat也会和Hive元数据存储进行通信,以确定写入的文件格式和模式。

三、Hive与NoSQL

把其它NoSQL类型的数据当作标准的Hive数据表处理

HiveStorageHandler是Hive用于连接如HBase、Cassandra等类似NoSQL存储的主要接口,检查下接口可以发现需要定义一个定制的InputFormat、一个OutputFormat以及SerDe。存储处理程序负责从底层存储子系统中读取或写入数据。这就转变成通过写select查询读取数据系统中的数据,以及通过如reports这样的操作将数据写入到数据系统。

Hive中的抽象如表、类型、行格式以及其他元数据都用于Hive理解源数据。一旦Hive了解了源数据的描述信息,那么查询引擎就可以使用熟悉的HiveSQL操作符来处理数据。

在一个系统整体架构中将NoSQL数据库和Hadoop结合使用的一个通用技术就是使用NoSQL数据库集群来进行实时处理,而利用Hadoop集群进行非实时面向批处理的工作。如果NoSQL系统是主要数据存储,而其数据又需要通过Hadoop的批处理job进行查询的话,批量导出是一个将NoSQL数据导入到HDFS文件中的高效的方式。一旦HDFS中的文件通过导出生成后,就可以以最大效率执行批处理Hadoop job。

四、Hive的Thrift服务

以JDBC/ODBC编程方式访问Hive表中的数据和管理Hive的元数据

五、HiveSQL

  1. Hive不支持行级别的增删改;
  2. 和标准SQL一样,HiveSQL也可以分为三个部分:DDL、DML、DCL。
    其中,DCL又和Hive安全紧密相关(用户、组和角色;Grant 和 Revoke权限).

在使用HiveSQL时,经常会使用到分区,其实一个分区就对应着一个包含多个文件的文件夹。

六、Hive自定义函数UDF

Hive UDF分为3类:UDF/UDAF/UDTF

UDF官方文档

UDF

官网UDF示例
User Defined Scalar Function,通常也称之为UDF。用户自定义标量值函数(User Defined Scalar Function)通常也称之为UDF。其输入与输出是一对一的关系,即读入一行数据,写出一条输出值。

UDAF

官网UDAF示例
UDAF(User Defined Aggregation Function),自定义聚合函数,其输入与输出是多对一的关系,即将多条输入记录聚合成一条输出值。可以与 SQL中的Group By语句联用。

UDTF

官方UDTF示例
UDTF(User Defined Table Valued Function),自定义表值函数,是用来解决一次函数调用输出多行数据场景的,也是唯一能返回多个字段的自定义函数。而UDF只能一次计算输出一条返回值。

参考文献

Hive编程指南
HCatalog官方文档