Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能。其本质是将SQL转换为MapReduce的任务进行运算,底层由HDFS来提供数据的存储;通俗来讲,hive就是将SQL转换为MapReduce的任务的工具,或者可以说hive就是一个MapReduce的客户端。
尽管hive具有sql数据库的外表,但它不是一个完整的数据库,首先hive不支持记录级别的更新、插入或者删除操作,而且查询延时也比较严重,还不支持事务。因此,hive最适合于数据仓库应用程序,使用该程序进行相关的静态数据分析,不需要快速响应给出结果,而且数据本身不会频繁变化。
hive大致由用户接口、元数据库和驱动模块三部分组成。用户接口提供了如CLI、webGUI,以及可通过JDBC/ODBC和Thrift服务器进行编程访问的几个模块;元数据库主要用来存储表的名字、表的列和分区及其属性,以及表的数据所在目录等,常用使用关系型数据库Mysql来作为源数据库。hive默认的元数据库为derby。机构图如下如所示:
明白了什么是Hive之后,接下来要做的就是如何安装Hive。具体安装方法请参考官网、书籍或其它博文解决。安装好了hive之后,启动HIVE。
## 配置了环境变量,则可以在任意目录下输入hive即可启动
$ /usr/local/hive-2.1.1/bin/hive
提示:下文默认用户已经配置了环境变量,将不在使用绝对路径
有时候只是执行一个或者多个查询(分号分隔),执行完毕后就退出,则可以采用以下形式:
$ hive -e "SELECT * FROM mytable LIMIT 3";
## -S:开启静默模式,去掉一些无关紧要的输出信息
$ hive -S -e "SELECT * FROM mytable LIMIT 3";
如果使用sql脚本的方式执行,则采用以下形式:
$ hive -f /<path>/xxx.sql
补充知识点:
- 想要不退出hiveCLI就可以执行简单的bash shell命令,则在命令前面加上
!
并且以分号;
结尾即可;
了解了hive的交互方式之后,紧接着就要学习hive的基本操作。首当其冲的就是HiveQL,它是Hive的查询语言,用法和MySQL很接近,但也存在显著的差异。
一、数据定义
1 数据库操作
创建数据库
CREATE DATABASE [IF NOT EXISTS] dbName;
USE dbName;
说明:hive的表存放位置模式是由hive-site.xml当中的一个属性指定的
<name>hive.metastore.warehouse.dir</name>
<value>/user/hive/warehouse</value>
创建数据库并指定hdfs存储位置
CREATE DATABASE dbName LOCATION '/my/preferred/directory';
设置数据库键值对信息
数据库可以有一些描述性的键值对信息,在创建时添加:
CREATE DATABASE dbName WITH DBPROPERTIES('owner'='wjqixige','date'='20190120');
查看数据库键值对信息
DESC DATABASE EXTENDED dbName;
修改数据库键值对信息
ALTER DATABASE dbName SET DBPROPERTIES('owner'='wjqixige');
删除数据库
删除一个空数据库,如果数据库下面有数据表,则会报错
DROP DATABASE dbName;
强制删除数据库,包含数据库下面的表一起删除
DROP DATABASE dbName CASCADE;
2 数据库表操作
2.1 创建表
CREATE [EXTERNAL] TABLE [IF NOTE EXISTS] table_name(
col_name data_type [COMMENT '字段描述信息'],
col_name data_type [COMMENT '字段描述信息']) [COMMENT '表描述信息']
[PARTITIONED BY (col_name data_type [COMMENT '字段描述信息'], ...)]
[CLUSTERED BY (col_name, col_name, ...)]
[SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets buckets]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION '指定表的路径']
关键字说明,如下表:
关键字 | 说明 |
---|---|
CREATE TABLE | 创建一个指定名字的表。名字存在,则抛出异常;可以用 IF NOT EXISTS 选项忽略异常 |
EXTERNAL | 创建一个外部表,在建表的同时指定一个指向实际数据的路径(LOCATION), Hive 创建内部表时,会将数据移动到数据仓库指向的路径; 若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变。 在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据。 |
COMMENT | 注释,默认不能使用中文 |
PARTITIONED BY | 表分区,一个表可以拥有一个或者多个分区,每一个分区单独存在一个目录下 |
CLUSTERED BY | 对于每一个表分文件,Hive可以进一步组织成桶,是更为细粒度的数据范围划分 |
SORTED BY | 指定排序字段和排序规则 |
ROW FORMAT | 指定表文件字段分隔符 |
STORED AS | 指定表文件的存储格式。 常用格式:SEQUENCEFILE, TEXTFILE,RCFILE,如果文件数据是纯文本,可以使用SRORED AS TEXTFILE。 如果数据需要压缩,使用storted as SEQUENCEFILE。 |
LOCATION | 指定表文件的存储路径 |
2.1.1 内部表
创建表时,如果没有使用external关键字,则该表是内部表。
复制表结构和表内容创建表
CREATE TABLE tb1 AS SELECT * FROM tb2;
复制表结构,不复制内容
CREATE TABLE tb1 LIKE tb2;
查询表详细信息
DESC FORMATTED tb1;
删除表
DROP TABLE tb2;
2.1.2 外部表
创建表时,如果使用external关键字,则该表是外部表。外部表因为是指定其他的hdfs路径的数据加载到表当中来,所以hive表会认为自己不完全独占这份数据,所以删除hive表的时候,数据仍然存放在hdfs当中,不会删掉。
2.2 分区表
在大数据中,最常用的一种思想就是分治,我们可以把大的文件切割划分成一个个的小文件,这样每次操作一个小的文件就很容易了,同样的道理,在hive中也是支持这种思想的,就是我么你可以把大的数据,按照每月,或者天进行切分成一个个的小的文件,这样去操作小的文件就很容易了。
**语法:**在创建表的时候添加以下字段
[PARTITIONED BY (col_name data_type [COMMENT '字段描述信息'], ...)]
查看分区
SHOW PARTITIONS tb_name;
添加表分区
ALTER TABLE tb_name ADD [IF NOT EXISTS] PARTITION(col_name data_type,...);
修改表分区
-- 通过移动位置来修改分区的路径;旧路径和旧数据仍保留
ALTER TABLE tb_name PARTITION(col_name data_type,...) SET LOCATION '<new path>';
删除表分区
ALTER TABLE tb_name DROP [IF NOT EXISTS] PARTITION(col_name data_type,...);
2.3 分桶表
分桶,就是将数据按照指定的字段进行划分到多个文件当中去,分桶就是MapReduce中的分区。
开启分桶功能
SET hive.enforce.bucketing=true;
设置reduce的个数
SET mapreduce.job.reduces=1;
**创建分桶表:**在创建表的时候添加以下字段
[CLUSTERED BY (col_name, col_name, ...)]
桶表的数据加载,由于桶表的数据加载通过hdfs dfs -put文件或通过load data均不好使,只能通过insert overwrite
的方式将普通表的数据通过查询的方式加载到桶表当中去。
2.4 修改表
重命名
ALTER TABLE tb_name RENAME TO new_tb_name;
修改列信息
ALTER TABLE tb_name CHANGE COLUMN old_col_name new_col_name new_data_type [AFTER|FIRST other_column];
增加列
ALTER TABLE tb_name ADD COLUMNS(col_name data_type,...)
删除或替换列
-- 只能用于使用了两种内置SerDe模块的表:DynamicSerDe或者MetadataTypedColumnsetSerDe
ALTER TABLE tb_name REPLACE COLUMNS(col_name data_type,...)
修改表属性
-- 可以增加附加的表属性或修改已存在的属性,但是无法删除属性
ALTER TABLE tb_name SET TBLPROPERTIES('notes'='wjqixige is my blog');
2.5 删除表
DROP TABLE [IF EXISTS] tb_name;
二、数据操作
1 装载数据
-- 1. 如果分区目录不存在,则会先创建分区目录,再将数据拷贝到该目录下;
-- 2. 如果是非分区表,则省略PARTITION子句;
-- 3. 使用LOCAL,路径为本地文件系统路径; 省略LOCAL,路径为分布式文件系统中路径
-- 4. 使用OVERWRITE,目标文件夹中存在的数据会被删除; 省略OVERWRITE,目标文件中存在的数据不会被删除,如果出现同名文件,那么旧的同名文件会被覆盖重写;
-- 5. INPATH子句中使用的文件路径下不可以包含任何文件夹
LOAD DATA LOCAL INPATH '<directory>' OVERWRITE INTO TABLE tb_name [PARTITION(col_name data_type,...)];
2 插入数据
-- 在版本V0.8.0之后,如果没有使用OVERWRITE或者使用INRO替换掉它,则hive会以追加的方式写入数据
INSERT OVERWRITE TABLE tb_name [PARTITION(col_name1 = val1, ...)] SELECT * FROM tb_name2 tb2 [WHERE tb2.col = val1 AND ...];
3 加载数据
-- 不能用于外部表
CREATE TABLE tb_name AS SELECT * FROM tb_name1;
4 导出数据
INSERT OVERWRITE LOCAL DIRECTORY '<directory>' SELECT * FROM tb_name;
三、查询
1 SELECT…FROM语句
SELECT [ALL|DISTINCT] select_expr,select_expr,... FORM table_reference
[WHERE where_condition]
[GROUP BY col_list [HVAING condition]]
[ClUSTER BY col_list | [DISTRIBUTE BY col_list] [SORT BY | ORDER BY col_list]]
[LIMIT number]
采用正则表达式指定列
SELECT col_name1,`regex` FROM tb_name;
limit语句
用户限制返回的行数
SELECT * FROM tb_name LIMIT n;
列别名
紧跟列名,也可以在列名和别名之间加入关键字as
SELECT col_name AS alas_name, ... FROM tb_name;
2 WHERE语句
使用where子句,将不满足条件的行过滤掉;where子句紧随from子句
LIKE和RLIKE
-
LIKE运算选择类似的值,选择条件可以包含字符或数字
%
代表零个或多个字符(任意个字符)
_
代表一个字符 -
RLIKE子句是Hive中这个功能的一个扩展,其可以通过Java的正则表达式这个更强大的语言来指定匹配条件
3 GROUP BY语句
GROUP BY语句通常会和聚合函数一起使用,按照一个或者多个队列结果进行分组,然后对每个组执行聚合操作。
having与where不同点
- where针对表中的列发挥作用,查询数据;having针对查询结果中的列发挥作用,筛选数据;
- where后面不能写分组函数,而having后面可以使用分组函数;
- having只用于group by分组统计语句;
4 JOIN语句
等值JOIN
Hive支持通常的SQL JOIN语句,但是只支持等值连接,不支持非等值连接。例如:
SELECT s.s_id,s.s_score,stu.s_name,stu.s_birth FROM score s LEFT JOIN student stu ON s.s_id=stu.s_id;
内连接(INNER JOIN)
只有进行连接的两个表中都存在与连接条件相匹配的数据才会被保留下来。例如:
SELECT * FROM teacher t INNER JOIN course c ON t.t_id=c.t_id;
左外连接(LEFT OUTER JOIN)
JOIN操作符左边表中符合WHERE子句的所有记录将会被返回。例如:
SELECT * FROM teacher t LEFT JOIN course c ON t.t_id = c.t_id;
右外连接(RIGHT OUTER JOIN)
JOIN操作符右边表中复合WHERE子句的所有记录将会被返回。例如:
SELECT * FROM teacher t RIGHT JOIN course c ON t.t_id = c.t_id;
多表连接
注意:连接n个表,至少需要n-1个连接条件。
多表连接查询,查询老师对应的课程,以及对应的分数,对应的学生
SELECT * FROM teacher t LEFT JOIN course c ON t.t_id = c.t_id LEFT JOIN score s ON s.c_id=c.c_id LEFT JOIN student stu ON s.s_id = stu.s_id;
5 ORDER BY 和 SORT BY
ORDER BY:全局排序(前提:一个reduce);ASC
(升序,默认),DESC
(降序);ORDER BY子句在SELECT语句的结尾。
SELECT col1,col2,... FROM tb_name GROUP BY col1 ORDER BY col1,col2,... [DESC];
SORT BY:局部排序,每个MapReduce内部进行排序,对全局结果集来说不是排序。
-
设置reduce个数
set mapreduce.job.reduces=1;
-
查看设置reduce个数
set mapreduce.job.reduces;
-
查询成绩按照成绩降序排列
SELECT * FROM tb_name SORT BY coln;
6 DISTRIBUTE BY
Distribute By:类似MR中partition,进行分区,结合sort by使用。
注意,Hive要求DISTRIBUTE BY语句要写在SORT BY语句之前。
对于distribute by进行测试,一定要分配多reduce进行处理,否则无法看到distribute by的效果。
7 CLUSTER BY
当distribute by和sort by字段相同时,可以使用cluster by方式。cluster by除了具有distribute by的功能外还兼具sort by的功能。但是排序只能是倒序排序,不能指定排序规则为ASC或者DESC。以下两种写法等价
SELECT * FROM tb_name CLUSTER BY coln;
SELECT * FROM tb_name DISTRIBUTE BY coln SORT BY coln;
8 UNION ALL
union all可以将2个或多个表进行合并。每一个union子查询都必须具有相同的列,而且对应的每个字段的字段类型必须是一致的。
三、调优
1 使用Explain
使用explain功能,可以帮助我们学习hive是如何将查询转化成MapReduce任务的。例如查看SELECT * FROM tb_name LIMIT n;
转化过程:
EXPLAIN SELECT * FROM tb_name LIMIT n;
理解hive是如何对每个查询进行解析和计划的复杂细节是有用的,这是分析复杂或执行效率低的查询的一个不错的方式。另外,使用EXPLAIN EXTENDED
可以产生更多的输出信息。
2 Fetch抓取
Hive中对某些情况的查询可以不必使用MapReduce计算。例如:SELECT * FROM employees;在这种情况下,Hive可以简单地读取employee对应的存储目录下的文件,然后输出查询结果到控制台。通过设置hive.fetch.task.conversion
参数,可以控制查询语句是否走MapReduce。
3 限制调整
很多情况下LIMIT语句还是需要执行整个查询语句,然后再返回部分结果的。hive中有一个属性可以开启,当使用LIMIT时,其可以对源数据进行抽样。
<property>
<name>hive.limit.optimize.enable</name>
<value>true.</value>
</property>
另外还有两个参数可以控制这个操作,就是hive.limit.row.max.size
和hive.limit.optimize.limit.file
。这个功能有一个缺点就是,有可能输入中有用的数据永远不会被处理到。
4 Join优化
如果不指定MapJoin或者不符合MapJoin的条件,那么Hive解析器会在Reduce阶段完成join,容易发生数据倾斜。可以用MapJoin把小表全部加载到内存在map端进行join,避免reduce处理。以下是开启MapJoin参数设置:
-
设置自动选择Mapjoin
SET hive.auto.convert.join = true;
-
大表小表的阈值设置(默认25M以下认为是小表)
SET hive.mapjoin.smalltable.filesize=25123456;
5 本地模式
大多数的Hadoop Job是需要Hadoop提供的完整的可扩展性来处理大数据集的。不过,有时Hive的输入数据量是非常小的。在这种情况下,为查询触发执行任务时消耗可能会比实际job的执行时间要多的多。对于大多数这种情况,Hive可以通过本地模式在单台机器上处理所有的任务。对于小数据集,执行时间可以明显被缩短。用户可以通过设置hive.exec.mode.local.auto
的值为true
,来让Hive在适当的时候自动启动这个优化。
SET hive.exec.mode.local.auto=true ##开启本地模式
SET hive.exec.mode.local.auto=false ##关闭本地模式
6 并行执行
Hive会将一个查询转化成一个或者多个阶段。这样的阶段可以是MapReduce阶段、抽样阶段、合并阶段、limit阶段。或者Hive执行过程中可能需要的其他阶段。默认情况下,Hive一次只会执行一个阶段。不过,某个特定的job可能包含众多的阶段,而这些阶段可能并非完全互相依赖的,也就是说有些阶段是可以并行执行的,这样可能使得整个job的执行时间缩短。不过,如果有更多的阶段可以并行执行,那么job可能就越快完成。
通过设置参数hive.exec.parallel
值为true
,就可以开启并发执行。不过,在共享集群中,需要注意下,如果job中并行阶段增多,那么集群利用率就会增加。
SET hive.exec.parallel=true;
当然,得是在系统资源比较空闲的时候才有优势,否则,没资源,并行也起不来。
7 严格模式
Hive提供了一个严格模式,可以防止用户执行那些可能意向不到的不好的影响的查询。通过设置属性hive.mapred.mode
值为默认是非严格模式nonstrict 。开启严格模式需要修改hive.mapred.mode
值为strict,开启严格模式可以禁止3种类型的查询。
SET hive.mapred.mode=strict; ##开启严格模式
SET hive.mapred.mode=nostrict; ##开启非严格模式
-
对于分区表,除非where语句中含有分区字段过滤条件来限制范围,否则不允许执行。换句话说,就是用户不允许扫描所有分区。进行这个限制的原因是,通常分区表都拥有非常大的数据集,而且数据增加迅速。没有进行分区限制的查询可能会消耗令人不可接受的巨大资源来处理这个表。
-
对于使用了order by语句的查询,要求必须使用limit语句。因为order by为了执行排序过程会将所有的结果数据分发到同一个Reducer中进行处理,强制要求用户增加这个LIMIT语句可以防止Reducer额外执行很长一段时间。
-
限制笛卡尔积的查询。对关系型数据库非常了解的用户可能期望在执行查询的时候不使用ON语句而是使用WHERE语句,这样关系数据库的执行优化器就可以高效地将语句转化成那个语句。不幸的是,并不会执行这种优化,因此,如果表足够大,那么这个查询就会出现不可控的情况。
8 GROUP BY
默认情况下,Map阶段同一Key数据分发给一个reduce,当一个key数据过大时就倾斜了。并不是所有的聚合操作都需要在Reduce端完成,很多聚合操作都可以先在Map端进行部分聚合,最后在Reduce端得出最终结果。
开启Map端聚合参数设置
-
是否在Map端进行聚合,默认为True
SET hive.map.aggr = true;
-
在Map端进行聚合操作的条目数目
SET hive.groupby.mapaggr.checkinterval = 100000;
-
有数据倾斜的时候进行负载均衡(默认是false)
SET hive.groupby.skewindata = true;
当选项设定为 true,生成的查询计划会有两个MR Job。
第一个MR Job中,Map的输出结果会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;
第二个MR Job再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。
9 JVM重用
JVM重用是Hadoop调优参数的内容,其对Hive的性能具有非常大的影响,特别是对于很难避免小文件的场景或task特别多的场景,这类场景大多数执行时间都很短。
Hadoop的默认配置通常是使用派生JVM来执行map和Reduce任务的。这时JVM的启动过程可能会造成相当大的开销,尤其是执行的job包含有成百上千task任务的情况。JVM重用可以使得JVM实例在同一个job中重新使用N次。N的值可以在Hadoop的mapred-site.xml
文件(位于$HODOOP_HOME/conf目录下)中进行配置。通常在10-20之间,具体多少需要根据具体业务场景测试得出。也可以在hive当中通过
SET mapred.job.reuse.jvm.num.tasks=10;
来设置我们的jvm重用。这个功能的缺点是,开启JVM重用将一直占用使用到的task插槽,以便进行重用,直到任务完成后才能释放。如果某个“不平衡的”job中有某几个reduce task执行的时间要比其他Reduce task消耗的时间多的多的话,那么保留的插槽就会一直空闲着却无法被其他的job使用,直到所有的task都结束了才会释放。
10 索引
索引可以用来加快含有GROUP BY语句的查询的计算速度。
11 动态分区调整
往hive分区表中插入数据时,hive提供了一个动态分区功能,其可以基于查询参数的位置去推断分区的名称,从而建立分区。使用hive动态分区,需要进行相应的配置。hive的动态分区是以第一个表的分区规则,来对应第二个表的分区规则,将第一个表的所有分区,全部拷贝到第二个表中来,第二个表在加载数据的时候,不需要指定分区了,直接用第一个表的分区即可。
开启动态分区参数设置
-
开启动态分区功能(默认true,开启)
SET hive.exec.dynamic.partition=true;
-
设置为非严格模式(动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区)
SET hive.exec.dynamic.partition.mode=nonstrict;
-
在所有执行MR的节点上,最大一共可以创建多少个动态分区
SET hive.exec.max.dynamic.partitions=1000;
-
在每个执行MR的节点上,最大可以创建多少个动态分区。该参数需要根据实际的数据来设定。比如:源数据中包含了一年的数据,即day字段有365个值,那么该参数就需要设置成大于365,如果使用默认值100,则会报错。
SET hive.exec.max.dynamic.partitions.pernode=100
-
整个MR Job中,最大可以创建多少个HDFS文件。
在linux系统当中,每个linux用户最多可以开启1024个进程,每一个进程最多可以打开2048个文件,即持有2048个文件句柄,下面这个值越大,就可以打开文件句柄越大
SET hive.exec.max.created.files=100000;
-
当有空分区生成时,是否抛出异常。一般不需要设置。
SET hive.error.on.empty.partition=false;
12 推测执行
在分布式集群环境下,因为程序Bug(包括Hadoop本身的bug),负载不均衡或者资源分布不均等原因,会造成同一个作业的多个任务之间运行速度不一致,有些任务的运行速度可能明显慢于其他任务(比如一个作业的某个任务进度只有50%,而其他所有任务已经运行完毕),则这些任务会拖慢作业的整体执行进度。为了避免这种情况发生,Hadoop采用了推测执行(Speculative Execution)机制,它根据一定的法则推测出“拖后腿”的任务,并为这样的任务启动一个备份任务,让该任务与原始任务同时处理同一份数据,并最终选用最先成功运行完成任务的计算结果作为最终结果。
设置开启推测执行参数:
SET mapred.map.tasks.speculative.execution=true
SET mapred.reduce.tasks.speculative.execution=true
SET hive.mapred.reduce.tasks.speculative.execution=true
关于调优推测执行变量,很难给出具体的建议。如果对运行时的偏差非常敏感的话,可以将这些功能关掉。
13 Count(distinct)
数据量小的时候无所谓,数据量大的情况下,由于COUNT DISTINCT操作需要用一个Reduce Task来完成,这一个Reduce需要处理的数据量太大,就会导致整个Job很难完成,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替换:
SELECT COUNT(DISTINCT s_id) FROM score;
SELECT COUNT(s_id) FROM (SELECT id FROM score GROUP BY s_id) a;
虽然会多用一个job来完成,但是在数据量大的情况下,这个绝对是值得的。
14 笛卡尔积
尽量避免笛卡尔积,即避免join的时候不加on条件,或者无效的on条件,Hive只能使用1个reducer来完成笛卡尔积。
四、数据压缩
在实际工作当中,hive当中处理的数据,一般都需要经过压缩来节省我们的MR处理的网络带宽。
MR支持的压缩编码
压缩格式 | 工具 | 算法 | 文件扩展名 | 是否可切分 |
---|---|---|---|---|
DEFAULT | 无 | DEFAULT | .deflate | 否 |
Gzip | gzip | DEFAULT | .gz | 否 |
bzip2 | bzip2 | bzip2 | .bz2 | 是 |
LZO | lzop | LZO | .lzo | 否 |
LZ4 | 无 | LZ4 | .lz4 | 否 |
Snappy | 无 | Snappy | .snappy | 否 |
为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器,如下表所示
压缩格式 | 对应的编码/解码器 |
---|---|
DEFLATE | org.apache.hadoop.io.compress.DefaultCodec |
gzip | org.apache.hadoop.io.compress.GzipCodec |
bzip2 | org.apache.hadoop.io.compress.BZip2Codec |
LZO | com.hadoop.compression.lzo.LzopCodec |
LZ4 | org.apache.hadoop.io.compress.Lz4Codec |
Snappy | org.apache.hadoop.io.compress.SnappyCodec |
压缩性能的比较
压缩算法 | 原始文件大小 | 压缩文件大小 | 压缩速度 | 解压速度 |
---|---|---|---|---|
gzip | 8.3GB | 1.8GB | 17.5MB/s | 58MB/s |
bzip2 | 8.3GB | 1.1GB | 2.4MB/s | 9.5MB/s |
LZO | 8.3GB | 2.9GB | 49.3MB/s | 74.6MB/s |
http://google.github.io/snappy/
On a single core of a Core i7 processor in 64-bit mode, Snappy compresses at about
250 MB/sec
or more and decompresses at about500 MB/se
c or more.
压缩配置参数
要在Hadoop中启用压缩,可以配置如下参数(mapred-site.xml文件中):
参数 | 默认值 | 阶段 | 建议 |
---|---|---|---|
io.compression.codecs (在core-site.xml中配置) | DefaultCodec, GzipCodec, BZip2Codec, Lz4Codec, 在org.apache.hadoop.io.compress下 | 输入压缩 | Hadoop使用文件扩展名判断是否支持某种编解码器 |
mapreduce.map.output.compress | false | mapper输出 | 这个参数设为true启用压缩 |
mapreduce.map.output.compress.codec | DefaultCodec (在org.apache.hadoop.io.compress下) | mapper输出 | 使用LZO、LZ4或snappy编解码器在此阶段压缩数据 |
mapreduce.output.fileoutputformat.compress | false | reducer输出 | 这个参数设为true启用压缩 |
mapreduce.output.fileoutputformat.compress.codec | org.apache.hadoop.io.compress. DefaultCodec | reducer输出 | 使用标准工具或者编解码器,如gzip和bzip2 |
mapreduce.output.fileoutputformat.compress.type | RECORD | reducer输出 | SequenceFile输出使用的压缩类型:NONE和BLOCK |
1 开启Map输出阶段压缩
开启map输出阶段压缩可以减少job中map和Reduce task间数据传输量。具体配置如下:
-
开启hive中间传输数据压缩功能
SET hive.exec.compress.intermediate=true;
-
开启mapreduce中map输出压缩功能
SET mapreduce.map.output.compress=true;
-
设置mapreduce中map输出数据的压缩方式
SET mapreduce.map.output.compress.codec= org.apache.hadoop.io.compress.SnappyCodec;
2 开启Reduce输出阶段压缩
当Hive将输出写入到表中时,输出内容同样可以进行压缩。属性hive.exec.compress.output
控制着这个功能。用户可能需要保持默认设置文件中的默认值false
,这样默认的输出就是非压缩的纯文本文件了。用户可以通过在查询语句或执行脚本中设置这个值为true,来开启输出结果压缩功能。具体配置如下:
-
开启hive最终输出数据压缩功能
SET hive.exec.compress.output=true;
-
开启mapreduce最终输出数据压缩
SET mapreduce.output.fileoutputformat.compress=true;
-
设置mapreduce最终数据输出压缩方式
SET mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;
-
设置mapreduce最终数据输出压缩为块压缩
SET mapreduce.output.fileoutputformat.compress.type=BLOCK;
-
测试一下输出结果是否是压缩文件
INSERT overwrite LOCAL DIRECTORY '<directory>' SELECT * FROM score DISTRIBUTE BY s_id SORT BY s_id DESC;
五、数据存储格式
Hive支持的存储数的格式主要有:TEXTFILE(行式存储) 、SEQUENCEFILE(行式存储)、ORC(列式存储)、PARQUET(列式存储)。
1 列式存储和行式存储
上图左边为逻辑表,右边第一个为行式存储,第二个为列式存储。
行存储的特点: 查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。
列存储的特点: 因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。
2 常用数据存储格式
TEXTFILE格式
默认格式,数据不做压缩,磁盘开销大,数据解析开销大。可结合Gzip、Bzip2使用(系统自动检查,执行查询时自动解压),但使用这种方式,hive不会对数据进行切分,从而无法对数据进行并行操作。
ORC格式
Orc (Optimized Row Columnar)是hive 0.11版里引入的新的存储格式。可以看到每个Orc文件由1个或多个stripe组成,每个stripe250MB大小,每个Stripe里有三部分组成,分别是:
- Index Data:某些列的索引数据
- Row Data:真正的数据存储
- Stripe Footer:stripe的元数据信息
PARQUET格式
Parquet是面向分析型业务的列式存储格式,由Twitter和Cloudera合作开发,Parquet文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此Parquet格式文件是自解析的。通常情况下,在存储Parquet数据的时候会按照Block大小设置行组的大小,由于一般情况下每一个Mapper任务处理数据的最小单位是一个Block,这样可以把每一个行组由一个Mapper任务处理,增大任务执行并行度。Parquet文件的格式如下图所示。
六、函数
1 内置函数
内容较多,需要用到的时候再查也不迟;查询方式:
-
《Hive官方文档》:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF
-
《Hive编程指南》第六章 6.1.4 使用函数
-
在CLI中使用命令查询
-
查看系统自带的函数
SHOW functions;
-
显示自带函数的用法
DESC FUNCTION func_name; -- 详细显示用法 DESC FUNCTION EXTENDED func_name;
-
2 自定义函数
Hive 自带了一些函数,比如:max/min等,当hive提供的内置函数无法满足你的业务处理需求时,此时就可以考虑使用用户自定义函数(UDF:user-defined function)。根据用户自定义函数类别分为以下三种:
- UDF(User-Defined-Function):一进一出
- UDAF(User-Defined Aggregation Function):聚集函数,多进一出,类似于:count/max/min
- UDTF(User-Defined Table-Generating Functions):一进多出,如lateral view explore()
官方文档地址:https://cwiki.apache.org/confluence/display/Hive/HivePlugins
编程步骤:
- 继承org.apache.hadoop.hive.ql.UDF
- 需要实现evaluate函数;evaluate函数支持重载;
注意事项
- UDF必须要有返回类型,可以返回null,但是返回类型不能为void;
- UDF中常用Text/LongWritable等类型,不推荐使用java类型;
简单UDF示例
-
需要的依赖包;
org.apache.hive:hive-exec:2.7.5 org.apache.hadoop:hadoop-common:2.7.5
-
开发java类继承UDF,并重载evaluate 方法;
public class MyUDF extends UDF{ public Text evaluate(final Text str){ String tmp_str = str.toString(); if(str != null && !tmp_str.equals("")){ String str_ret = tmp_str.substring(0, 1).toUpperCase() + tmp_str.substring(1); return new Text(str_ret); } return new Text(""); } }
-
将项目打包,并上传到hive的lib目录下;
-
Hive的客户端添加jar包
add jar /hive-2.1.1/lib/udf.jar
-
设置函数与自定义的函数关联;
-- 不加TEMPORARY关键字,则为永久函数 CREATE TEMPORARY FUNCTION my_upper AS 'cn.wjqixige.udf.MyUDF';
-
使用自定义函数
SELECT my_upper('abc');