Hive学习也有一段时间了,今天来对Hive进行一个总结,谈谈自己的理解,作者还是个小白,有不对的地方请大家指出相互学习,共同进步。今天来谈一谈什么是Hive,产生背景,优势等一系列问题。
什么是Hive
先来谈谈自己的理解:
有些人可能会说Hive不就是写SQL的吗,那我们其实可以从另一个角度来理解:Hive就是那么强大啊,只要写SQL就能解决问题,其实这些人说的也没错Hive确实就是写SQL的,对于传统的 DBA人员或者会写SQL就很容易上手了,但是您知道他的底层细节吗,怎么优化呢?和传统的关系型数据库又有什么区别呢?等等一系列问题。。。
Hive是一个构建在Hadoop之上的数据仓库软件,它可以使已经存储的数据结构化,它提供类似sql的查询语句HiveQL对数据进行分析处理。
Hive将HiveQL语句转换成一系列成MapReduce作业并执行(SQL转化为MapReduce的过程你知道吗?)。用户可以很方便的使用命令行和JDBC程序的方式来连接到hive。
目前,Hive除了支持MapReduce计算引擎,还支持Spark和Tez这两中分布式计算引擎。常用于离线批处理。
(Hive On Spark 还是试验版本)
Hive的产生背景
大数据的时代,海量的数据对于传统的关系型数据库来说维护起来成本非常高,那该如何是好,Hadoop分布式的框架,可以使用廉价的机器部署分布式系统把数据存储再HDFS之上,通过MR进行计算,分析,这样是可以的,但是,MR大家应该知道,MapReduce编程带来的不便性,编程十分繁琐,在大多情况下,每个MapReduce程序需要包含Mapper、Reduceer和一个Driver,之后需要打成jar包扔到集群上运 行。如果mr写完之后,且该项目已经上线,一旦业务逻辑发生了改变,可能就会带来大规模的改动代码,然后重新打包,发布,非常麻烦(这种方式,也是最古老的方式)
当大量数据都存放在HDFS上,如何快速的对HDFS上的文件进行统计分析操作?
一般来说,想要做会有两种方式:
学Java、学MapReduce(十分麻烦)
做DBA的:写SQL(希望能通过写SQL这样的方式来实现,这种方式较好)
然而,HDFS中最关键的一点就是,数据存储HDFS上是没有schema的概念的(schema:相当于表里面有列、字段、字段名称、字段与字段之间的分隔符等,这些就是schema信息)然而HDFS上的仅仅只是一个纯的文本文件而已,那么,没有schema,就没办法使用sql进行查询了啊。。。因此,在这种背景下,就有问题产生:如何为HDFS上的文件添加Schema信息?如果加上去,是否就可以通过SQL的方式进行处理了呢?于是强大的Hive出现了。
Hive深入剖析
再来看看官网给我们的介绍:
官方第一句话就说明了Apache Hive 是构建在Apache Hadoop之上的数据仓库。有助于对大型的数据集进行读、写和管理。
那我们先对这句话进行剖析:
首先Hive是构建在Hadoop之上的,其实就是Hive中的数据其实是存储再HDFS上的(加上LOCAL关键字则是在本地),默认在/user/hive/warehouse/table,有助于对大型数据集进行读、写和管理,那也就是意味着传统的关系型数据库已经无法满足现在的数据量了,需要一个更大的仓库来帮助我们存储,这里也引出一个问题:Hive和关系型数据库的区别,后面我们再来聊。
Hive的特征:
1.可通过SQL轻松访问数据的工具,从而实现数据仓库任务,如提取/转换/加载(ETL),报告和数据分析。
2.它可以使已经存储的数据结构化
3.可以直接访问存储在Apache HDFS™或其他数据存储系统(如Apache HBase™)中的文件
4.Hive除了支持MapReduce计算引擎,还支持Spark和Tez这两中分布式计算引擎(这里会引申出一个问题,哪些查询跑mr哪些不跑?)
5.它提供类似sql的查询语句HiveQL对数据进行分析处理。
6. 数据的存储格式有多种,比如数据源是二进制格式, 普通文本格式等等
而hive强大之处不要求数据转换成特定的格式,而是利用hadoop本身InputFormat API来从不同的数据源读取数据,同样地使用OutputFormat API将数据写成不同的格式。所以对于不同的数据源,或者写出不同的格式就需要不同的对应的InputFormat和Outputformat类的实现。
以stored as textfile为例,其在底层java API中表现是输入InputFormat格式:TextInputFormat以及输出OutputFormat格式:HiveIgnoreKeyTextOutputFormat.这里InputFormat中定义了如何对数据源文本进行读取划分,以及如何将切片分割成记录存入表中。而Outputformat定义了如何将这些切片写回到文件里或者直接在控制台输出。
不仅如此Hive的SQL还可以通过用户定义的函数(UDF),用户定义的聚合(UDAF)和用户定义的表函数(UDTF)进行扩展。
(几个函数之间的区别)
Hive中不仅可以使用逗号和制表符分隔值(CSV / TSV)文本文件,还可以使用Sequence File、RC、ORC、Parquet
(知道这几种存储格式的区别),
当然Hive还可以通过用户来自定义自己的存储格式,基本上前面说的到的几种格式完全够了。
Hive旨在最大限度地提高可伸缩性(通过向Hadoop集群动态添加更多机器扩展),性能,可扩展性,
容错性以及与其输入格式的松散耦合。
安装部署
安装部署这里我们就不讲解了,不会的同学,参考作者以前的博客
Hive基本语法
该篇博客主要讲解Hive底层的东西和一些优化对于基本的东西可以参考作者以前的博客。
DDL
DML
基本HQL
内置函数和基本的UDF函数
UDF函数这里要进行一个讲解UDF、DUAF、UDTF分别是啥。
我们知道Hive的SQL还可以通过用户定义的函数(UDF),用户定义的聚合(UDAF)和用户定义的表函数(UDTF)进行扩展。
当Hive提供的内置函数无法满足你的业务处理需要时,此时就可以考虑使用用户自定义函数(UDF:user-defined function)。
UDF(User-Defined-Function) 一进一出
UDAF(User- Defined Aggregation Funcation) 聚集函数,多进一出。
UDTF(User-Defined Table-Generating Functions) 一进多出,如lateral view explore()
Hive于关系型数据库的区别
时效性、延时性比较高,可扩展性高;
Hive数据规模大,优势在于处理大数据集,对于小数据集没有优势
事务没什么用(比较鸡肋,没什么实际的意义,对于离线的来说) 一个小问题:那个版本开始提供了事务?
insert/update没什么实际用途,大数据场景下大多数是select
RDBMS也支持分布式,节点有限 成本高,处理的数据量小
Hadoop集群规模更大 部署在廉价机器上,处理的数据量大
数据库可以用在Online的应用中,Hive主要进行离线的大数据分析;
数据库的查询语句为SQL,Hive的查询语句为HQL;
数据库数据存储在LocalFS,Hive的数据存储在HDFS;
数据格式:Hive中有多种存储格式:由于在加载数据的过程中,不需要从用户数据格式到 Hive 定义的数据格式的转换,
因此,Hive 在加载的过程中不会对数据本身进行任何修改,而只是将数据内容复制或者移动到相应的 HDFS 目录中。
而在数据库中,不同的数据库有不同的存储引擎,定义了自己的数据格式。所有数据都会按照一定的组织存储,因此,
数据库加载数据的过程会比较耗时。
Hive执行MapReduce,MySQL执行Executor;
Hive的优点
1.简单易上手
2.扩展能力较好(指集群 HDFS或是YARN)
3.统一的元数据管理 metastore包括的了数据库,表,字段分区等详细信息
该篇博客对于元数据信息进行了详细的讲解
4.由于统一的元数据管理所以和spark/impala等SQL引擎是通用的
通用是指,在拥有了统一的metastore之后,在Hive中创建一张表,在Spark/impala中是能用的,反之在Spark中创建一张表,
在Hive中也能用;只需要共用元数据,就可以切换SQL引擎
涉及到了Spark sql 和Hive On Spark(实验版本)
5.使用SQL语法,提供快速开发的能力,支持自定义函数UDF。
6.避免了去写mapreduce,减少开发人员学习成本。
7.数据离线处理,比如日志分析,海量数据结构化分析
SQL转化为MapReduce的过程
了解了MapReduce实现SQL基本操作之后,我们来看看Hive是如何将SQL转化为MapReduce任务的,整个编译过程分为六个阶段:
Antlr定义SQL的语法规则,完成SQL词法,语法解析,将SQL转化为抽象语法树AST Tree
遍历AST Tree,抽象出查询的基本组成单元QueryBlock
遍历QueryBlock,翻译为执行操作树OperatorTree
逻辑层优化器进行OperatorTree变换,合并不必要的ReduceSinkOperator,减少shuffle数据量
遍历OperatorTree,翻译为MapReduce任务
物理层优化器进行MapReduce任务的变换,生成最终的执行计划
可以参考美团的技术沙龙
Hive内部表和外部表的区别
未被external修饰的是内部表(managed table),被external修饰的为外部表(external table);
区别:
内部表数据由Hive自身管理,外部表数据由HDFS管理;
内部表数据存储的位置是hive.metastore.warehouse.dir(默认:/user/hive/warehouse),外部表数据的存储位置由自己制定;
删除内部表会直接删除元数据(metadata)及存储数据;删除外部表仅仅会删除元数据,HDFS上的文件并不会被删除;
行式存储vs列式存储
行式数据库存储在hdfs上式按行进行存储的,一个block存储一或多行数据。而列式数据库在hdfs上则是按照列进行存储,一个block可能有一列或多列数据。
如果要将数据进行压缩:
对于行式数据库,必然按行压缩,当一行中有多个字段,各个字段对应的数据类型可能不一致,压缩性能压缩比就比较差。
对于列式数据库,必然按列压缩,每一列对应的是相同数据类型的数据,故列式数据库的压缩性能要强于行式数据库。
如果要进行数据的查询:
假设执行的查询操作是:select id,name from table_emp;
对于行式数据库,它要遍历一整张表将每一行中的id,name字段拼接再展现出来,这样需要查询的数据量就比较大,效率低。
对于列式数据库,它只需找到对应的id,name字段的列展现出来即可,需要查询的数据量小,效率高。
假设执行的查询操作是:select * from table_emp;
对于这种查询整个表全部信息的操作,由于列式数据库需要将分散的行进行重新组合,行式数据库效率就高于列式数据库。
但是,在大数据领域,进行全表查询的场景少之又少,进而我们使用较多的还是列式数据库及列式储存。
Hive哪些查询会执行mr
hive 0.10.0为了执行效率考虑,简单的查询,就是只是select,不带count,sum,group by这样的,都不走map/reduce,直接读取hdfs文件进行filter过滤。
这样做的好处就是不新开mr任务,执行效率要提高不少,但是不好的地方就是用户界面不友好,有时候数据量大还是要等很长时间,但是又没有任何返回。
改这个很简单,在hive-site.xml里面有个配置参数叫
hive.fetch.task.conversion
将这个参数设置为more,简单查询就不走map/reduce了,设置为minimal,就任何简单select都会走map/reduce
Create Table As Select (CTAS) 走mr
create table emp2 as select * from emp;
insert一条或者多条 走mr
Hive静态分区动态分区
分区的概念
Hive的分区方式:由于Hive实际是存储在HDFS上的抽象,Hive的一个分区名对应HDFS上的一个目录名,子分区名就是子目录名,并不是一个实际字段。
分区的好处
产生背景:如果一个表中数据很多,我们查询时就很慢,耗费大量时间,如果要查询其中部分数据该怎么办呢,这是我们引入分区的概念。
Partition:分区,每张表中可以加入一个分区或者多个,方便查询,提高效率;并且HDFS上会有对应的分区目录:
语法:
Hive分区是在创建表的时候用Partitioned by 关键字定义的,但要注意,Partitioned by子句中定义的列是表中正式的列,
但是Hive下的数据文件中并不包含这些列,因为它们是目录名,真正的数据在分区目录下。
静态分区和 动态分区的区别
创建表的语法都一样
静态分区:加载数据的时候要指定分区的值(key=value),比较麻烦的是每次插入数据都要指定分区的值,创建多个分区多分区一样,以逗号分隔。
动态分区:
如果用上述的静态分区,插入的时候必须首先要知道有什么分区类型,而且每个分区写一个load data,太烦人。使用动态分区可解决以上问题,其可以根据查询得到的数据动态分配到分区里。其实动态分区与静态分区区别就是不指定分区目录,由系统自己选择。
首先,启动动态分区功能
hive> set hive.exec.dynamic.partition=true;
采用动态方式加载数据到目标表
加载之前先设置一下下面的参数
hive (default)> set hive.exec.dynamic.partition.mode=nonstrict
开始加载
insert into table emp_dynamic_partition partition(deptno)
select empno , ename , job , mgr , hiredate , sal , comm, deptno from emp;
12345678910111213
加载数据方式并没有指定具体的分区,只是指出了分区字段。
在select最后一个字段必须跟你的分区字段,这样就会自行根据deptno的value来分区。
删除分区:
ALTER TABLE my_partition_test_table DROP IF EXISTS PARTITION (day=‘2018-08-08’);