一、Hive架构与基本组成java
下面是Hive的架构图。python
图1.1 Hive体系结构mysql
Hive的体系结构能够分为如下几部分:web
(1)用户接口主要有三个:CLI,Client 和 WUI。其中最经常使用的是CLI,Cli启动的时候,会同时启动一个Hive副本。Client是Hive的客户端,用户链接至Hive Server。在启动 Client模式的时候,须要指出Hive Server所在节点,而且在该节点启动Hive Server。 WUI是经过浏览器访问Hive。
(2)Hive将元数据存储在数据库中,如mysql、derby。Hive中的元数据包括表的名字,表的列和分区及其属性,表的属性(是否为外部表等),表的数据所在目录等。
(3)解释器、编译器、优化器完成HQL查询语句从词法分析、语法分析、编译、优化以及查询计划的生成。生成的查询计划存储在HDFS中,并在随后有MapReduce调用执行。
(4)Hive的数据存储在HDFS中,大部分的查询、计算由MapReduce完成(包含*的查询,好比select * from tbl不会生成MapRedcue任务)。正则表达式
Hive将元数据存储在RDBMS中,算法
有三种模式能够链接到数据库:sql
(1) 单用户模式。此模式链接到一个In-memory 的数据库Derby,通常用于Unit Test。数据库
图2.1 单用户模式apache
(2)多用户模式。经过网络链接到一个数据库中,是最常用到的模式。浏览器
图2.2 多用户模式
(3) 远程服务器模式。用于非Java客户端访问元数据库,在服务器端启动MetaStoreServer,客户端利用Thrift协议经过MetaStoreServer访问元数据库。
对于数据存储,Hive没有专门的数据存储格式,也没有为数据创建索引,用户能够很是自由的组织Hive中的表,只须要在建立表的时候告诉Hive数据中 的列分隔符和行分隔符,Hive就能够解析数据。Hive中全部的数据都存储在HDFS中,存储结构主要包括数据库、文件、表和视图。Hive中包含如下 数据模型:Table内部表,External Table外部表,Partition分区,Bucket桶。Hive默承认以直接加载文本文件,还支持sequence file 、RCFile。
Hive的数据模型介绍以下:
(1)Hive数据库
相似传统数据库的DataBase,在第三方数据库里实际是一张表。简单示例命令行 hive > create database test_database;
(2)内部表
Hive的内部表与数据库中的Table在概念上是相似。每个Table在Hive中都有一个相应的目录存储数据。例如一个表pvs,它在HDFS中的 路径为/wh/pvs,其中wh是在hive-site.xml中由${hive.metastore.warehouse.dir} 指定的数据仓库的目录,全部的Table数据(不包括External Table)都保存在这个目录中。删除表时,元数据与数据都会被删除。
内部表简单示例:
建立数据文件:test_inner_table.txt
建立表:create table test_inner_table (key string)
加载数据:LOAD DATA LOCAL INPATH ‘filepath’ INTO TABLE test_inner_table
查看数据:select * from test_inner_table; select count(*) from test_inner_table
删除表:drop table test_inner_table
(3)外部表
外部表指向已经在HDFS中存在的数据,能够建立Partition。它和内部表在元数据的组织上是相同的,而实际数据的存储则有较大的差别。内部表的建立过程和数据加载过程这两个过程能够分别独立完成,也能够在同一个语句中完成,在加载数据的过程当中,实际数据会被移动到数据仓库目录中;以后对数据对访问将会直接在数据仓库目录中完成。删除表时,表中的数据和元数据将会被同时删除。而外部表只有一个过程,加载数据和建立表同时完成(CREATE EXTERNAL TABLE ……LOCATION),实际数据是存储在LOCATION后面指定的 HDFS 路径中,并不会移动到数据仓库目录中。当删除一个External Table时,仅删除该连接。
外部表简单示例:
建立数据文件:test_external_table.txt
建立表:create external table test_external_table (key string)
加载数据:LOAD DATA INPATH ‘filepath’ INTO TABLE test_inner_table
查看数据:select * from test_external_table; •select count(*) from test_external_table
删除表:drop table test_external_table
(4)分区
Partition对应于数据库中的Partition列的密集索引,可是Hive中Partition的组织方式和数据库中的很不相同。在Hive中, 表中的一个Partition对应于表下的一个目录,全部的Partition的数据都存储在对应的目录中。例如pvs表中包含ds和city两个 Partition,则对应于ds = 20090801, ctry = US 的HDFS子目录为/wh/pvs/ds=20090801/ctry=US;对应于 ds = 20090801, ctry = CA 的HDFS子目录为/wh/pvs/ds=20090801/ctry=CA。
分区表简单示例:
建立数据文件:test_partition_table.txt
建立表:create table test_partition_table (key string) partitioned by (dt string)
加载数据:LOAD DATA INPATH ‘filepath’ INTO TABLE test_partition_table partition (dt=‘2006’)
查看数据:select * from test_partition_table; select count(*) from test_partition_table
删除表:drop table test_partition_table
(5)桶
Buckets是将表的列经过Hash算法进一步分解成不一样的文件存储。它对指定列计算hash,根据hash值切分数据,目的是为了并行,每个 Bucket对应一个文件。例如将user列分散至32个bucket,首先对user列的值计算hash,对应hash值为0的HDFS目录为/wh /pvs/ds=20090801/ctry=US/part-00000;hash值为20的HDFS目录为/wh/pvs/ds=20090801 /ctry=US/part-00020。若是想应用不少的Map任务这样是不错的选择。
桶的简单示例:
建立数据文件:test_bucket_table.txt
建立表:create table test_bucket_table (key string) clustered by (key) into 20 buckets
加载数据:LOAD DATA INPATH ‘filepath’ INTO TABLE test_bucket_table
查看数据:select * from test_bucket_table; set hive.enforce.bucketing = true;
(6)Hive的视图
视图与传统数据库的视图相似。视图是只读的,它基于的基本表,若是改变,数据增长不会影响视图的呈现;若是删除,会出现问题。•若是不指定视图的列,会根据select语句后的生成。
示例:create view test_view as select * from test
二、Hive的执行原理
图2.1 Hive的执行原理
Hive构建在Hadoop之上,
(1)HQL中对查询语句的解释、优化、生成查询计划是由Hive完成的
(2)全部的数据都是存储在Hadoop中
(3)查询计划被转化为MapReduce任务,在Hadoop中执行(有些查询没有MR任务,如:select * from table)
(4)Hadoop和Hive都是用UTF-8编码的
Hive编译器将一个Hive QL转换操做符。操做符Operator是Hive的最小的处理单元,每一个操做符表明HDFS的一个操做或者一道MapReduce做业。Operator都是hive定义的一个处理过程,其定义有:
protected List <Operator<? extends Serializable >> childOperators;
protected List <Operator<? extends Serializable >> parentOperators;
protected boolean done; // 初始化值为false
全部的操做构成了Operator图,hive正是基于这些图关系来处理诸如limit, group by, join等操做。
图2.2 Hive QL的操做符
操做符以下:
TableScanOperator:扫描hive表数据
ReduceSinkOperator:建立将发送到Reducer端的<Key,Value>对
JoinOperator:Join两份数据
SelectOperator:选择输出列
FileSinkOperator:创建结果数据,输出至文件
FilterOperator:过滤输入数据
GroupByOperator:GroupBy语句
MapJoinOperator:/*+mapjoin(t) */
LimitOperator:Limit语句
UnionOperator:Union语句
Hive经过ExecMapper和ExecReducer执行MapReduce任务。在执行MapReduce时有两种模式,即本地模式和分布式模式 。
Hive编译器的组成:
图2.3 Hive编译器的组成
编译流程以下:
图2.4 Hive QL编译流程
三、Hive和数据库的异同
因为Hive采用了SQL的查询语言HQL,所以很容易将Hive理解为数据库。其实从结构上来看,Hive和数据库除了拥有相似的查询语言,再无相似之 处。数据库能够用在Online的应用中,可是Hive是为数据仓库而设计的,清楚这一点,有助于从应用角度理解Hive的特性。
Hive和数据库的比较以下表:
Hive |
RDBMS |
|
查询语言 |
HQL |
SQL |
数据存储 |
HDFS |
Raw Device or Local FS |
数据格式 |
用户定义 |
系统决定 |
数据更新 |
不支持 |
支持 |
索引 |
无 |
有 |
执行 |
MapReduce |
Executor |
执行延迟 |
高 |
低 |
处理数据规模 |
大 |
小 |
可扩展性 |
高 |
低 |
(1)查询语言。因为 SQL 被普遍的应用在数据仓库中,所以专门针对Hive的特性设计了类SQL的查询语言HQL。熟悉SQL开发的开发者能够很方便的使用Hive进行开发。
(2)数据存储位置。Hive是创建在Hadoop之上的,全部Hive的数据都是存储在HDFS中的。而数据库则能够将数据保存在块设备或者本地文件系统中。
(3)数据格式。Hive中没有定义专门的数据格式,数据格式能够由用户指定,用户定义数据格式须要指定三个属性:列分隔符(一般为空 格、”\t”、”\x001″)、行分隔符(”\n”)以及读取文件数据的方法(Hive中默认有三个文件格式 TextFile,SequenceFile以及RCFile)。因为在加载数据的过程当中,不须要从用户数据格式到Hive定义的数据格式的转换,所以,
Hive在加载的过程当中不会对数据自己进行任何修改,而只是将数据内容复制或者移动到相应的HDFS目录中。
而在数据库中,不一样的数据库有不一样的存储引擎,定义了本身的数据格式。全部数据都会按照必定的组织存储,所以,数据库加载数据的过程会比较耗时。
(4)数据更新。因为Hive是针对数据仓库应用设计的,而数据仓库的内容是读多写少的。所以,Hive中不支持对数据的改写和添加,全部的数据都 是在加载的时候中肯定好的。而数据库中的数据一般是须要常常进行修改的,所以可使用INSERT INTO ... VALUES添加数据,使用UPDATE ... SET修改数据。
(5)索引。以前已经说过,Hive在加载数据的过程当中不会对数据进行任何处理,甚至不会对数据进行扫描,所以也没有对数据中的某些Key创建索 引。Hive要访问数据中知足条件的特定值时,须要暴力扫描整个数据,所以访问延迟较高。因为MapReduce的引入, Hive能够并行访问数据,所以即便没有索引,对于大数据量的访问,Hive仍然能够体现出优点。数据库中,一般会针对一个或者几个列创建索引,所以对于 少许的特定条件的数据的访问,数据库能够有很高的效率,较低的延迟。因为数据的访问延迟较高,决定了Hive不适合在线数据查询。
(6)执行。Hive中大多数查询的执行是经过Hadoop提供的MapReduce来实现的(相似select * from tbl的查询不须要MapReduce)。而数据库一般有本身的执行引擎。
(7)执行延迟。以前提到,Hive在查询数据的时候,因为没有索引,须要扫描整个表,所以延迟较高。另一个致使Hive执行延迟高的因素是 MapReduce框架。因为MapReduce自己具备较高的延迟,所以在利用MapReduce执行Hive查询时,也会有较高的延迟。相对的,数据 库的执行延迟较低。固然,这个低是有条件的,即数据规模较小,当数据规模大到超过数据库的处理能力的时候,Hive的并行计算显然能体现出优点。
(8)可扩展性。因为Hive是创建在Hadoop之上的,所以Hive的可扩展性是和Hadoop的可扩展性是一致的(世界上最大的Hadoop 集群在Yahoo!,2009年的规模在4000台节点左右)。而数据库因为ACID语义的严格限制,扩展行很是有限。目前最早进的并行数据库 Oracle在理论上的扩展能力也只有100台左右。
(9)数据规模。因为Hive创建在集群上并能够利用MapReduce进行并行计算,所以能够支持很大规模的数据;对应的,数据库能够支持的数据规模较小。
四、Hive元数据库
Hive将元数据存储在RDBMS 中,通常经常使用的有MYSQL和DERBY。
启动HIVE的元数据库时,须要进入到hive的安装目录
启动derby数据库:/home/admin/caona/hive/build/dist/,运行startNetworkServer -h 0.0.0.0。
链接Derby数据库进行测试:查看/home/admin/caona/hive/build/dist/conf/hive-default.xml。找到
复制代码
进入derby安装目录:/home/admin/caona/hive/build/dist/db-derby-10.4.1.3-bin/bin
输入:./ij Connect 'jdbc:derby://hadoop1:1527/metastore_db;create=true';
hive元数据对应的表约有20个,其中和表结构信息有关的有9张,其他的10多张或为空,或只有简单的几条记录,如下是部分主要表的简要说明。
表名 | 说明 | 关联键 |
TBLS | 全部hive表的基本信息 | TBL_ID,SD_ID |
TABLE_PARAM | 表级属性,如是否外部表,表注释等 | TBL_ID |
COLUMNS | Hive表字段信息(字段注释,字段名,字段类型,字段序号) | SD_ID |
SDS | 全部hive表、表分区所对应的hdfs数据目录和数据格式 | SD_ID,SERDE_ID |
SERDE_PARAM | 序列化反序列化信息,如行分隔符、列分隔符、NULL的表示字符等 | SERDE_ID |
PARTITIONS | Hive表分区信息 | PART_ID,SD_ID,TBL_ID |
PARTITION_KEYS | Hive分区表分区键 | TBL_ID |
PARTITION_KEY_VALS | Hive表分区名(键值) | PART_ID |
从上面表的内容来看,hive整个建立表的过程已经比较清楚了。
(1)解析用户提交hive语句,对其进行解析,分解为表、字段、分区等hive对象
(2)根据解析到的信息构建对应的表、字段、分区等对象,从 SEQUENCE_TABLE中获取构建对象的最新ID,与构建对象信息(名称,类型等)一同经过DAO方法写入到元数据表中去,成功后将SEQUENCE_TABLE中对应的最新ID+5。
实际上咱们常见的RDBMS都是经过这种方法进行组织的,典型的如postgresql,其系统表中和hive元数据同样裸露了这些id信息 (oid,cid等),而Oracle等商业化的系统则隐藏了这些具体的ID。经过这些元数据咱们能够很容易的读到数据诸如建立一个表的数据字典信息,比 如导出建表语名等。
五、Hive基本操做
Create Table语句的一些注意项:
(1)CREATE TABLE建立一个指定名字的表。若是相同名字的表已经存在,则抛出异常;用户能够用IF NOT EXIST选项来忽略这个异常。
(2)EXTERNAL 关键字可让用户建立一个外部表,在建表的同时指定一个指向实际数据的路径( LOCATION ),Hive 建立内部表时,会将数据移动到数据仓库指向的路径;若建立外部表,仅记录数据所在的路径,不对数据的位置作任何改变。在删除表的时候,内部表的元数据和数 据会被一块儿删除,而外部表只删除元数据,不删除数据。
(3)LIKE容许用户复制现有的表结构,可是不复制数据。
(4)用户在建表的时候能够自定义SerDe或者使用自带的 SerDe ( Serialize/Deserilize 的简称,目的是用于序列化和反序列化 )。若是没有指定 ROW FORMAT 或者 ROW FORMAT DELIMITED,将会使用自带的SerDe。在建表的时候,用户还须要为表指定列,用户在指定表的列的同时也会指定自定义的SerDe,Hive经过 SerDe肯定表的具体的列的数据。
(5)若是文件数据是纯文本,可使用STORED AS TEXTFILE。若是数据须要压缩,使用STORED AS SEQUENCE。
(6)有分区的表能够在建立的时候使用 PARTITIONED B Y语句。一个表能够拥有一个或者多个分区,每个分区单独存在一个目录下。并且,表和分区均可以对某个列进行CLUSTERED BY操做,将若干个列放入一个桶(bucket)中。也能够利用SORT BY对数据进行排序。这样能够为特定应用提升性能。
(7)表名和列名不区分大小写,SerDe和属性名区分大小写。表和列的注释是字符串。
Alter Table语句:主要功能包括Add Partitions, Drop Partitions, Rename Table, Change Column, Add/Replace Columns。
Create View语句:建立视图。格式CREATE VIEW [IF NOT EXISTS] view_name [ (column_name [COMMENT column_comment], ...) ]
Showy语句:Show tables; Show partitions; describe查看表结构。
Load语句:HIVE装载数据时没有作任何转换,加载到表中的数据只是进入相应的配置单元表的位置。Load操做只是单纯的复制/移动操做,将数据文件移动到Hive表对应的位置。
Insert语句:插入数据。Hive不支持一条一条的用 insert 语句进行插入操做,这个应该是与hive的storage layer是有关系的,由于它的存储层是HDFS,插入一个数据要全表扫描,还不如用整个表的替换来的快些。Hive也不支持update的操做。数据是 以load的方式,加载到创建好的表中。数据一旦导入,则不可修改。要么drop掉整个表,要么创建新的表,导入新的数据。
Drop语句:删除一个内部表的同时会同时删除表的元数据和数据。删除一个外部表,只删除元数据而保留数据。
Limit子句:能够限制查询的记录数。查询的结果是随机选择的。下面的查询语句从 t1 表中随机查询5条记录,SELECT * FROM t1 LIMIT 5。
Top K查询:下面的查询语句查询销售记录最大的 5 个销售表明。
SET mapred.reduce.tasks = 1
SELECT * FROM sales SORT BY amount DESC LIMIT 5
正则表达式使用:SELECT语句可使用正则表达式作列选择,下面的语句查询除了ds和h 以外的全部列:
SELECT `(ds|hr)?+.+` FROM sales
SELECT语句:查询数据。
Group by, Order by, Sort by子句:聚合可进一步分为多个表,甚至发送到 Hadoop 的 DFS 的文件(能够进行操做,而后使用HDFS的utilitites)。能够用hive.map.aggr控制怎么进行汇总。默认为为true,配置单元会作 的第一级聚合直接在MAP上的任务。这一般提供更好的效率,但可能须要更多的内存来运行成功。
Join语句:链接操做。一些注意事项:
(1)Hive只支持等值链接(equality joins)、外链接(outer joins)和(left/right joins)。Hive不支持全部非等值的链接,由于非等值链接很是难转化到map/reduce任务。
(2)Hive 支持多于2个表的链接。
(3)join 时,每次 map/reduce 任务的逻辑: reducer 会缓存 join 序列中除了最后一个表的全部表的记录, 再经过最后一个表将结果序列化到文件系统。这一实现有助于在reduce端减小内存的使用量。实践中,应该把最大的那个表写在最后(不然会由于缓存浪费大 量内存)。
(4)LEFT,RIGHT 和 FULL OUTER 关键字用于处理 join 中空记录的状况。
(5)LEFT SEMI JOIN 是 IN/EXISTS 子查询的一种更高效的实现。Hive 当前没有实现 IN/EXISTS 子查询,因此你能够用 LEFT SEMI JOIN 重写你的子查询语句。LEFT SEMI JOIN的限制是, JOIN子句中右边的表只能在ON子句中设置过滤条件,在WHERE子句、SELECT子句或其余地方过滤都不行。
六、使用HIVE注意点
(1)字符集
Hadoop和Hive都是用UTF-8编码的,因此, 全部中文必须是UTF-8编码, 才能正常使用。
备注:中文数据load到表里面,,若是字符集不一样,颇有可能全是乱码须要作转码的,可是hive自己没有函数来作这个。
(2)压缩
hive.exec.compress.output 这个参数,默认是false,可是不少时候貌似要单独显式设置一遍,不然会对结果作压缩的,若是你的这个文件后面还要在hadoop下直接操做,那么就不能压缩了。
(3)count(distinct)
当前的Hive不支持在一条查询语句中有多Distinct。若是要在Hive查询语句中实现多Distinct,须要使用至少n+1条查询语句(n为 distinct的数目),前n条查询分别对n个列去重,最后一条查询语句对n个去重以后的列作Join操做,获得最终结果。
(4)JOIN
只支持等值链接
(5)DML操做
只支持INSERT/LOAD操做,无UPDATE和DELTE
(6)HAVING
不支持HAVING操做。若是须要这个功能要嵌套一个子查询用where限制
(7)子查询
Hive不支持where子句中的子查询
(8)Join中处理null值的语义区别
SQL标准中,任何对null的操做(数值比较,字符串操做等)结果都为null。Hive对null值处理的逻辑和标准基本一致,除了Join时的特殊 逻辑。这里的特殊逻辑指的是,Hive的Join中,做为Join key的字段比较,null=null是有意义的,且返回值为true。
(9)分号字符
分号是SQL语句结束标记,在HiveQL中也是,可是在HiveQL中,对分号的识别没有那么智慧,例如:
select concat(cookie_id,concat(';',’zoo’)) from c02_clickstat_fatdt1 limit 2;
FAILED: Parse Error: line 0:-1 cannot recognize input '<EOF>' in function specification
能够推断,Hive解析语句的时候,只要遇到分号就认为语句结束,而不管是否用引号包含起来。
解决的办法是,使用分号的八进制的ASCII码进行转义,那么上述语句应写成:
select concat(cookie_id,concat('\073','zoo')) from c02_clickstat_fatdt1 limit 2;
为何是八进制ASCII码?我尝试用十六进制的ASCII码,但Hive会将其视为字符串处理并未转义,好像仅支持八进制,缘由不详。这个规则也适用于 其余非SELECT语句,如CREATE TABLE中须要定义分隔符,那么对不可见字符作分隔符就须要用八进制的ASCII码来转义。
(10)Insert
根据语法Insert必须加“OVERWRITE”关键字,也就是说每一次插入都是一次重写。
七、Hive的扩展特性
Hive 是一个很开放的系统,不少内容都支持用户定制,包括:
* 文件格式:Text File,Sequence File
* 内存中的数据格式: Java Integer/String, Hadoop IntWritable/Text
* 用户提供的map/reduce脚本:无论什么语言,利用stdin/stdout传输数据
* 用户自定义函数:Substr, Trim, 1 – 1
* 用户自定义聚合函数:Sum, Average…… n – 1
(1)数据文件格式
TextFile | SequenceFIle | RCFFile | |
Data type | Text Only | Text/Binary | Text/Binary |
Internal Storage Order | Row-based | Row-based | Column-based |
Compression | File Based | Block Based | Block Based |
Splitable | YES | YES | YES |
Splitable After Compression | No | YES | YES |
例如使用文件文件格式存储建立的表:
复制代码
当用户的数据文件格式不能被当前Hive所识别的时候,能够自定义文件格式。能够参考contrib/src/java/org/apache /hadoop/hive/contrib/fileformat/base64中的例子。写完自定义的格式后,在建立表的时候指定相应的文件格式就可 以:
复制代码
(2)SerDe
SerDe是Serialize/Deserilize的简称,目的是用于序列化和反序列化。序列化的格式包括:分隔符(tab、逗号、CTRL-A)、Thrift 协议
反序列化(内存内):Java Integer/String/ArrayList/HashMap、Hadoop Writable类、用户自定义类
其中,LazyObject只有在访问到列的时候才进行反序列化。 BinarySortable保留了排序的二进制格式。
当存在如下状况时,能够考虑增长新的SerDe:
* 用户的数据有特殊的序列化格式,当前的Hive不支持,而用户又不想在将数据加载至Hive前转换数据格式。
* 用户有更有效的序列化磁盘数据的方法。
用户若是想为Text数据增长自定义Serde,能够参照contrib/src/java/org/apache/hadoop/hive /contrib/serde2/RegexSerDe.java中的例子。RegexSerDe利用用户提供的正则表却是来反序列化数据,例如:
复制代码
用户若是想为Binary数据增长自定义的SerDe,能够参考例子serde/src/java/org/apache/hadoop/hive/serde2/binarysortable,例如:
复制代码
(3)Map/Reduce脚本(Transform)
用户能够自定义Hive使用的Map/Reduce脚本,好比:
复制代码
Map/Reduce脚本经过stdin/stdout进行数据的读写,调试信息输出到stderr。
(4)UDF(User-Defined-Function)
用户能够自定义函数对数据进行处理,例如:
复制代码
UDFTestLength.java为:
复制代码
UDF 具备如下特性:
* 用java写UDF很容易。
* Hadoop的Writables/Text 具备较高性能。
* UDF能够被重载。
* Hive支持隐式类型转换。
* UDF支持变长的参数。
* genericUDF 提供了较好的性能(避免了反射)。
(5)UDAF(User-Defined Aggregation Funcation)
例子:
复制代码
UDAFCount.java代码以下:
复制代码