写于2013年春,hive0.10
第1章 Hive基础
1.1 Hive基础
1.1.1 Hive是什么
Hive是基于hadoop开发的数据仓库工具,它提供给用户类似SQL的语言hiveQL进行开发,将其转换成map/reduce执行。同时,用户也可以写map/reduce程序作为hiveql插件运行。
Hive针对数据仓库特性做了很多,包括数据的存储,hiveql语法,HIVEQL解析。同时由于map/reduce等限制,不适合在OLTP中使用。
Hive最初是由facebook开发,现有apache基金会管理。很多大型的互联网公司已广泛应用于数据仓库,包括facebook, yahoo, 百度,阿里巴巴,腾讯等,中等的互联网公司也已开始应用,传统行业也有公司开始应用。
hadoop生态系统(主要包含hdfs, map/reduce, hbase, hive, pig, mahout, ZooKeeper),除了apache基金会,还有很多第三方公司对其性能、易用性,稳定性等方面进行改进,提供免费版或者收费版给用户使用,这些公司主要有cloudera,hortonworks,Intel,GreenPlum,MapR(C++重写了hadoop,对apache hadoop的改动最大)。
1.1.2 Hive的优点
基于hadoop,可以达到hadoop所能达到的横向扩展能力,数千台服务器的集群已不难做到,这是很多大互联网公司选择它的最重要的原因。与hadoop其他组件良好的配合实现复杂功能。针对数据仓库建立,hiveql可以让RDBMS用户快速的学习、开发、实现数据仓库的ETL、数据存储、即席查询等功能,绝大多数都不需要用复杂的MR就可以实现。
1.1.3 Hive的缺点
相对RDBMS,hiveql与sql92标准相比,有些用法没有实现,有些不一样,更不用说实现SQL99或SQL03了,但是短期目标也已要把hiveql做成是SQL92的超集。很多功能的缺失,意味着好多SQL功能需要开发人员自己实现。HiveQL跟MySQL数据库的语法很像,大部分都一样。
Hadoop的单机效率、稳定性、易用性不是很好,hive自然在这些方面做的不好。尤其是在小集群上,相对于greenplum这样的分布式数据库性能非常差。特别是HIVE BUG比较多,很多hive-site.xml中参数的修改,都可能引起BUG。
1.1.4 Hive与RDBMS相同点
从使用的角度来看,hive就是一个数据库系统。除了提供相当部分的SQL92功能外,还提供了JDBC支持,使其可以和一些SQL开发工具、ETL工具、BI工具进行集成;hiveQL中也有database, schema, table, view, index, partition等这样的概念,有很多RDBMS提供的函数,也可以自定义函数,可以将数据批量导入导出。
1.1.5 Hive与RDBMS不同点
深入两者的实现,就会发现其实还是有很大不同的。Hive是一个建立在文件系统的数据仓库系统,而RDBMS是关系型数据库管理系统。文件系统与RDBMS的不同,也是这二者的不同。Hive没有事务,没有快照,没有复制。HIVE依靠HDFS提供的数据块冗余实现高可用。RDBMS基本都有主外键约束,HIVE没有。RDBMS基本都是有表空间概念,元数据都是自身保存,HIVE中数据都是放在HDFS文件目录下的文件,元数据在Deby,mysql等关系型数据库中保存。
第2章 Hive对象
2.1 数据库
Hive中的数据库和schema是同一个东西,其意义同RDBMS中的schema一样。表现在HDFS中就是一个目录。如数据库dw默认表现在HDFS上就是$ hive.metastore.warehouse.dir下的dw.db目录。在此数据库下创建的表都会默认放在此目录下。目录可在创建时指定。
2.2 表
Hive中的表分为内部表和外部表,体现在HDFS上也是一个目录。内部表就是数据库中的普通表,外部表与内部的区别就在于drop table时外部表不会被删掉数据,内部表同时删掉数据,其他功能同普通表没有区别。外部表和内部表都可以通过hadoop操作文件方式进行管理,只要是和表的字段顺序,类型,列分隔符,行分隔符,文件类型等一致,都可以用hadoop fs –put 放入数据文件在HIVE中即可使用,跟在HIVE中insert或者import方式一样,从表中获取数据也可以用相反的方式得到,这一点很方便。
HIVE是文件系统,所以import(LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)]或 IMPORT [[EXTERNAL] TABLE tableOrPartitionReference] FROM path[a1] )的文件必须和目标表建表语句中指定的各参数保持一致,否则就会报错。它只是简单的将文件由hadoop namenode分隔传输到各datanode节点,不做任务转换。export(INSERT OVERWRITE [LOCAL] DIRECTORY directory1 SELECT ... FROM ...或EXPORT TABLE tableOrPartitionReference TO path)导出的数据文件列分隔符,行分隔符、NULL填充值(“\N”)等也和源表一样。其中列分隔符默认是ascii中的第一个字符,hive建表语句用“\001”表示,在C,C++,shell中用”\x01”表示,在java中用”\u001”表示。
在数据仓库中,表分区是一个很好的优化方式,HIVE中也支持,在HDFS中是在表目录下的目录。如dw数据库中的t表有分区day_id=20130401,二级分区data_type=1目录为 XX/dw.db/t/day_id=20130401/data_type=1。[1]
2.2.1 字段
字段与RDBMS中的字段用法基本一样。不同的地方在于,字段只是对其下的数据的逻辑反映,修改或者删除字段对数据没有任何影响。在对此表插入数据时,不能指定插入哪些字段,是按照建表时的字段顺序顺序放入数据的。
删除字段时需特别注意。
2.2.2 字段类型
支持的字段类型已经比较丰富,像单一类型(Primitive types)中的TINYINT,SMALLINT,INT,BIGINT,BOOLEAN与mysql中的一样。FLOAT, DOUBLE不能指定长度,精度,RDBMS默认。STRING即RDBMS中经常用的varchar,不能指定长度,JAVA String类型默认。BINARY与mysql中的差不多,不能指定长度。TIMESTAMP 除了包含年月日等,还精确到毫秒,同时支持unix的时间类型,可以是整数,也可以是小数[a2] 。
复合类型(Complex types)使得存储和计算不满足数据第一范式的数据比较方便。arrays:ARRAY<data_type> data_types可以是单一或者复合类型;maps:MAP<primitive_type,data_type> KEY:value结构;structs:STRUCT<col_name : data_type [COMMENTcol_comment], ...>,union:UNIONTYPE<data_type, data_type, ...>[a3] 复合类型需要在建表时用“COLLECTIONITEMS TERMINATED BY ',' MAP KEYS TERMINATED BY ':'”指定界定符, ','和':'自定义。[2]
2.3 索引
表和视图都可以建索引,但是建索引的性能测试却一直没有看到测试。[3]
2.4 视图
视图是表的逻辑表示,只读。在HDFS上没有对应的目录。视图的作用主要是屏蔽数据逻辑,即视图可以是个很复杂的查询,其他查询可以直接调用此视图而不用关心其中的逻辑;另一作用就是安全,可以控制用户对特定数据的访问。
2.5 函数
Hive中提供了丰富的内置函数供用户使用,同其他RDBMS一样,主要分成数学函数(MathematicalFunctions)、集合函数(CollectionFunctions)、类型转换函数(Type ConversionFunctions)、时间函数(Date Functions)、条件函数(Conditional Functions)、字符串函数(StringFunctions)、Misc. Functions[a4] 、聚合函数(Aggregate Functions)(UDAF)、Table-Generating Functions[a5] (UDTF)等。[4]
同时提供自定义接口,用户可以自定义函数。[5] 自定义函数必须用JAVA写,打成jar包,然后在HIVE中创建函数与JAVA类映射。默认创建的函数只在当前session有效,session关闭即失效。如果像RDBMS中的函数一样,一旦创建就可以一直使用,需要修改HIVE源代码中的一个文件,然后重新ant HIVE源代码。具体步骤如下:
2.6 其他
Hive支持很多种存储格式,除了其本身已经支持的SEQUENCEFILE、TEXTFILE、RCFILE,还支持Avro存储,支持将数据存储在HBASE中。另外还支持用户自定义的文件格式,很多第三方数据存储格式也得到了开源社区很好的支持,如elephant-bird就对google Protocol Buffer提供了很好的支持[6]。
TEXTFILE就是文本文件,适用于直接与HVIE交换文本文件数据,在压缩后不能分成块,因此不能并行执行。SEQUENCEFILE是二进制文件,在压缩后分隔成块,可以并行执行。TEXTFILE与SEQUENCEFILE都是行存储,RCFILE[7]是基于行列混和的方式,先按行把数据划分成N个row group,在row group中对每个列分别进行存储。因此RCFILE对数据的读取效率很高,在存储时性能相比其他两种要差一些。对于数据仓库应用,一次写入多次读取还是很有优势的。三种不同文件格式性能的区别见[8]。
第3章 HiveQL
HiveQL总体上与MySQL语法很像,很多写法可以直接参考MySQL语法。同RDBMS一样,HiveQL与分为DDL,DML,Select三种。Hive语法并不是SQL92的子集,有相当一部分功能还是不支持的。
3.1 DDL
与RDBMS系统不同,创建分区表时分区字段不能出现在create table的字段中,只能写在PARTITIONED BY的后面。表分区支持多级分区,对性能优化,开发的方便都有很大的帮助。
在HIVE中数据倾斜是个很头疼的问题,特别是关联时,hive.optimize.skewjoin参数起一定作用,很多时候还不得不重写生产上的SQL。从0.10.0开始,支持SKEWED BY关键字,用以指定字段中比例很高的数据,并将此数据分成单独的文件,查询时单独处理。
对表字段的修改不会影响表的数据,对表参数,文件格式的修改也不会影响现有的数据,只会影响新插入的数据。
创建索引时可选字段有些是必须的,没有默认的index_type,WITH DEFERRED REBUILD也必须加上。[a6]
DDL语法见[9]
3.2 DML
Hive支持从本地文件系统或者HDFS文件系统中将数据导入到表或者表分区中,也支持将表中的数据导出到本地文件系统或者HDFS文件系统。导入导出指定的都是文件夹,而不是文件。导入过程没有任何转换逻辑,导出的文件格式都是hive默认的。
将表中的数据插入表或者文件系统,都支持一次读取,多次写入,查询相同或类似的可以充分利用此特性,可大大提高性能。写入分两种:INSERT OVERWRITE和INSERTINTO,对于不存在的表分区或文件目录,如果不存在,则创建,但是如果查询结果是空的,则相当于没有执行此语句。
DML语法见[10]
3.3 Select
像RDBMS中的exists, not exists, in(子查询),not in(子查询)都不支持,可以用join改写。Exists, in 可以用left semi join改写,not in , not exists可以用left outer join或者right outer join改写。只支持等值JOIN,不支持不等值JOIN,可以用先join再where条件过滤的方式,或者自己写函数,在函数内部实现一张表与另一张表的不等值JOIN,这两种方式都不适用两张表都很大的情况。
越来越多的功能加入到HIVE中,大大减少了开发工作量,同时提高了运行效率,特别是cube, rollup, grouping set[11]等功能的加入。期待更多的功能加入HIVE,运行效率再大大提高。HIVE蓝图[12]
Hive和Hadoop有不少参数经调整后,对特定查询有比较大的影响。像RDBMS一样,如果能把所有的查询性能都有很大的提升,参数就会改成那样了。Hive也没有参数银弹,调整参数除需要对此参数的作用进行详细了解外,还得关注会不会影响其他的查询,会不会引发BUG。真正对查询性能影响最大的,还是设计和查询语句的写法。
在查询语句中,对性能影响最大的是JOIN操作,HIVE也不例外。HVIE本身是文件系统,对于JOIN操作,也是全表扫描的方式实现,其性能相比RDBMS降低更严重。HIVE特别不适合N多表关联计算。如果关联的表中有可以在单台服务器内存中放下的小表,可以用mapjoin(注有表别名的要用别名) hint的方式使其在MAP阶段中进行JOIN,可以大大提高JOIN性能。另外,可以设hive.auto.convert.join=true,自动根据文件大小决定是否执行mapjon。
第4章 Hive交互
4.1 与Hbase集成
Hive的数据除了可以放在HDFS文件系统上,还可以放在HBASE[13]中。HIVE提供的HiveQL,完成统计分析的难度大大简化。Hbase具有非常好的线性扩展能力,可以支持百亿级以上记录的查询写入,一些简单的统计分析可以在此借用HIVE实现。
在Hive中建表示例如下:
CREATE TABLE hbase_table_1(keyint,value string)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES("hbase.columns.mapping"=":key,cf1:val")
TBLPROPERTIES ("hbase.table.name"="xyz");
hbase.table.name 定义在hbase的table名称
hbase.columns.mapping 定义在hbase的列族。
这样在HIVE中会创建hbase_table_1表,同时在HBASE中会创建xyz表,两者的字段通过hbase.columns.mapping关联赶来。
对于在hbase中已经存在的表,可以创建hive外部表。
4.2 与其他工具集成
Hive实现了部分JDBC,ODBC功能,可以与支持JDBC,ODBC功能的工具集成使用,但同时需要注意没有支持的功能,在集成时可能会有问题。
使用JDBC,需要引入hadoop下的hadoop-*-core.jar[a8] , HIVE下 /build/dist/lib/*.jar。其他方面就跟使用其他JDBC功能一样了。[15]
ODBC除了需要安装对应的ODBC服务(如unixODBC)外,需要在服务器端起Hive Server服务,Apache Thrift服务。在客户端需要hive client和unixODBC API wrapper。只支持Linux操作系统。
4.3 与其他语言交互
Hive支持Thrift,理论上Thrift支持的语言如C++, Java, Python, PHP, Ruby, Erlang, Perl,Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delph等都可以支持。通过JDBC 和hrift Java Client的方式可以在embedded mode、standalone server中都可以用。python,php,ODBC,Thrift C++ Client等只能在standalone server下使用。[16]